diff --git a/.gitattributes b/.gitattributes
index 7800fad58..2fdc4ff88 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,3 +12,4 @@
/phpstan.neon export-ignore
/.coveralls.yml export-ignore
/logs export-ignore
+/mlc_config.json export-ignore
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index c10d85094..a79b02b1f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -27,8 +27,6 @@ Here is the [official documentation](https://www.selenium.dev/documentation/en/)
To make sure your code comply with [PSR-2](http://www.php-fig.org/psr/psr-2/) coding style, tests passes and to execute other automated checks, run locally:
```sh
-composer preinstall # Run this only on the first run - this will install some additional dependencies
-
composer all
```
@@ -63,7 +61,7 @@ then start the local PHP server which will serve the test pages and then run the
```sh
export BROWSER_NAME="htmlunit" # see below for other browsers
-java -jar selenium-server-standalone-X.X.X.jar -log selenium.log &
+java -jar selenium-server-X.XX.0.jar standalone --log selenium.log &
php -S localhost:8000 -t tests/functional/web/ &
# Use following to run both unit and functional tests
composer all
@@ -71,7 +69,7 @@ composer all
composer test -- --testsuite functional
```
-If you want to run tests in different browser then "htmlunit" (Chrome or Firefox), you need to setup the browser driver (Chromedriver/Geckodriver), as it is [explained in wiki](https://github.com/php-webdriver/php-webdriver/wiki/Chrome)
+If you want to run tests in different browser then "htmlunit" (Chrome or Firefox), you need to set up the browser driver (Chromedriver/Geckodriver), as it is [explained in wiki](https://github.com/php-webdriver/php-webdriver/wiki/Chrome)
and then the `BROWSER_NAME` environment variable:
```sh
@@ -87,3 +85,10 @@ export GECKODRIVER=1
export BROWSER_NAME="firefox"
composer all
```
+
+To see the tests as they are happening (in the browser window), you can disable headless mode. This is useful eg. when debugging the tests or writing a new one:
+
+```sh
+export DISABLE_HEADLESS="1"
+composer all
+```
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index dcfc43785..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,65 +0,0 @@
----
-name: 🐛 Bug report
-about: Create a bug report to help us improve php-webdriver
-title: ''
-labels: bug
-assignees: ''
----
-
-
-
-### Bug description
-
-
-### How could the issue be reproduced
-
-Steps to reproduce the behavior:
-1.
-2.
-3. ...
-
-
-
-```php
-// You can insert your PHP code here (or remove this block if it is not relevant for the issue).
-
-// For example you can provide how you create WebDrivere instance:
-$capabilities = DesiredCapabilities::chrome();
-$driver = RemoteWebDriver::create('http://localhost:4444/', $capabilities);
-
-// And the code you use to execute the php-webdriver commands, for example:
-$driver->get('http://site.localhost/foo.html');
-$button = $driver->findElement(WebDriverBy::cssSelector('#foo'));
-$button->click();
-```
-
-```html
-
-
- Foo
-
-```
-
-### Expected behavior
-
-
-### Details
-
-
-* Php-webdriver version:
-* PHP version:
-* How do you start the browser driver or Selenium server:
-
-
-* Selenium server version:
-* Browser driver (chromedriver/geckodriver...) version:
-* Browser used + version:
-* Operating system:
-
-### Additional context
-
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 000000000..bac315d1d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,124 @@
+name: 🐛 Bug report
+description: Create a bug report to help us improve php-webdriver
+labels: [ "bug" ]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ If you have a question, [ask in Discussions](https://github.com/php-webdriver/php-webdriver/discussions) instead of filling a bug.
+
+ If you are reporting a bug, please **fill as much as possible information**, otherwise the community and maintainers cannot provide a prompt feedback and help solving the issue.
+ - type: textarea
+ id: bug-description
+ attributes:
+ label: Bug description
+ description: |
+ A clear description of what the bug is.
+ validations:
+ required: true
+
+ - type: textarea
+ id: steps-to-reproduce
+ attributes:
+ label: How could the issue be reproduced
+ description: |
+ Provide steps to reproduce the behavior. Please include everything relevant - the PHP code you use to initialize driver instance, the PHP code causing the error, HTML snippet or URL of the page where you encounter the issue etc.
+ This will be automatically formatted into code, so no need for backticks ```.
+ placeholder: |
+ // For example you can provide how you create WebDriver instance:
+ $capabilities = DesiredCapabilities::chrome();
+ $driver = RemoteWebDriver::create('http://localhost:4444/', $capabilities);
+ // And the code you use to execute the php-webdriver commands, for example:
+ $driver->get('http://site.localhost/foo.html');
+ $button = $driver->findElement(WebDriverBy::cssSelector('#foo'));
+ $button->click();
+
+
+ Foo
+
+
+ render: shell
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected-behavior
+ attributes:
+ label: Expected behavior
+ description: |
+ A clear and concise description of what you expected to happen.
+ validations:
+ required: false
+
+ - type: input
+ id: php-webdriver-version
+ attributes:
+ label: Php-webdriver version
+ description: You can run `composer show php-webdriver/webdriver` to find the version number
+ placeholder: |
+ For example: 1.13.0
+ validations:
+ required: true
+
+ - type: input
+ id: php-version
+ attributes:
+ label: PHP version
+ description: You can run `php -v` to find the version
+ placeholder: |
+ For example: 8.1.11
+ validations:
+ required: true
+
+ - type: input
+ id: how-start
+ attributes:
+ label: How do you start the browser driver or Selenium server
+ description: |
+ For example: Selenium server jar, Selenium in Docker, chromedriver command, Laravel Dusk, SauceLabs etc.
+ If relevant, provide the complete command you use to start the browser driver or Selenium server
+ validations:
+ required: true
+
+ - type: input
+ id: selenium-version
+ attributes:
+ label: Selenium server / Selenium Docker image version
+ description: Relevant only if you use Selenium server / Selenium in Docker
+ validations:
+ required: false
+
+ - type: input
+ id: browser-driver
+ attributes:
+ label: Browser driver (chromedriver/geckodriver...) version
+ description: You can run `chromedriver --version` or `geckodriver --version` to find the version
+ placeholder: |
+ For example: geckodriver 0.31.0
+ validations:
+ required: false
+
+ - type: input
+ id: browser
+ attributes:
+ label: Browser name and version
+ placeholder: |
+ For example: Firefox 105.0.2
+ validations:
+ required: false
+
+ - type: input
+ id: operating-system
+ attributes:
+ label: Operating system
+ validations:
+ required: false
+
+ - type: textarea
+ id: additional-context
+ attributes:
+ label: Additional context
+ description: |
+ Add any other context or you notes about the problem here.
+ validations:
+ required: false
diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
new file mode 100644
index 000000000..50112e87a
--- /dev/null
+++ b/.github/dependabot.yaml
@@ -0,0 +1,7 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: daily
+ time: "04:00"
diff --git a/.github/workflows/coveralls-workaround.yaml b/.github/workflows/coveralls-workaround.yaml
index fc35817a2..d6044861d 100644
--- a/.github/workflows/coveralls-workaround.yaml
+++ b/.github/workflows/coveralls-workaround.yaml
@@ -15,8 +15,34 @@ jobs:
coveralls:
name: Coveralls coverage workaround
runs-on: ubuntu-latest
+ if: >
+ github.event.workflow_run.event == 'pull_request' &&
+ github.event.workflow_run.conclusion == 'success'
steps:
+ # see https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
+ - name: 'Download artifact'
+ uses: actions/github-script@v8
+ with:
+ script: |
+ var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: ${{github.event.workflow_run.id }},
+ });
+ var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
+ return artifact.name == "data"
+ })[0];
+ var download = await github.rest.actions.downloadArtifact({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ artifact_id: matchArtifact.id,
+ archive_format: 'zip',
+ });
+ var fs = require('fs');
+ fs.writeFileSync('${{github.workspace}}/data.zip', Buffer.from(download.data));
+ - run: unzip data.zip
- name: Coveralls coverage workaround
# see https://github.com/lemurheavy/coveralls-public/issues/1653#issuecomment-1251587119
run: |
- curl --location --request GET 'https://coveralls.io/rerun_build?repo_token=${{%20secrets.COVERALLS_REPO_TOKEN%20}}&build_num=${{%20github.run_id%20}}'
+ BUILD_NUM=$(cat run_id)
+ curl --location --request GET "https://coveralls.io/rerun_build?repo_token=${{%20secrets.COVERALLS_REPO_TOKEN%20}}&build_num=$BUILD_NUM"
diff --git a/.github/workflows/docs-lint.yml b/.github/workflows/docs-lint.yml
index e0f0bb531..42baa0000 100644
--- a/.github/workflows/docs-lint.yml
+++ b/.github/workflows/docs-lint.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v6
with:
fetch-depth: 0
diff --git a/.github/workflows/docs-publish.yml b/.github/workflows/docs-publish.yml
old mode 100755
new mode 100644
index edf50d967..d0da12f8b
--- a/.github/workflows/docs-publish.yml
+++ b/.github/workflows/docs-publish.yml
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v6
with:
fetch-depth: 0
ssh-key: ${{ secrets.SSH_KEY_DEPLOY }}
diff --git a/.github/workflows/sauce-labs.yaml b/.github/workflows/sauce-labs.yaml
index c966cbacc..812fbfe25 100644
--- a/.github/workflows/sauce-labs.yaml
+++ b/.github/workflows/sauce-labs.yaml
@@ -20,21 +20,20 @@ jobs:
strategy:
fail-fast: false
matrix:
- w3c: [true]
include:
# Chrome 74 is the last version which doesn't use W3C WebDriver by default and rather use OSS protocol
- - { name: "Chrome 74, OSS protocol", BROWSER_NAME: "chrome", VERSION: "74.0", PLATFORM: "Windows 10", w3c: false, tunnel-id: "gh-1" }
- - { name: "Chrome latest, W3C protocol", BROWSER_NAME: "chrome", VERSION: "latest", PLATFORM: "Windows 10", tunnel-id: "gh-2" }
- - { name: "Edge latest, W3C protocol", BROWSER_NAME: "MicrosoftEdge", VERSION: "latest", PLATFORM: "Windows 10", tunnel-id: "gh-3" }
+ - { name: "Chrome 74, OSS protocol", BROWSER_NAME: "chrome", VERSION: "74.0", PLATFORM: "Windows 11", w3c: false, tunnel-name: "gh-1-chrome-oss-legacy" }
+ - { name: "Chrome latest, W3C protocol", BROWSER_NAME: "chrome", VERSION: "latest", PLATFORM: "Windows 11", w3c: true, tunnel-name: "gh-2-chrome-w3c" }
+ - { name: "Edge latest, W3C protocol", BROWSER_NAME: "MicrosoftEdge", VERSION: "latest", PLATFORM: "Windows 11", w3c: true, tunnel-name: "gh-3-MicrosoftEdge" }
- name: ${{ matrix.name }} (${{ matrix.tunnel-id }})
+ name: ${{ matrix.name }} (${{ matrix.tunnel-name }})
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: '7.4'
+ php-version: '8.3'
extensions: mbstring, intl, zip
coverage: none
@@ -46,11 +45,13 @@ jobs:
php -S 127.0.0.1:8000 -t tests/functional/web/ &>>./logs/php-server.log &
- name: Start Sauce Connect
- uses: saucelabs/sauce-connect-action@v1
+ uses: saucelabs/sauce-connect-action@v3
with:
username: ${{ secrets.SAUCE_USERNAME }}
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
- tunnelIdentifier: ${{ matrix.tunnel-id }}
+ tunnelName: ${{ matrix.tunnel-name }}
+ proxyLocalhost: allow
+ region: 'us-west-1'
- name: Run tests
env:
@@ -58,7 +59,7 @@ jobs:
VERSION: ${{ matrix.VERSION }}
PLATFORM: ${{ matrix.PLATFORM }}
DISABLE_W3C_PROTOCOL: "${{ matrix.w3c && '0' || '1' }}"
- SAUCE_TUNNEL_IDENTIFIER: ${{ matrix.tunnel-id }}
+ SAUCE_TUNNEL_NAME: ${{ matrix.tunnel-name }}
run: |
if [ -n "$SAUCELABS" ]; then EXCLUDE_GROUP+="exclude-saucelabs,"; fi
if [ "$BROWSER_NAME" = "MicrosoftEdge" ]; then EXCLUDE_GROUP+="exclude-edge,"; fi
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 2452c8619..d215e92ce 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -14,16 +14,16 @@ jobs:
name: "Code style and static analysis"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: '7.4'
+ php-version: '8.3'
extensions: mbstring, intl, zip
- name: Install PHP dependencies
- run: composer preinstall
+ run: composer update --no-interaction
- name: Lint
run: composer lint
@@ -35,8 +35,8 @@ jobs:
name: "Markdown link check"
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: gaurav-nelson/github-action-markdown-link-check@v1
+ - uses: actions/checkout@v6
+ - uses: tcort/github-action-markdown-link-check@v1
with:
use-verbose-mode: 'yes'
@@ -46,14 +46,13 @@ jobs:
strategy:
matrix:
- php-version: ['5.6', '7.1', '7.2', '7.3', '7.4', '8.0']
+ php-version: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
dependencies: ['']
include:
- - { php-version: '5.6', dependencies: '--prefer-lowest' }
- - { php-version: '8.1', dependencies: '--ignore-platform-req=php' }
+ - { php-version: '7.3', dependencies: '--prefer-lowest' }
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -66,10 +65,6 @@ jobs:
- name: Install PHP dependencies
run: composer update --no-interaction ${{ matrix.dependencies }}
- - name: Apply PHPUnit patches
- if: ${{ matrix.php-version < 7.2 }}
- run: scripts/apply-phpunit-patches.sh
-
- name: Run tests
run: vendor/bin/phpunit --testsuite unit --colors=always --coverage-clover ./logs/clover.xml
@@ -87,7 +82,7 @@ jobs:
functional-tests:
runs-on: ${{ matrix.os }}
env:
- SELENIUM_SERVER_DOWNLOAD_URL: https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.3.0/selenium-server-4.3.0.jar
+ SELENIUM_SERVER_DOWNLOAD_URL: https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.38.0/selenium-server-4.38.0.jar
strategy:
fail-fast: false
@@ -104,12 +99,12 @@ jobs:
name: "Functional tests (${{ matrix.browser }}, Selenium server: ${{ matrix.selenium-server }}, W3C: ${{ matrix.w3c }})"
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: 7.4
+ php-version: '8.3'
extensions: mbstring, intl, zip
coverage: xdebug
@@ -132,7 +127,7 @@ jobs:
run: |
google-chrome --version
xvfb-run --server-args="-screen 0, 1280x720x24" --auto-servernum \
- chromedriver --port=4444 --url-base=/wd/hub &> ./logs/chromedriver.log &
+ chromedriver --port=4444 &> ./logs/chromedriver.log &
- name: Start GeckoDriver
if: ${{ !matrix.selenium-server && matrix.browser == 'firefox' }}
@@ -153,6 +148,7 @@ jobs:
php -S 127.0.0.1:8000 -t tests/functional/web/ &> ./logs/php-server.log &
- name: Wait for browser & PHP to start
+ timeout-minutes: 1
run: |
while ! nc -z localhost 4444 ./data/run_id
+ - uses: actions/upload-artifact@v6
+ with:
+ name: data
+ path: data/
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 5cf7d36f6..f754fb8b7 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -12,24 +12,28 @@
'blank_line_before_statement' => ['statements' => ['return', 'try']],
'braces' => ['allow_single_line_anonymous_class_with_empty_body' => true, 'allow_single_line_closure' => true],
'cast_spaces' => true,
- 'class_attributes_separation' => ['elements' => ['method' => 'one']],
+ 'class_attributes_separation' => ['elements' => ['method' => 'one', 'trait_import' => 'none']],
'clean_namespace' => true,
+ 'combine_nested_dirname' => true,
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => true,
+ //'declare_strict_types' => true, // TODO: used only in tests, use in lib in next major version
'fopen_flag_order' => true,
'fopen_flags' => true,
'full_opening_tag' => true,
'function_typehint_space' => true,
+ 'heredoc_indentation' => ['indentation' => 'same_as_start'],
'implode_call' => true,
'is_null' => true,
'lambda_not_used_import' => true,
- 'linebreak_after_opening_tag' => true,
+ 'list_syntax' => true,
'lowercase_cast' => true,
'lowercase_static_reference' => true,
'magic_constant_casing' => true,
'magic_method_casing' => true,
'mb_str_functions' => true,
+ 'method_argument_space' => ['after_heredoc' => true],
'native_function_casing' => true,
'native_function_type_declaration_casing' => true,
'new_with_braces' => true,
@@ -54,29 +58,40 @@
'switch',
'throw',
'use',
- 'use_trait',
],
],
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_singleline_whitespace_before_semicolons' => true,
- 'no_trailing_comma_in_singleline_array' => true,
+ 'no_superfluous_phpdoc_tags' => [
+ 'allow_mixed' => true,
+ 'remove_inheritdoc' => true,
+ 'allow_unused_params' => true, // Used in RemoteWebDriver::createBySessionID to maintain BC
+ ],
+ 'no_trailing_comma_in_singleline' => true,
'no_unreachable_default_argument_value' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_useless_sprintf' => true,
+ 'no_whitespace_before_comma_in_array' => ['after_heredoc' => true],
'no_whitespace_in_blank_line' => true,
+ 'non_printable_character' => true,
+ 'nullable_type_declaration' => [
+ 'syntax' => 'question_mark',
+ ],
+ 'nullable_type_declaration_for_default_null_value' => true,
'object_operator_without_whitespace' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
- 'php_unit_dedicate_assert' => false,
- 'php_unit_expectation' => ['target' => '5.6'],
+ 'php_unit_dedicate_assert' => true,
+ 'php_unit_dedicate_assert_internal_type' => true,
+ 'php_unit_expectation' => ['target' => '8.4'],
'php_unit_method_casing' => ['case' => 'camel_case'],
'php_unit_mock_short_will_return' => true,
'php_unit_mock' => true,
- 'php_unit_namespaced' => ['target' => '5.7'],
+ 'php_unit_namespaced' => ['target' => '6.0'],
'php_unit_no_expectation_annotation' => true,
'php_unit_set_up_tear_down_visibility' => true,
'php_unit_test_case_static_method_calls' => ['call_type' => 'this'],
@@ -91,9 +106,13 @@
'phpdoc_scalar' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_trim' => true,
+ //'phpdoc_to_param_type' => true, // TODO: used only in tests, use in lib in next major version
+ //'phpdoc_to_return_type' => true, // TODO: used only in tests, use in lib in next major version
'phpdoc_types' => true,
'phpdoc_var_annotation_correct_order' => true,
+ 'pow_to_exponentiation' => true,
'psr_autoloading' => true,
+ 'random_api_migration' => true,
'self_accessor' => true,
'set_type_to_cast' => true,
'short_scalar_cast' => true,
@@ -107,10 +126,12 @@
'switch_continue_to_break' => true,
'ternary_operator_spaces' => true,
'ternary_to_elvis_operator' => true,
- 'trailing_comma_in_multiline' => ['elements' => ['arrays']],
+ 'ternary_to_null_coalescing' => true,
+ 'trailing_comma_in_multiline' => ['elements' => ['arrays'], 'after_heredoc' => true],
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
- 'visibility_required' => ['elements' => ['method', 'property']],
+ 'visibility_required' => ['elements' => ['method', 'property', 'const']],
+ //'void_return' => true, // TODO: used only in tests, use in lib in next major version
'whitespace_after_comma_in_array' => true,
'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false],
])
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32b4c737f..a468c26f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,50 @@
This project versioning adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
+
+## 1.15.2 - 2024-11-21
+### Fixed
+- PHP 8.4 deprecation notices, especially in nullable type-hints.
+- Docs: Fix static return types in RemoteWebElement phpDoc.
+- Tests: Disable chrome 127+ search engine pop-up in tests
+- Tests: Enable Shadow DOM tests in Geckodriver
+
+### Added
+- Tests: Allow running tests in headfull (not headless) mode using `DISABLE_HEADLESS` environment variable.
+
+### Changed
+- Docs: Update selenium server host URL in example.
+
+## 1.15.1 - 2023-10-20
+- Update `symfony/process` dependency to support upcoming Symfony 7.
+
+## 1.15.0 - 2023-08-29
+### Changed
+- Capability key `ChromeOptions::CAPABILITY_W3C` used to set ChromeOptions is now deprecated in favor of `ChromeOptions::CAPABILITY`, which now also contains the W3C compatible value (`goog:chromeOptions`).
+- ChromeOptions are now passed to the driver always as a W3C compatible key `goog:chromeOptions`, even in the deprecated OSS JsonWire payload (as ChromeDriver [supports](https://bugs.chromium.org/p/chromedriver/issues/detail?id=1786) this since 2017).
+- Improve Safari compatibility for `` element.
+- Remove no longer needed compatibility layer with old Symfony.
+- Docs: Document exception throwing in findElement.
+
+### Fixed
+- Handle unexpected response when getting element(s) by throwing an exception, not triggering fatal error.
+
+## 1.14.0 - 2023-02-09
+### Added
+- `PhpWebDriverExceptionInterface` as a common interface to identify all exceptions thrown in php-webdriver.
+
+### Changed
+- Require PHP ^7.3.
+- Capabilities must be either explicitly provided or retrievable from Selenium Grid when resuing session with `createBySessionID()`.
+- Throw `UnexpectedResponseException` instead of `UnknownErrorException` in `findElement()` and `findElements()` methods.
+- Throw custom php-webdriver exceptions instead of native PHP SPL exceptions.
+- Do not mix internal non-W3C WebDriver exceptions, separate them into own namespaces.
+
+## 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.
@@ -11,10 +55,10 @@ This project versioning adheres to [Semantic Versioning](http://semver.org/).
- 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.
-- Increase default request timeout to 3 minutes (instead of 30 seconds).
## 1.12.1 - 2022-05-03
### Fixed
diff --git a/README.md b/README.md
index 2f2432095..f3e8d3801 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# php-webdriver – Selenium WebDriver bindings for PHP
[](https://packagist.org/packages/php-webdriver/webdriver)
-[](https://github.com/php-webdriver/php-webdriver/actions)
-[](https://saucelabs.com/u/php-webdriver)
+[](https://github.com/php-webdriver/php-webdriver/actions)
+[](https://saucelabs.com/u/php-webdriver)
[](https://packagist.org/packages/php-webdriver/webdriver)
## Description
diff --git a/composer.json b/composer.json
index 13b925349..f44663100 100644
--- a/composer.json
+++ b/composer.json
@@ -12,29 +12,31 @@
],
"homepage": "https://github.com/php-webdriver/php-webdriver",
"require": {
- "php": "^5.6 || ~7.0 || ^8.0",
+ "php": "^7.3 || ^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"
+ "symfony/process": "^5.0 || ^6.0 || ^7.0 || ^8.0"
},
"require-dev": {
- "ondram/ci-detector": "^2.1 || ^3.5 || ^4.0",
+ "ergebnis/composer-normalize": "^2.20.0",
+ "ondram/ci-detector": "^4.0",
"php-coveralls/php-coveralls": "^2.4",
- "php-mock/php-mock-phpunit": "^1.1 || ^2.0",
+ "php-mock/php-mock-phpunit": "^2.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
- "phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9",
+ "phpunit/phpunit": "^9.3",
"squizlabs/php_codesniffer": "^3.5",
- "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0"
+ "symfony/var-dumper": "^5.0 || ^6.0 || ^7.0 || ^8.0"
},
"replace": {
"facebook/webdriver": "*"
},
"suggest": {
- "ext-SimpleXML": "For Firefox profile creation"
+ "ext-simplexml": "For Firefox profile creation"
},
"minimum-stability": "dev",
+ "prefer-stable": true,
"autoload": {
"psr-4": {
"Facebook\\WebDriver\\": "lib/"
@@ -62,10 +64,12 @@
},
"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"
+ "@composer install --working-dir=tools/php-cs-fixer --no-progress --no-interaction",
+ "@composer install --working-dir=tools/phpstan --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"
+ "@composer update --working-dir=tools/php-cs-fixer --no-progress --no-interaction",
+ "@composer update --working-dir=tools/phpstan --no-progress --no-interaction"
],
"all": [
"@lint",
@@ -73,27 +77,22 @@
"@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/"
+ "@php tools/phpstan/vendor/bin/phpstan analyze -c phpstan.neon --ansi",
+ "@php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --diff --dry-run -vvv --ansi",
+ "@php 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/"
+ "@php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --diff -vvv || exit 0",
+ "@php vendor/bin/phpcbf --standard=PSR2 --ignore=*.js ./lib/ ./tests/"
],
"lint": [
- "vendor/bin/parallel-lint -j 10 ./lib ./tests example.php",
+ "@php 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"
+ "@php vendor/bin/phpunit --colors=always"
]
}
}
diff --git a/example.php b/example.php
index 4491df386..92cd431d6 100644
--- a/example.php
+++ b/example.php
@@ -10,8 +10,8 @@
require_once('vendor/autoload.php');
-// This is where Selenium server 2/3 listens by default. For Selenium 4, Chromedriver or Geckodriver, use http://localhost:4444/
-$host = 'http://localhost:4444/wd/hub';
+// This is where Selenium, Chromedriver and Geckodriver 4 listens by default. For Selenium 2/3, use http://localhost:4444/wd/hub
+$host = 'http://localhost:4444/';
$capabilities = DesiredCapabilities::chrome();
diff --git a/lib/AbstractWebDriverCheckboxOrRadio.php b/lib/AbstractWebDriverCheckboxOrRadio.php
index 450bc387e..00aee242a 100644
--- a/lib/AbstractWebDriverCheckboxOrRadio.php
+++ b/lib/AbstractWebDriverCheckboxOrRadio.php
@@ -2,9 +2,9 @@
namespace Facebook\WebDriver;
+use Facebook\WebDriver\Exception\InvalidElementStateException;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnexpectedTagNameException;
-use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Support\XPathEscaper;
/**
@@ -30,7 +30,7 @@ public function __construct(WebDriverElement $element)
$this->name = $element->getAttribute('name');
if ($this->name === null) {
- throw new WebDriverException('The input does not have a "name" attribute.');
+ throw new InvalidElementStateException('The input does not have a "name" attribute.');
}
$this->element = $element;
diff --git a/lib/Chrome/ChromeDevToolsDriver.php b/lib/Chrome/ChromeDevToolsDriver.php
index ffbb91a18..2d95d274b 100644
--- a/lib/Chrome/ChromeDevToolsDriver.php
+++ b/lib/Chrome/ChromeDevToolsDriver.php
@@ -11,7 +11,7 @@
*/
class ChromeDevToolsDriver
{
- const SEND_COMMAND = [
+ public const SEND_COMMAND = [
'method' => 'POST',
'url' => '/session/:sessionId/goog/cdp/execute',
];
diff --git a/lib/Chrome/ChromeDriver.php b/lib/Chrome/ChromeDriver.php
index 1d840eaed..e947a498e 100644
--- a/lib/Chrome/ChromeDriver.php
+++ b/lib/Chrome/ChromeDriver.php
@@ -21,8 +21,10 @@ class ChromeDriver extends LocalWebDriver
* @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)
- {
+ 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();
}
@@ -40,7 +42,7 @@ public static function start(DesiredCapabilities $desired_capabilities = null, C
*/
public static function startUsingDriverService(
ChromeDriverService $service,
- DesiredCapabilities $capabilities = null
+ ?DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::chrome();
diff --git a/lib/Chrome/ChromeDriverService.php b/lib/Chrome/ChromeDriverService.php
index a6d3eb834..902a48ded 100644
--- a/lib/Chrome/ChromeDriverService.php
+++ b/lib/Chrome/ChromeDriverService.php
@@ -10,14 +10,14 @@ 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';
+ public 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';
+ public const CHROME_DRIVER_EXECUTABLE = 'WEBDRIVER_CHROME_DRIVER';
/**
* @var string Default executable used when no other is provided
* @internal
*/
- const DEFAULT_EXECUTABLE = 'chromedriver';
+ public const DEFAULT_EXECUTABLE = 'chromedriver';
/**
* @return static
diff --git a/lib/Chrome/ChromeOptions.php b/lib/Chrome/ChromeOptions.php
index 0e547b209..daea44c67 100644
--- a/lib/Chrome/ChromeOptions.php
+++ b/lib/Chrome/ChromeOptions.php
@@ -9,19 +9,18 @@
/**
* The class manages the capabilities in ChromeDriver.
*
- * @see https://sites.google.com/a/chromium.org/chromedriver/capabilities
+ * @see https://sites.google.com/chromium.org/driver/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
+ * The key of chromeOptions in desired capabilities
*/
- const CAPABILITY = 'chromeOptions';
+ public const CAPABILITY = 'goog:chromeOptions';
/**
- * The key of chromeOptions in desired capabilities (in W3C compatible protocol)
+ * @deprecated Use CAPABILITY instead
*/
- const CAPABILITY_W3C = 'goog:chromeOptions';
+ public const CAPABILITY_W3C = self::CAPABILITY;
/**
* @var array
*/
@@ -65,7 +64,6 @@ public function setBinary($path)
}
/**
- * @param array $arguments
* @return ChromeOptions
*/
public function addArguments(array $arguments)
@@ -79,7 +77,6 @@ public function addArguments(array $arguments)
* 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)
diff --git a/lib/Cookie.php b/lib/Cookie.php
index 77b04687c..2ae257bd1 100644
--- a/lib/Cookie.php
+++ b/lib/Cookie.php
@@ -2,14 +2,14 @@
namespace Facebook\WebDriver;
-use InvalidArgumentException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
/**
* Set values of an cookie.
*
* Implements ArrayAccess for backwards compatibility.
*
- * @see https://w3c.github.io/webdriver/webdriver-spec.html#cookies
+ * @see https://w3c.github.io/webdriver/#cookies
*/
class Cookie implements \ArrayAccess
{
@@ -36,10 +36,10 @@ public function __construct($name, $value)
public static function createFromArray(array $cookieArray)
{
if (!isset($cookieArray['name'])) {
- throw new InvalidArgumentException('Cookie name should be set');
+ throw LogicException::forError('Cookie name should be set');
}
if (!isset($cookieArray['value'])) {
- throw new InvalidArgumentException('Cookie value should be set');
+ throw LogicException::forError('Cookie value should be set');
}
$cookie = new self($cookieArray['name'], $cookieArray['value']);
@@ -107,7 +107,7 @@ public function getPath()
public function setDomain($domain)
{
if (mb_strpos($domain, ':') !== false) {
- throw new InvalidArgumentException(sprintf('Cookie domain "%s" should not contain a port', $domain));
+ throw LogicException::forError(sprintf('Cookie domain "%s" should not contain a port', $domain));
}
$this->offsetSet('domain', $domain);
@@ -258,11 +258,11 @@ public function offsetUnset($offset)
protected function validateCookieName($name)
{
if ($name === null || $name === '') {
- throw new InvalidArgumentException('Cookie name should be non-empty');
+ throw LogicException::forError('Cookie name should be non-empty');
}
if (mb_strpos($name, ';') !== false) {
- throw new InvalidArgumentException('Cookie name should not contain a ";"');
+ throw LogicException::forError('Cookie name should not contain a ";"');
}
}
@@ -272,7 +272,7 @@ protected function validateCookieName($name)
protected function validateCookieValue($value)
{
if ($value === null) {
- throw new InvalidArgumentException('Cookie value is required when setting a cookie');
+ throw LogicException::forError('Cookie value is required when setting a cookie');
}
}
}
diff --git a/lib/Exception/DriverServerDiedException.php b/lib/Exception/DriverServerDiedException.php
deleted file mode 100644
index c3eff3b9e..000000000
--- a/lib/Exception/DriverServerDiedException.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getMessage(), $this->getCode(), $previous);
- }
-}
diff --git a/lib/Exception/Internal/DriverServerDiedException.php b/lib/Exception/Internal/DriverServerDiedException.php
new file mode 100644
index 000000000..7c23df5ed
--- /dev/null
+++ b/lib/Exception/Internal/DriverServerDiedException.php
@@ -0,0 +1,16 @@
+getCommandLine(),
+ $process->getErrorOutput()
+ )
+ );
+ }
+}
diff --git a/lib/Exception/Internal/UnexpectedResponseException.php b/lib/Exception/Internal/UnexpectedResponseException.php
new file mode 100644
index 000000000..18cdc88cc
--- /dev/null
+++ b/lib/Exception/Internal/UnexpectedResponseException.php
@@ -0,0 +1,51 @@
+getMessage()
+ )
+ );
+ }
+}
diff --git a/lib/Exception/Internal/WebDriverCurlException.php b/lib/Exception/Internal/WebDriverCurlException.php
new file mode 100644
index 000000000..ac81f97e2
--- /dev/null
+++ b/lib/Exception/Internal/WebDriverCurlException.php
@@ -0,0 +1,22 @@
+setCapability(FirefoxOptions::CAPABILITY, $firefoxOptions);
*/
- const PROFILE = 'firefox_profile';
+ public const PROFILE = 'firefox_profile';
/**
* Creates a new FirefoxDriver using default configuration.
@@ -26,7 +26,7 @@ class FirefoxDriver extends LocalWebDriver
*
* @return static
*/
- public static function start(DesiredCapabilities $capabilities = null)
+ public static function start(?DesiredCapabilities $capabilities = null)
{
$service = FirefoxDriverService::createDefaultService();
@@ -43,7 +43,7 @@ public static function start(DesiredCapabilities $capabilities = null)
*/
public static function startUsingDriverService(
FirefoxDriverService $service,
- DesiredCapabilities $capabilities = null
+ ?DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::firefox();
diff --git a/lib/Firefox/FirefoxDriverService.php b/lib/Firefox/FirefoxDriverService.php
index 525c5b5bc..83c6a28fb 100644
--- a/lib/Firefox/FirefoxDriverService.php
+++ b/lib/Firefox/FirefoxDriverService.php
@@ -9,12 +9,12 @@ 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';
+ public const WEBDRIVER_FIREFOX_DRIVER = 'WEBDRIVER_FIREFOX_DRIVER';
/**
* @var string Default executable used when no other is provided
* @internal
*/
- const DEFAULT_EXECUTABLE = 'geckodriver';
+ public const DEFAULT_EXECUTABLE = 'geckodriver';
/**
* @return static
diff --git a/lib/Firefox/FirefoxOptions.php b/lib/Firefox/FirefoxOptions.php
index 0a3a16744..84cf94cef 100644
--- a/lib/Firefox/FirefoxOptions.php
+++ b/lib/Firefox/FirefoxOptions.php
@@ -2,6 +2,7 @@
namespace Facebook\WebDriver\Firefox;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use ReturnTypeWillChange;
/**
@@ -12,13 +13,13 @@
class FirefoxOptions implements \JsonSerializable
{
/** @var string The key of FirefoxOptions in desired capabilities */
- const CAPABILITY = 'moz:firefoxOptions';
+ public const CAPABILITY = 'moz:firefoxOptions';
/** @var string */
- const OPTION_ARGS = 'args';
+ public const OPTION_ARGS = 'args';
/** @var string */
- const OPTION_PREFS = 'prefs';
+ public const OPTION_PREFS = 'prefs';
/** @var string */
- const OPTION_PROFILE = 'profile';
+ public const OPTION_PROFILE = 'profile';
/** @var array */
private $options = [];
@@ -49,13 +50,13 @@ public function __construct()
public function setOption($name, $value)
{
if ($name === self::OPTION_PREFS) {
- throw new \InvalidArgumentException('Use setPreference() method to set Firefox preferences');
+ throw LogicException::forError('Use setPreference() method to set Firefox preferences');
}
if ($name === self::OPTION_ARGS) {
- throw new \InvalidArgumentException('Use addArguments() method to add Firefox arguments');
+ throw LogicException::forError('Use addArguments() method to add Firefox arguments');
}
if ($name === self::OPTION_PROFILE) {
- throw new \InvalidArgumentException('Use setProfile() method to set Firefox profile');
+ throw LogicException::forError('Use setProfile() method to set Firefox profile');
}
$this->options[$name] = $value;
@@ -96,7 +97,6 @@ public function setPreference($name, $value)
/**
* @see https://github.com/php-webdriver/php-webdriver/wiki/Firefox#firefox-profile
- * @param FirefoxProfile $profile
* @return self
*/
public function setProfile(FirefoxProfile $profile)
diff --git a/lib/Firefox/FirefoxPreferences.php b/lib/Firefox/FirefoxPreferences.php
index 159a9c806..2a33fb003 100644
--- a/lib/Firefox/FirefoxPreferences.php
+++ b/lib/Firefox/FirefoxPreferences.php
@@ -11,13 +11,13 @@
class FirefoxPreferences
{
/** @var string Port WebDriver uses to communicate with Firefox instance */
- const WEBDRIVER_FIREFOX_PORT = 'webdriver_firefox_port';
+ public 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';
+ public const READER_PARSE_ON_LOAD_ENABLED = 'reader.parse-on-load.enabled';
/** @var string Browser homepage */
- const BROWSER_STARTUP_HOMEPAGE = 'browser.startup.homepage';
+ public const BROWSER_STARTUP_HOMEPAGE = 'browser.startup.homepage';
/** @var string Should the Devtools JSON view be enabled? */
- const DEVTOOLS_JSONVIEW = 'devtools.jsonview.enabled';
+ public const DEVTOOLS_JSONVIEW = 'devtools.jsonview.enabled';
private function __construct()
{
diff --git a/lib/Firefox/FirefoxProfile.php b/lib/Firefox/FirefoxProfile.php
index 6e06db6be..95e8004a8 100644
--- a/lib/Firefox/FirefoxProfile.php
+++ b/lib/Firefox/FirefoxProfile.php
@@ -2,7 +2,9 @@
namespace Facebook\WebDriver\Firefox;
-use Facebook\WebDriver\Exception\WebDriverException;
+use Facebook\WebDriver\Exception\Internal\IOException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
+use Facebook\WebDriver\Exception\Internal\RuntimeException;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
@@ -71,7 +73,7 @@ public function setRdfFile($rdf_file)
/**
* @param string $key
* @param string|bool|int $value
- * @throws WebDriverException
+ * @throws LogicException
* @return FirefoxProfile
*/
public function setPreference($key, $value)
@@ -85,7 +87,7 @@ public function setPreference($key, $value)
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
} else {
- throw new WebDriverException(
+ throw LogicException::forError(
'The value of the preference should be either a string, int or bool.'
);
}
@@ -185,8 +187,7 @@ public function encode()
/**
* @param string $extension The path to the extension.
* @param string $profileDir The path to the profile directory.
- * @throws \Exception
- * @throws WebDriverException
+ * @throws IOException
*/
private function installExtension($extension, $profileDir)
{
@@ -195,18 +196,24 @@ private function installExtension($extension, $profileDir)
// 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');
+ throw IOException::forFileError(
+ 'Cannot install Firefox extension - cannot create directory',
+ $extensionDir
+ );
}
if (!copy($extension, $extensionDir . $extensionCommonName . '.xpi')) {
- throw new WebDriverException('Cannot install Firefox extension - cannot copy file');
+ throw IOException::forFileError(
+ 'Cannot install Firefox extension - cannot copy file',
+ $extension
+ );
}
}
/**
* @param string $prefix Prefix of the temp directory.
*
- * @throws WebDriverException
+ * @throws IOException
* @return string The path to the temp directory created.
*/
private function createTempDirectory($prefix = '')
@@ -216,7 +223,10 @@ private function createTempDirectory($prefix = '')
unlink($temp_dir);
mkdir($temp_dir);
if (!is_dir($temp_dir)) {
- throw new WebDriverException('Cannot create firefox profile.');
+ throw IOException::forFileError(
+ 'Cannot install Firefox extension - cannot create directory',
+ $temp_dir
+ );
}
}
@@ -246,7 +256,7 @@ private function deleteDirectory($directory)
* @param string $xpi The path to the .xpi extension.
* @param string $target_dir The path to the unzip directory.
*
- * @throws \Exception
+ * @throws IOException
* @return FirefoxProfile
*/
private function extractTo($xpi, $target_dir)
@@ -257,10 +267,10 @@ private function extractTo($xpi, $target_dir)
$zip->extractTo($target_dir);
$zip->close();
} else {
- throw new \Exception("Failed to open the firefox extension. '$xpi'");
+ throw IOException::forFileError('Failed to open the firefox extension.', $xpi);
}
} else {
- throw new \Exception("Firefox extension doesn't exist. '$xpi'");
+ throw IOException::forFileError('Firefox extension doesn\'t exist.', $xpi);
}
return $this;
@@ -287,7 +297,7 @@ private function parseExtensionName($extensionPath)
strpos($mozillaRsaHex, $objectIdentifierHexMarker, $firstMarkerPosInHex + 2); // phpcs:ignore
if ($secondMarkerPosInHexString === false) {
- throw new WebDriverException('Cannot install extension. Cannot fetch extension commonName');
+ throw RuntimeException::forError('Cannot install extension. Cannot fetch extension commonName');
}
// phpcs:ignore
diff --git a/lib/Interactions/Internal/WebDriverCoordinates.php b/lib/Interactions/Internal/WebDriverCoordinates.php
index 387bdbea6..3a92f0a64 100644
--- a/lib/Interactions/Internal/WebDriverCoordinates.php
+++ b/lib/Interactions/Internal/WebDriverCoordinates.php
@@ -11,7 +11,8 @@
class WebDriverCoordinates
{
/**
- * @var null
+ * @var mixed
+ * @todo remove in next major version (if it is unused)
*/
private $onScreen;
/**
@@ -28,9 +29,7 @@ class WebDriverCoordinates
private $auxiliary;
/**
- * @param null $on_screen
- * @param callable $in_view_port
- * @param callable $on_page
+ * @param mixed $on_screen
* @param string $auxiliary
*/
public function __construct($on_screen, callable $in_view_port, callable $on_page, $auxiliary)
diff --git a/lib/Interactions/Internal/WebDriverKeysRelatedAction.php b/lib/Interactions/Internal/WebDriverKeysRelatedAction.php
index 69f4aa179..a5ba0875f 100644
--- a/lib/Interactions/Internal/WebDriverKeysRelatedAction.php
+++ b/lib/Interactions/Internal/WebDriverKeysRelatedAction.php
@@ -24,15 +24,10 @@ abstract class WebDriverKeysRelatedAction
*/
protected $locationProvider;
- /**
- * @param WebDriverKeyboard $keyboard
- * @param WebDriverMouse $mouse
- * @param WebDriverLocatable $location_provider
- */
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
- WebDriverLocatable $location_provider = null
+ ?WebDriverLocatable $location_provider = null
) {
$this->keyboard = $keyboard;
$this->mouse = $mouse;
diff --git a/lib/Interactions/Internal/WebDriverMouseAction.php b/lib/Interactions/Internal/WebDriverMouseAction.php
index 5cb0cfd10..ecb1127ee 100644
--- a/lib/Interactions/Internal/WebDriverMouseAction.php
+++ b/lib/Interactions/Internal/WebDriverMouseAction.php
@@ -19,11 +19,7 @@ class WebDriverMouseAction
*/
protected $locationProvider;
- /**
- * @param WebDriverMouse $mouse
- * @param WebDriverLocatable|null $location_provider
- */
- public function __construct(WebDriverMouse $mouse, WebDriverLocatable $location_provider = null)
+ public function __construct(WebDriverMouse $mouse, ?WebDriverLocatable $location_provider = null)
{
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
diff --git a/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php b/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php
index 98fd824d7..c865da46a 100644
--- a/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php
+++ b/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php
@@ -18,14 +18,12 @@ class WebDriverMoveToOffsetAction extends WebDriverMouseAction implements WebDri
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,
+ ?WebDriverLocatable $location_provider = null,
$x_offset = null,
$y_offset = null
) {
diff --git a/lib/Interactions/Internal/WebDriverSendKeysAction.php b/lib/Interactions/Internal/WebDriverSendKeysAction.php
index 4e65cc27c..2ed3cfd06 100644
--- a/lib/Interactions/Internal/WebDriverSendKeysAction.php
+++ b/lib/Interactions/Internal/WebDriverSendKeysAction.php
@@ -15,15 +15,12 @@ class WebDriverSendKeysAction extends WebDriverKeysRelatedAction implements WebD
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,
+ ?WebDriverLocatable $location_provider = null,
$keys = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
diff --git a/lib/Interactions/Internal/WebDriverSingleKeyAction.php b/lib/Interactions/Internal/WebDriverSingleKeyAction.php
index 9b1a014d5..6efe9384a 100644
--- a/lib/Interactions/Internal/WebDriverSingleKeyAction.php
+++ b/lib/Interactions/Internal/WebDriverSingleKeyAction.php
@@ -2,6 +2,7 @@
namespace Facebook\WebDriver\Interactions\Internal;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
@@ -10,7 +11,7 @@
abstract class WebDriverSingleKeyAction extends WebDriverKeysRelatedAction implements WebDriverAction
{
- const MODIFIER_KEYS = [
+ public const MODIFIER_KEYS = [
WebDriverKeys::SHIFT,
WebDriverKeys::LEFT_SHIFT,
WebDriverKeys::RIGHT_SHIFT,
@@ -35,13 +36,13 @@ abstract class WebDriverSingleKeyAction extends WebDriverKeysRelatedAction imple
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
- WebDriverLocatable $location_provider = null,
+ ?WebDriverLocatable $location_provider = null,
$key = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
if (!in_array($key, self::MODIFIER_KEYS, true)) {
- throw new \InvalidArgumentException(
+ throw LogicException::forError(
sprintf(
'keyDown / keyUp actions can only be used for modifier keys, but "%s" was given',
$key
diff --git a/lib/Interactions/Touch/WebDriverDownAction.php b/lib/Interactions/Touch/WebDriverDownAction.php
index 2e0f1e5d1..225726fcf 100644
--- a/lib/Interactions/Touch/WebDriverDownAction.php
+++ b/lib/Interactions/Touch/WebDriverDownAction.php
@@ -16,7 +16,6 @@ class WebDriverDownAction extends WebDriverTouchAction implements WebDriverActio
private $y;
/**
- * @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
diff --git a/lib/Interactions/Touch/WebDriverFlickAction.php b/lib/Interactions/Touch/WebDriverFlickAction.php
index 5430852ac..acdb7fc3e 100644
--- a/lib/Interactions/Touch/WebDriverFlickAction.php
+++ b/lib/Interactions/Touch/WebDriverFlickAction.php
@@ -16,7 +16,6 @@ class WebDriverFlickAction extends WebDriverTouchAction implements WebDriverActi
private $y;
/**
- * @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
diff --git a/lib/Interactions/Touch/WebDriverFlickFromElementAction.php b/lib/Interactions/Touch/WebDriverFlickFromElementAction.php
index 799febe10..28d359718 100644
--- a/lib/Interactions/Touch/WebDriverFlickFromElementAction.php
+++ b/lib/Interactions/Touch/WebDriverFlickFromElementAction.php
@@ -21,8 +21,6 @@ class WebDriverFlickFromElementAction extends WebDriverTouchAction implements We
private $speed;
/**
- * @param WebDriverTouchScreen $touch_screen
- * @param WebDriverElement $element
* @param int $x
* @param int $y
* @param int $speed
diff --git a/lib/Interactions/Touch/WebDriverMoveAction.php b/lib/Interactions/Touch/WebDriverMoveAction.php
index 8cdf5eb99..d0a5f85f9 100644
--- a/lib/Interactions/Touch/WebDriverMoveAction.php
+++ b/lib/Interactions/Touch/WebDriverMoveAction.php
@@ -10,7 +10,6 @@ class WebDriverMoveAction extends WebDriverTouchAction implements WebDriverActio
private $y;
/**
- * @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
diff --git a/lib/Interactions/Touch/WebDriverScrollAction.php b/lib/Interactions/Touch/WebDriverScrollAction.php
index 0fd40c5b4..952d57e34 100644
--- a/lib/Interactions/Touch/WebDriverScrollAction.php
+++ b/lib/Interactions/Touch/WebDriverScrollAction.php
@@ -10,7 +10,6 @@ class WebDriverScrollAction extends WebDriverTouchAction implements WebDriverAct
private $y;
/**
- * @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
diff --git a/lib/Interactions/Touch/WebDriverScrollFromElementAction.php b/lib/Interactions/Touch/WebDriverScrollFromElementAction.php
index ba68bc62c..217564dc7 100644
--- a/lib/Interactions/Touch/WebDriverScrollFromElementAction.php
+++ b/lib/Interactions/Touch/WebDriverScrollFromElementAction.php
@@ -11,8 +11,6 @@ class WebDriverScrollFromElementAction extends WebDriverTouchAction implements W
private $y;
/**
- * @param WebDriverTouchScreen $touch_screen
- * @param WebDriverElement $element
* @param int $x
* @param int $y
*/
diff --git a/lib/Interactions/Touch/WebDriverTouchAction.php b/lib/Interactions/Touch/WebDriverTouchAction.php
index 3919170a9..10100ea21 100644
--- a/lib/Interactions/Touch/WebDriverTouchAction.php
+++ b/lib/Interactions/Touch/WebDriverTouchAction.php
@@ -19,13 +19,9 @@ abstract class WebDriverTouchAction
*/
protected $locationProvider;
- /**
- * @param WebDriverTouchScreen $touch_screen
- * @param WebDriverLocatable $location_provider
- */
public function __construct(
WebDriverTouchScreen $touch_screen,
- WebDriverLocatable $location_provider = null
+ ?WebDriverLocatable $location_provider = null
) {
$this->touchScreen = $touch_screen;
$this->locationProvider = $location_provider;
diff --git a/lib/Interactions/Touch/WebDriverTouchScreen.php b/lib/Interactions/Touch/WebDriverTouchScreen.php
index 21696fc90..ff9f9c4d1 100644
--- a/lib/Interactions/Touch/WebDriverTouchScreen.php
+++ b/lib/Interactions/Touch/WebDriverTouchScreen.php
@@ -12,7 +12,6 @@ interface WebDriverTouchScreen
/**
* Single tap on the touch enabled device.
*
- * @param WebDriverElement $element
* @return $this
*/
public function tap(WebDriverElement $element);
@@ -20,7 +19,6 @@ 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);
@@ -48,7 +46,6 @@ 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
@@ -64,7 +61,6 @@ public function flickFromElement(
/**
* Long press on the touch screen using finger motion events.
*
- * @param WebDriverElement $element
* @return $this
*/
public function longPress(WebDriverElement $element);
@@ -92,7 +88,6 @@ 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
diff --git a/lib/Interactions/WebDriverActions.php b/lib/Interactions/WebDriverActions.php
index b5172e2e7..031d91ac6 100644
--- a/lib/Interactions/WebDriverActions.php
+++ b/lib/Interactions/WebDriverActions.php
@@ -25,9 +25,6 @@ class WebDriverActions
protected $mouse;
protected $action;
- /**
- * @param WebDriverHasInputDevices $driver
- */
public function __construct(WebDriverHasInputDevices $driver)
{
$this->driver = $driver;
@@ -48,10 +45,9 @@ public function 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)
+ public function click(?WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverClickAction($this->mouse, $element)
@@ -64,10 +60,9 @@ public function click(WebDriverElement $element = null)
* 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)
+ public function clickAndHold(?WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverClickAndHoldAction($this->mouse, $element)
@@ -80,10 +75,9 @@ public function clickAndHold(WebDriverElement $element = null)
* 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)
+ public function contextClick(?WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverContextClickAction($this->mouse, $element)
@@ -96,10 +90,9 @@ public function contextClick(WebDriverElement $element = null)
* 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)
+ public function doubleClick(?WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverDoubleClickAction($this->mouse, $element)
@@ -111,8 +104,6 @@ public function doubleClick(WebDriverElement $element = null)
/**
* Drag and drop from $source to $target.
*
- * @param WebDriverElement $source
- * @param WebDriverElement $target
* @return WebDriverActions
*/
public function dragAndDrop(WebDriverElement $source, WebDriverElement $target)
@@ -133,7 +124,6 @@ public function dragAndDrop(WebDriverElement $source, WebDriverElement $target)
/**
* Drag $source and drop by offset ($x_offset, $y_offset).
*
- * @param WebDriverElement $source
* @param int $x_offset
* @param int $y_offset
* @return WebDriverActions
@@ -174,7 +164,6 @@ public function moveByOffset($x_offset, $y_offset)
* 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
@@ -195,10 +184,9 @@ public function moveToElement(WebDriverElement $element, $x_offset = null, $y_of
* 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)
+ public function release(?WebDriverElement $element = null)
{
$this->action->addAction(
new WebDriverButtonReleaseAction($this->mouse, $element)
@@ -212,11 +200,10 @@ public function release(WebDriverElement $element = null)
* 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)
+ public function keyDown(?WebDriverElement $element = null, $key = null)
{
$this->action->addAction(
new WebDriverKeyDownAction($this->keyboard, $this->mouse, $element, $key)
@@ -230,11 +217,10 @@ public function keyDown(WebDriverElement $element = null, $key = null)
* 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)
+ public function keyUp(?WebDriverElement $element = null, $key = null)
{
$this->action->addAction(
new WebDriverKeyUpAction($this->keyboard, $this->mouse, $element, $key)
@@ -248,11 +234,10 @@ public function keyUp(WebDriverElement $element = null, $key = null)
* 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)
+ public function sendKeys(?WebDriverElement $element = null, $keys = null)
{
$this->action->addAction(
new WebDriverSendKeysAction(
diff --git a/lib/Interactions/WebDriverCompositeAction.php b/lib/Interactions/WebDriverCompositeAction.php
index 168bf5ff5..955d61986 100644
--- a/lib/Interactions/WebDriverCompositeAction.php
+++ b/lib/Interactions/WebDriverCompositeAction.php
@@ -17,7 +17,6 @@ class WebDriverCompositeAction implements WebDriverAction
/**
* Add an WebDriverAction to the sequence.
*
- * @param WebDriverAction $action
* @return WebDriverCompositeAction The current instance.
*/
public function addAction(WebDriverAction $action)
diff --git a/lib/Interactions/WebDriverTouchActions.php b/lib/Interactions/WebDriverTouchActions.php
index da0e47815..fd3298410 100644
--- a/lib/Interactions/WebDriverTouchActions.php
+++ b/lib/Interactions/WebDriverTouchActions.php
@@ -33,7 +33,6 @@ public function __construct(WebDriver $driver)
}
/**
- * @param WebDriverElement $element
* @return WebDriverTouchActions
*/
public function tap(WebDriverElement $element)
@@ -102,7 +101,6 @@ public function scroll($x, $y)
}
/**
- * @param WebDriverElement $element
* @param int $x
* @param int $y
* @return WebDriverTouchActions
@@ -117,7 +115,6 @@ public function scrollFromElement(WebDriverElement $element, $x, $y)
}
/**
- * @param WebDriverElement $element
* @return WebDriverTouchActions
*/
public function doubleTap(WebDriverElement $element)
@@ -130,7 +127,6 @@ public function doubleTap(WebDriverElement $element)
}
/**
- * @param WebDriverElement $element
* @return WebDriverTouchActions
*/
public function longPress(WebDriverElement $element)
@@ -157,7 +153,6 @@ public function flick($x, $y)
}
/**
- * @param WebDriverElement $element
* @param int $x
* @param int $y
* @param int $speed
diff --git a/lib/Local/LocalWebDriver.php b/lib/Local/LocalWebDriver.php
index d695ff016..a23aefe1a 100644
--- a/lib/Local/LocalWebDriver.php
+++ b/lib/Local/LocalWebDriver.php
@@ -2,27 +2,17 @@
namespace Facebook\WebDriver\Local;
-use Facebook\WebDriver\Exception\WebDriverException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
+ * @codeCoverageIgnore
* @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)
- */
+ // @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,
@@ -30,26 +20,18 @@ public static function create(
$request_timeout_in_ms = null,
$http_proxy = null,
$http_proxy_port = null,
- DesiredCapabilities $required_capabilities = null
+ ?DesiredCapabilities $required_capabilities = null
) {
- throw new WebDriverException('Use start() method to start local WebDriver.');
+ throw LogicException::forError('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)
- */
+ // @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.');
+ throw LogicException::forError('Use start() method to start local WebDriver.');
}
}
diff --git a/lib/Net/URLChecker.php b/lib/Net/URLChecker.php
index 05e9cf1f6..5fe5d4abe 100644
--- a/lib/Net/URLChecker.php
+++ b/lib/Net/URLChecker.php
@@ -7,8 +7,8 @@
class URLChecker
{
- const POLL_INTERVAL_MS = 500;
- const CONNECT_TIMEOUT_MS = 500;
+ public const POLL_INTERVAL_MS = 500;
+ public const CONNECT_TIMEOUT_MS = 500;
public function waitUntilAvailable($timeout_in_ms, $url)
{
diff --git a/lib/Remote/CustomWebDriverCommand.php b/lib/Remote/CustomWebDriverCommand.php
index 157902199..cef2c95b2 100644
--- a/lib/Remote/CustomWebDriverCommand.php
+++ b/lib/Remote/CustomWebDriverCommand.php
@@ -2,12 +2,13 @@
namespace Facebook\WebDriver\Remote;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\WebDriverException;
class CustomWebDriverCommand extends WebDriverCommand
{
- const METHOD_GET = 'GET';
- const METHOD_POST = 'POST';
+ public const METHOD_GET = 'GET';
+ public const METHOD_POST = 'POST';
/** @var string */
private $customUrl;
@@ -18,7 +19,6 @@ class CustomWebDriverCommand extends WebDriverCommand
* @param string $session_id
* @param string $url
* @param string $method
- * @param array $parameters
*/
public function __construct($session_id, $url, $method, array $parameters)
{
@@ -34,7 +34,7 @@ public function __construct($session_id, $url, $method, array $parameters)
public function getCustomUrl()
{
if ($this->customUrl === null) {
- throw new WebDriverException('URL of custom command is not set');
+ throw LogicException::forError('URL of custom command is not set');
}
return $this->customUrl;
@@ -47,7 +47,7 @@ public function getCustomUrl()
public function getCustomMethod()
{
if ($this->customMethod === null) {
- throw new WebDriverException('Method of custom command is not set');
+ throw LogicException::forError('Method of custom command is not set');
}
return $this->customMethod;
@@ -62,7 +62,7 @@ protected function setCustomRequestParameters($custom_url, $custom_method)
{
$allowedMethods = [static::METHOD_GET, static::METHOD_POST];
if (!in_array($custom_method, $allowedMethods, true)) {
- throw new WebDriverException(
+ throw LogicException::forError(
sprintf(
'Invalid custom method "%s", must be one of [%s]',
$custom_method,
@@ -73,7 +73,7 @@ protected function setCustomRequestParameters($custom_url, $custom_method)
$this->customMethod = $custom_method;
if (mb_strpos($custom_url, '/') !== 0) {
- throw new WebDriverException(
+ throw LogicException::forError(
sprintf('URL of custom command has to start with / but is "%s"', $custom_url)
);
}
diff --git a/lib/Remote/DesiredCapabilities.php b/lib/Remote/DesiredCapabilities.php
index a7bde312e..88aa6b141 100644
--- a/lib/Remote/DesiredCapabilities.php
+++ b/lib/Remote/DesiredCapabilities.php
@@ -2,8 +2,8 @@
namespace Facebook\WebDriver\Remote;
-use Exception;
use Facebook\WebDriver\Chrome\ChromeOptions;
+use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Firefox\FirefoxDriver;
use Facebook\WebDriver\Firefox\FirefoxOptions;
use Facebook\WebDriver\Firefox\FirefoxProfile;
@@ -20,7 +20,6 @@ class DesiredCapabilities implements WebDriverCapabilities
WebDriverCapabilityType::PLATFORM => 'platformName',
WebDriverCapabilityType::VERSION => 'browserVersion',
WebDriverCapabilityType::ACCEPT_SSL_CERTS => 'acceptInsecureCerts',
- ChromeOptions::CAPABILITY => ChromeOptions::CAPABILITY_W3C,
];
public function __construct(array $capabilities = [])
@@ -152,7 +151,7 @@ public function isJavascriptEnabled()
* This is a htmlUnit-only option.
*
* @param bool $enabled
- * @throws Exception
+ * @throws UnsupportedOperationException
* @return DesiredCapabilities
* @see https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#read-write-capabilities
*/
@@ -160,7 +159,7 @@ public function setJavascriptEnabled($enabled)
{
$browser = $this->getBrowserName();
if ($browser && $browser !== WebDriverBrowserType::HTMLUNIT) {
- throw new Exception(
+ throw new UnsupportedOperationException(
'isJavascriptEnabled() is a htmlunit-only option. ' .
'See https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#read-write-capabilities.'
);
@@ -250,16 +249,7 @@ public function toW3cCompatibleArray()
// Convert ChromeOptions
if (array_key_exists(ChromeOptions::CAPABILITY, $ossCapabilities)) {
- if (array_key_exists(ChromeOptions::CAPABILITY_W3C, $ossCapabilities)) {
- $w3cCapabilities[ChromeOptions::CAPABILITY_W3C] = new \ArrayObject(
- array_merge_recursive(
- (array) $ossCapabilities[ChromeOptions::CAPABILITY],
- (array) $ossCapabilities[ChromeOptions::CAPABILITY_W3C]
- )
- );
- } else {
- $w3cCapabilities[ChromeOptions::CAPABILITY_W3C] = $ossCapabilities[ChromeOptions::CAPABILITY];
- }
+ $w3cCapabilities[ChromeOptions::CAPABILITY] = $ossCapabilities[ChromeOptions::CAPABILITY];
}
// Convert Firefox profile
@@ -433,8 +423,6 @@ private function set($key, $value)
*/
private function get($key, $default = null)
{
- return isset($this->capabilities[$key])
- ? $this->capabilities[$key]
- : $default;
+ return $this->capabilities[$key] ?? $default;
}
}
diff --git a/lib/Remote/DriverCommand.php b/lib/Remote/DriverCommand.php
index 3d35549bd..a3a230b7c 100644
--- a/lib/Remote/DriverCommand.php
+++ b/lib/Remote/DriverCommand.php
@@ -9,143 +9,143 @@
*/
class DriverCommand
{
- const GET_ALL_SESSIONS = 'getAllSessions';
- const GET_CAPABILITIES = 'getCapabilities';
- const NEW_SESSION = 'newSession';
- const STATUS = 'status';
- const CLOSE = 'close';
- const QUIT = 'quit';
- const GET = 'get';
- const GO_BACK = 'goBack';
- const GO_FORWARD = 'goForward';
- const REFRESH = 'refresh';
- const ADD_COOKIE = 'addCookie';
- const GET_ALL_COOKIES = 'getCookies';
- const DELETE_COOKIE = 'deleteCookie';
- const DELETE_ALL_COOKIES = 'deleteAllCookies';
- const FIND_ELEMENT = 'findElement';
- const FIND_ELEMENTS = 'findElements';
- const FIND_CHILD_ELEMENT = 'findChildElement';
- const FIND_CHILD_ELEMENTS = 'findChildElements';
- const CLEAR_ELEMENT = 'clearElement';
- const CLICK_ELEMENT = 'clickElement';
- const SEND_KEYS_TO_ELEMENT = 'sendKeysToElement';
- const SEND_KEYS_TO_ACTIVE_ELEMENT = 'sendKeysToActiveElement';
- const SUBMIT_ELEMENT = 'submitElement';
- const UPLOAD_FILE = 'uploadFile';
- const GET_CURRENT_WINDOW_HANDLE = 'getCurrentWindowHandle';
- const GET_WINDOW_HANDLES = 'getWindowHandles';
- const GET_CURRENT_CONTEXT_HANDLE = 'getCurrentContextHandle';
- const GET_CONTEXT_HANDLES = 'getContextHandles';
+ public const GET_ALL_SESSIONS = 'getAllSessions';
+ public const GET_CAPABILITIES = 'getCapabilities';
+ public const NEW_SESSION = 'newSession';
+ public const STATUS = 'status';
+ public const CLOSE = 'close';
+ public const QUIT = 'quit';
+ public const GET = 'get';
+ public const GO_BACK = 'goBack';
+ public const GO_FORWARD = 'goForward';
+ public const REFRESH = 'refresh';
+ public const ADD_COOKIE = 'addCookie';
+ public const GET_ALL_COOKIES = 'getCookies';
+ public const DELETE_COOKIE = 'deleteCookie';
+ public const DELETE_ALL_COOKIES = 'deleteAllCookies';
+ public const FIND_ELEMENT = 'findElement';
+ public const FIND_ELEMENTS = 'findElements';
+ public const FIND_CHILD_ELEMENT = 'findChildElement';
+ public const FIND_CHILD_ELEMENTS = 'findChildElements';
+ public const CLEAR_ELEMENT = 'clearElement';
+ public const CLICK_ELEMENT = 'clickElement';
+ public const SEND_KEYS_TO_ELEMENT = 'sendKeysToElement';
+ public const SEND_KEYS_TO_ACTIVE_ELEMENT = 'sendKeysToActiveElement';
+ public const SUBMIT_ELEMENT = 'submitElement';
+ public const UPLOAD_FILE = 'uploadFile';
+ public const GET_CURRENT_WINDOW_HANDLE = 'getCurrentWindowHandle';
+ public const GET_WINDOW_HANDLES = 'getWindowHandles';
+ public const GET_CURRENT_CONTEXT_HANDLE = 'getCurrentContextHandle';
+ public const GET_CONTEXT_HANDLES = 'getContextHandles';
// Switching between to window/frame/iframe
- const SWITCH_TO_WINDOW = 'switchToWindow';
- const SWITCH_TO_CONTEXT = 'switchToContext';
- const SWITCH_TO_FRAME = 'switchToFrame';
- const SWITCH_TO_PARENT_FRAME = 'switchToParentFrame';
- const GET_ACTIVE_ELEMENT = 'getActiveElement';
+ public const SWITCH_TO_WINDOW = 'switchToWindow';
+ public const SWITCH_TO_CONTEXT = 'switchToContext';
+ public const SWITCH_TO_FRAME = 'switchToFrame';
+ public const SWITCH_TO_PARENT_FRAME = 'switchToParentFrame';
+ public const GET_ACTIVE_ELEMENT = 'getActiveElement';
// Information of the page
- const GET_CURRENT_URL = 'getCurrentUrl';
- const GET_PAGE_SOURCE = 'getPageSource';
- const GET_TITLE = 'getTitle';
+ public const GET_CURRENT_URL = 'getCurrentUrl';
+ public const GET_PAGE_SOURCE = 'getPageSource';
+ public const GET_TITLE = 'getTitle';
// Javascript API
- const EXECUTE_SCRIPT = 'executeScript';
- const EXECUTE_ASYNC_SCRIPT = 'executeAsyncScript';
+ public const EXECUTE_SCRIPT = 'executeScript';
+ public const EXECUTE_ASYNC_SCRIPT = 'executeAsyncScript';
// API getting information from an element.
- const GET_ELEMENT_TEXT = 'getElementText';
- const GET_ELEMENT_TAG_NAME = 'getElementTagName';
- const IS_ELEMENT_SELECTED = 'isElementSelected';
- const IS_ELEMENT_ENABLED = 'isElementEnabled';
- const IS_ELEMENT_DISPLAYED = 'isElementDisplayed';
- const GET_ELEMENT_LOCATION = 'getElementLocation';
- const GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW = 'getElementLocationOnceScrolledIntoView';
- const GET_ELEMENT_SIZE = 'getElementSize';
- const GET_ELEMENT_ATTRIBUTE = 'getElementAttribute';
- const GET_ELEMENT_VALUE_OF_CSS_PROPERTY = 'getElementValueOfCssProperty';
- const ELEMENT_EQUALS = 'elementEquals';
- const SCREENSHOT = 'screenshot';
+ public const GET_ELEMENT_TEXT = 'getElementText';
+ public const GET_ELEMENT_TAG_NAME = 'getElementTagName';
+ public const IS_ELEMENT_SELECTED = 'isElementSelected';
+ public const IS_ELEMENT_ENABLED = 'isElementEnabled';
+ public const IS_ELEMENT_DISPLAYED = 'isElementDisplayed';
+ public const GET_ELEMENT_LOCATION = 'getElementLocation';
+ public const GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW = 'getElementLocationOnceScrolledIntoView';
+ public const GET_ELEMENT_SIZE = 'getElementSize';
+ public const GET_ELEMENT_ATTRIBUTE = 'getElementAttribute';
+ public const GET_ELEMENT_VALUE_OF_CSS_PROPERTY = 'getElementValueOfCssProperty';
+ public const ELEMENT_EQUALS = 'elementEquals';
+ public const SCREENSHOT = 'screenshot';
// Alert API
- const ACCEPT_ALERT = 'acceptAlert';
- const DISMISS_ALERT = 'dismissAlert';
- const GET_ALERT_TEXT = 'getAlertText';
- const SET_ALERT_VALUE = 'setAlertValue';
+ public const ACCEPT_ALERT = 'acceptAlert';
+ public const DISMISS_ALERT = 'dismissAlert';
+ public const GET_ALERT_TEXT = 'getAlertText';
+ public const SET_ALERT_VALUE = 'setAlertValue';
// Timeout API
- const SET_TIMEOUT = 'setTimeout';
- const IMPLICITLY_WAIT = 'implicitlyWait';
- const SET_SCRIPT_TIMEOUT = 'setScriptTimeout';
+ public const SET_TIMEOUT = 'setTimeout';
+ public const IMPLICITLY_WAIT = 'implicitlyWait';
+ public const SET_SCRIPT_TIMEOUT = 'setScriptTimeout';
/** @deprecated */
- const EXECUTE_SQL = 'executeSQL';
- const GET_LOCATION = 'getLocation';
- const SET_LOCATION = 'setLocation';
- const GET_APP_CACHE = 'getAppCache';
- const GET_APP_CACHE_STATUS = 'getStatus';
- const CLEAR_APP_CACHE = 'clearAppCache';
- const IS_BROWSER_ONLINE = 'isBrowserOnline';
- const SET_BROWSER_ONLINE = 'setBrowserOnline';
+ public const EXECUTE_SQL = 'executeSQL';
+ public const GET_LOCATION = 'getLocation';
+ public const SET_LOCATION = 'setLocation';
+ public const GET_APP_CACHE = 'getAppCache';
+ public const GET_APP_CACHE_STATUS = 'getStatus';
+ public const CLEAR_APP_CACHE = 'clearAppCache';
+ public const IS_BROWSER_ONLINE = 'isBrowserOnline';
+ public const SET_BROWSER_ONLINE = 'setBrowserOnline';
// Local storage
- const GET_LOCAL_STORAGE_ITEM = 'getLocalStorageItem';
- const GET_LOCAL_STORAGE_KEYS = 'getLocalStorageKeys';
- const SET_LOCAL_STORAGE_ITEM = 'setLocalStorageItem';
- const REMOVE_LOCAL_STORAGE_ITEM = 'removeLocalStorageItem';
- const CLEAR_LOCAL_STORAGE = 'clearLocalStorage';
- const GET_LOCAL_STORAGE_SIZE = 'getLocalStorageSize';
+ public const GET_LOCAL_STORAGE_ITEM = 'getLocalStorageItem';
+ public const GET_LOCAL_STORAGE_KEYS = 'getLocalStorageKeys';
+ public const SET_LOCAL_STORAGE_ITEM = 'setLocalStorageItem';
+ public const REMOVE_LOCAL_STORAGE_ITEM = 'removeLocalStorageItem';
+ public const CLEAR_LOCAL_STORAGE = 'clearLocalStorage';
+ public const GET_LOCAL_STORAGE_SIZE = 'getLocalStorageSize';
// Session storage
- const GET_SESSION_STORAGE_ITEM = 'getSessionStorageItem';
- const GET_SESSION_STORAGE_KEYS = 'getSessionStorageKey';
- const SET_SESSION_STORAGE_ITEM = 'setSessionStorageItem';
- const REMOVE_SESSION_STORAGE_ITEM = 'removeSessionStorageItem';
- const CLEAR_SESSION_STORAGE = 'clearSessionStorage';
- const GET_SESSION_STORAGE_SIZE = 'getSessionStorageSize';
+ public const GET_SESSION_STORAGE_ITEM = 'getSessionStorageItem';
+ public const GET_SESSION_STORAGE_KEYS = 'getSessionStorageKey';
+ public const SET_SESSION_STORAGE_ITEM = 'setSessionStorageItem';
+ public const REMOVE_SESSION_STORAGE_ITEM = 'removeSessionStorageItem';
+ public const CLEAR_SESSION_STORAGE = 'clearSessionStorage';
+ public const GET_SESSION_STORAGE_SIZE = 'getSessionStorageSize';
// Screen orientation
- const SET_SCREEN_ORIENTATION = 'setScreenOrientation';
- const GET_SCREEN_ORIENTATION = 'getScreenOrientation';
+ public const SET_SCREEN_ORIENTATION = 'setScreenOrientation';
+ public const GET_SCREEN_ORIENTATION = 'getScreenOrientation';
// These belong to the Advanced user interactions - an element is optional for these commands.
- const CLICK = 'mouseClick';
- const DOUBLE_CLICK = 'mouseDoubleClick';
- const MOUSE_DOWN = 'mouseButtonDown';
- const MOUSE_UP = 'mouseButtonUp';
- const MOVE_TO = 'mouseMoveTo';
+ public const CLICK = 'mouseClick';
+ public const DOUBLE_CLICK = 'mouseDoubleClick';
+ public const MOUSE_DOWN = 'mouseButtonDown';
+ public const MOUSE_UP = 'mouseButtonUp';
+ public const MOVE_TO = 'mouseMoveTo';
// Those allow interactions with the Input Methods installed on the system.
- const IME_GET_AVAILABLE_ENGINES = 'imeGetAvailableEngines';
- const IME_GET_ACTIVE_ENGINE = 'imeGetActiveEngine';
- const IME_IS_ACTIVATED = 'imeIsActivated';
- const IME_DEACTIVATE = 'imeDeactivate';
- const IME_ACTIVATE_ENGINE = 'imeActivateEngine';
+ public const IME_GET_AVAILABLE_ENGINES = 'imeGetAvailableEngines';
+ public const IME_GET_ACTIVE_ENGINE = 'imeGetActiveEngine';
+ public const IME_IS_ACTIVATED = 'imeIsActivated';
+ public const IME_DEACTIVATE = 'imeDeactivate';
+ public const IME_ACTIVATE_ENGINE = 'imeActivateEngine';
// These belong to the Advanced Touch API
- const TOUCH_SINGLE_TAP = 'touchSingleTap';
- const TOUCH_DOWN = 'touchDown';
- const TOUCH_UP = 'touchUp';
- const TOUCH_MOVE = 'touchMove';
- const TOUCH_SCROLL = 'touchScroll';
- const TOUCH_DOUBLE_TAP = 'touchDoubleTap';
- const TOUCH_LONG_PRESS = 'touchLongPress';
- const TOUCH_FLICK = 'touchFlick';
+ public const TOUCH_SINGLE_TAP = 'touchSingleTap';
+ public const TOUCH_DOWN = 'touchDown';
+ public const TOUCH_UP = 'touchUp';
+ public const TOUCH_MOVE = 'touchMove';
+ public const TOUCH_SCROLL = 'touchScroll';
+ public const TOUCH_DOUBLE_TAP = 'touchDoubleTap';
+ public const TOUCH_LONG_PRESS = 'touchLongPress';
+ public const TOUCH_FLICK = 'touchFlick';
// Window API (beta)
- const SET_WINDOW_SIZE = 'setWindowSize';
- const SET_WINDOW_POSITION = 'setWindowPosition';
- const GET_WINDOW_SIZE = 'getWindowSize';
- const GET_WINDOW_POSITION = 'getWindowPosition';
- const MAXIMIZE_WINDOW = 'maximizeWindow';
- const FULLSCREEN_WINDOW = 'fullscreenWindow';
+ public const SET_WINDOW_SIZE = 'setWindowSize';
+ public const SET_WINDOW_POSITION = 'setWindowPosition';
+ public const GET_WINDOW_SIZE = 'getWindowSize';
+ public const GET_WINDOW_POSITION = 'getWindowPosition';
+ public const MAXIMIZE_WINDOW = 'maximizeWindow';
+ public const FULLSCREEN_WINDOW = 'fullscreenWindow';
// Logging API
- const GET_AVAILABLE_LOG_TYPES = 'getAvailableLogTypes';
- const GET_LOG = 'getLog';
- const GET_SESSION_LOGS = 'getSessionLogs';
+ public const GET_AVAILABLE_LOG_TYPES = 'getAvailableLogTypes';
+ public const GET_LOG = 'getLog';
+ public const GET_SESSION_LOGS = 'getSessionLogs';
// Mobile API
- const GET_NETWORK_CONNECTION = 'getNetworkConnection';
- const SET_NETWORK_CONNECTION = 'setNetworkConnection';
+ public const GET_NETWORK_CONNECTION = 'getNetworkConnection';
+ public const SET_NETWORK_CONNECTION = 'setNetworkConnection';
// Custom command
- const CUSTOM_COMMAND = 'customCommand';
+ public const CUSTOM_COMMAND = 'customCommand';
// W3C specific
- const ACTIONS = 'actions';
- const GET_ELEMENT_PROPERTY = 'getElementProperty';
- const GET_NAMED_COOKIE = 'getNamedCookie';
- const NEW_WINDOW = 'newWindow';
- const TAKE_ELEMENT_SCREENSHOT = 'takeElementScreenshot';
- const MINIMIZE_WINDOW = 'minimizeWindow';
- const GET_ELEMENT_SHADOW_ROOT = 'getElementShadowRoot';
- const FIND_ELEMENT_FROM_SHADOW_ROOT = 'findElementFromShadowRoot';
- const FIND_ELEMENTS_FROM_SHADOW_ROOT = 'findElementsFromShadowRoot';
+ public const ACTIONS = 'actions';
+ public const GET_ELEMENT_PROPERTY = 'getElementProperty';
+ public const GET_NAMED_COOKIE = 'getNamedCookie';
+ public const NEW_WINDOW = 'newWindow';
+ public const TAKE_ELEMENT_SCREENSHOT = 'takeElementScreenshot';
+ public const MINIMIZE_WINDOW = 'minimizeWindow';
+ public const GET_ELEMENT_SHADOW_ROOT = 'getElementShadowRoot';
+ public const FIND_ELEMENT_FROM_SHADOW_ROOT = 'findElementFromShadowRoot';
+ public const FIND_ELEMENTS_FROM_SHADOW_ROOT = 'findElementsFromShadowRoot';
private function __construct()
{
diff --git a/lib/Remote/ExecuteMethod.php b/lib/Remote/ExecuteMethod.php
index 3f1636e5b..ba659e6d2 100644
--- a/lib/Remote/ExecuteMethod.php
+++ b/lib/Remote/ExecuteMethod.php
@@ -6,7 +6,6 @@ interface ExecuteMethod
{
/**
* @param string $command_name
- * @param array $parameters
* @return WebDriverResponse
*/
public function execute($command_name, array $parameters = []);
diff --git a/lib/Remote/HttpCommandExecutor.php b/lib/Remote/HttpCommandExecutor.php
index 94e2cb5d5..3e3ef1719 100644
--- a/lib/Remote/HttpCommandExecutor.php
+++ b/lib/Remote/HttpCommandExecutor.php
@@ -2,18 +2,18 @@
namespace Facebook\WebDriver\Remote;
-use BadMethodCallException;
-use Facebook\WebDriver\Exception\WebDriverCurlException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
+use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
+use Facebook\WebDriver\Exception\Internal\WebDriverCurlException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\WebDriverCommandExecutor;
-use InvalidArgumentException;
/**
* Command executor talking to the standalone server via HTTP.
*/
class HttpCommandExecutor implements WebDriverCommandExecutor
{
- const DEFAULT_HTTP_HEADERS = [
+ public const DEFAULT_HTTP_HEADERS = [
'Content-Type: application/json;charset=UTF-8',
'Accept: application/json',
];
@@ -175,6 +175,8 @@ class HttpCommandExecutor implements WebDriverCommandExecutor
DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'],
DriverCommand::SET_WINDOW_SIZE => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'],
DriverCommand::SET_WINDOW_POSITION => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'],
+ // Selenium extension of W3C protocol
+ DriverCommand::UPLOAD_FILE => ['method' => 'POST', 'url' => '/session/:sessionId/se/file'],
];
/**
* @var string
@@ -270,9 +272,6 @@ public function setRequestTimeout($timeout_in_ms)
}
/**
- * @param WebDriverCommand $command
- *
- * @throws WebDriverException
* @return WebDriverResponse
*/
public function execute(WebDriverCommand $command)
@@ -282,7 +281,7 @@ public function execute(WebDriverCommand $command)
$url = $http_options['url'];
$sessionID = $command->getSessionID();
- $url = str_replace(':sessionId', $sessionID === null ? '' : $sessionID, $url);
+ $url = str_replace(':sessionId', $sessionID ?? '', $url);
$params = $command->getParameters();
foreach ($params as $name => $value) {
if ($name[0] === ':') {
@@ -292,13 +291,7 @@ public function execute(WebDriverCommand $command)
}
if (is_array($params) && !empty($params) && $http_method !== 'POST') {
- throw new BadMethodCallException(sprintf(
- 'The http method called for %s is %s but it has to be POST' .
- ' if you want to pass the JSON params %s',
- $url,
- $http_method,
- json_encode($params)
- ));
+ throw LogicException::forInvalidHttpMethod($url, $http_method, $params);
}
curl_setopt($this->curl, CURLOPT_URL, $this->url . $url);
@@ -334,30 +327,13 @@ public function execute(WebDriverCommand $command)
$raw_results = trim(curl_exec($this->curl));
if ($error = curl_error($this->curl)) {
- $msg = sprintf(
- 'Curl error thrown for http %s to %s',
- $http_method,
- $url
- );
- if (is_array($params) && !empty($params)) {
- $msg .= sprintf(' with params: %s', json_encode($params, JSON_UNESCAPED_SLASHES));
- }
-
- throw new WebDriverCurlException($msg . "\n\n" . $error);
+ throw WebDriverCurlException::forCurlError($http_method, $url, $error, is_array($params) ? $params : null);
}
$results = json_decode($raw_results, true);
if ($results === null && json_last_error() !== JSON_ERROR_NONE) {
- throw new WebDriverException(
- sprintf(
- "JSON decoding of remote response failed.\n" .
- "Error code: %d\n" .
- "The response: '%s'\n",
- json_last_error(),
- $raw_results
- )
- );
+ throw UnexpectedResponseException::forJsonDecodingError(json_last_error(), $raw_results);
}
$value = null;
@@ -379,13 +355,13 @@ public function execute(WebDriverCommand $command)
$sessionId = $results['sessionId'];
}
- // @see https://w3c.github.io/webdriver/webdriver-spec.html#handling-errors
+ // @see https://w3c.github.io/webdriver/#errors
if (isset($value['error'])) {
// W3C's WebDriver
WebDriverException::throwException($value['error'], $message, $results);
}
- $status = isset($results['status']) ? $results['status'] : 0;
+ $status = $results['status'] ?? 0;
if ($status !== 0) {
// Legacy JsonWire
WebDriverException::throwException($status, $message, $results);
@@ -414,7 +390,7 @@ protected function getCommandHttpOptions(WebDriverCommand $command)
$commandName = $command->getName();
if (!isset(self::$commands[$commandName])) {
if ($this->isW3cCompliant && !isset(self::$w3cCompliantCommands[$commandName])) {
- throw new InvalidArgumentException($command->getName() . ' is not a valid command.');
+ throw LogicException::forError($command->getName() . ' is not a valid command.');
}
}
diff --git a/lib/Remote/JsonWireCompat.php b/lib/Remote/JsonWireCompat.php
index 65a6956ba..b9e1b5ee4 100644
--- a/lib/Remote/JsonWireCompat.php
+++ b/lib/Remote/JsonWireCompat.php
@@ -2,6 +2,7 @@
namespace Facebook\WebDriver\Remote;
+use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\WebDriverBy;
/**
@@ -14,12 +15,21 @@ abstract class JsonWireCompat
/**
* Element identifier defined in the W3C's WebDriver protocol.
*
- * @see https://w3c.github.io/webdriver/webdriver-spec.html#elements
+ * @see https://w3c.github.io/webdriver/#elements
*/
- const WEB_DRIVER_ELEMENT_IDENTIFIER = 'element-6066-11e4-a52e-4f735466cecf';
+ public const WEB_DRIVER_ELEMENT_IDENTIFIER = 'element-6066-11e4-a52e-4f735466cecf';
- public static function getElement(array $rawElement)
+ /**
+ * @param mixed $rawElement Value is validated to by an array, exception is thrown otherwise
+ * @throws UnexpectedResponseException When value of other type than array is given
+ */
+ public static function getElement($rawElement)
{
+ // The method intentionally accept mixed, so that assertion of the rawElement format could be done on one place
+ if (!is_array($rawElement)) {
+ throw UnexpectedResponseException::forElementNotArray($rawElement);
+ }
+
if (array_key_exists(self::WEB_DRIVER_ELEMENT_IDENTIFIER, $rawElement)) {
// W3C's WebDriver
return $rawElement[self::WEB_DRIVER_ELEMENT_IDENTIFIER];
@@ -30,7 +40,6 @@ public static function getElement(array $rawElement)
}
/**
- * @param WebDriverBy $by
* @param bool $isW3cCompliant
*
* @return array
diff --git a/lib/Remote/RemoteExecuteMethod.php b/lib/Remote/RemoteExecuteMethod.php
index 6cc0c26a7..265abcc1e 100644
--- a/lib/Remote/RemoteExecuteMethod.php
+++ b/lib/Remote/RemoteExecuteMethod.php
@@ -9,9 +9,6 @@ class RemoteExecuteMethod implements ExecuteMethod
*/
private $driver;
- /**
- * @param RemoteWebDriver $driver
- */
public function __construct(RemoteWebDriver $driver)
{
$this->driver = $driver;
@@ -19,7 +16,6 @@ public function __construct(RemoteWebDriver $driver)
/**
* @param string $command_name
- * @param array $parameters
* @return mixed
*/
public function execute($command_name, array $parameters = [])
diff --git a/lib/Remote/RemoteMouse.php b/lib/Remote/RemoteMouse.php
index d2429967e..fee209f94 100644
--- a/lib/Remote/RemoteMouse.php
+++ b/lib/Remote/RemoteMouse.php
@@ -11,11 +11,11 @@
class RemoteMouse implements WebDriverMouse
{
/** @internal */
- const BUTTON_LEFT = 0;
+ public const BUTTON_LEFT = 0;
/** @internal */
- const BUTTON_MIDDLE = 1;
+ public const BUTTON_MIDDLE = 1;
/** @internal */
- const BUTTON_RIGHT = 2;
+ public const BUTTON_RIGHT = 2;
/**
* @var RemoteExecuteMethod
@@ -27,7 +27,6 @@ class RemoteMouse implements WebDriverMouse
private $isW3cCompliant;
/**
- * @param RemoteExecuteMethod $executor
* @param bool $isW3cCompliant
*/
public function __construct(RemoteExecuteMethod $executor, $isW3cCompliant = false)
@@ -37,11 +36,9 @@ public function __construct(RemoteExecuteMethod $executor, $isW3cCompliant = fal
}
/**
- * @param null|WebDriverCoordinates $where
- *
* @return RemoteMouse
*/
- public function click(WebDriverCoordinates $where = null)
+ public function click(?WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$moveAction = $where ? [$this->createMoveAction($where)] : [];
@@ -68,11 +65,9 @@ public function click(WebDriverCoordinates $where = null)
}
/**
- * @param WebDriverCoordinates $where
- *
* @return RemoteMouse
*/
- public function contextClick(WebDriverCoordinates $where = null)
+ public function contextClick(?WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$moveAction = $where ? [$this->createMoveAction($where)] : [];
@@ -108,11 +103,9 @@ public function contextClick(WebDriverCoordinates $where = null)
}
/**
- * @param WebDriverCoordinates $where
- *
* @return RemoteMouse
*/
- public function doubleClick(WebDriverCoordinates $where = null)
+ public function doubleClick(?WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$clickActions = $this->createClickActions();
@@ -138,11 +131,9 @@ public function doubleClick(WebDriverCoordinates $where = null)
}
/**
- * @param WebDriverCoordinates $where
- *
* @return RemoteMouse
*/
- public function mouseDown(WebDriverCoordinates $where = null)
+ public function mouseDown(?WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$this->executor->execute(DriverCommand::ACTIONS, [
@@ -172,14 +163,13 @@ public function mouseDown(WebDriverCoordinates $where = null)
}
/**
- * @param WebDriverCoordinates $where
* @param int|null $x_offset
* @param int|null $y_offset
*
* @return RemoteMouse
*/
public function mouseMove(
- WebDriverCoordinates $where = null,
+ ?WebDriverCoordinates $where = null,
$x_offset = null,
$y_offset = null
) {
@@ -215,11 +205,9 @@ public function mouseMove(
}
/**
- * @param WebDriverCoordinates $where
- *
* @return RemoteMouse
*/
- public function mouseUp(WebDriverCoordinates $where = null)
+ public function mouseUp(?WebDriverCoordinates $where = null)
{
if ($this->isW3cCompliant) {
$moveAction = $where ? [$this->createMoveAction($where)] : [];
@@ -249,10 +237,7 @@ public function mouseUp(WebDriverCoordinates $where = null)
return $this;
}
- /**
- * @param WebDriverCoordinates $where
- */
- protected function moveIfNeeded(WebDriverCoordinates $where = null)
+ protected function moveIfNeeded(?WebDriverCoordinates $where = null)
{
if ($where) {
$this->mouseMove($where);
@@ -260,22 +245,21 @@ protected function moveIfNeeded(WebDriverCoordinates $where = null)
}
/**
- * @param WebDriverCoordinates $where
* @param int|null $x_offset
* @param int|null $y_offset
*
* @return array
*/
private function createMoveAction(
- WebDriverCoordinates $where = null,
+ ?WebDriverCoordinates $where = null,
$x_offset = null,
$y_offset = null
) {
$move_action = [
'type' => 'pointerMove',
'duration' => 100, // to simulate human delay
- 'x' => $x_offset === null ? 0 : $x_offset,
- 'y' => $y_offset === null ? 0 : $y_offset,
+ 'x' => $x_offset ?? 0,
+ 'y' => $y_offset ?? 0,
];
if ($where !== null) {
diff --git a/lib/Remote/RemoteStatus.php b/lib/Remote/RemoteStatus.php
index 3d123bd45..73ebeba21 100644
--- a/lib/Remote/RemoteStatus.php
+++ b/lib/Remote/RemoteStatus.php
@@ -29,7 +29,6 @@ protected function __construct($isReady, $message, array $meta = [])
}
/**
- * @param array $responseBody
* @return RemoteStatus
*/
public static function createFromResponse(array $responseBody)
diff --git a/lib/Remote/RemoteTargetLocator.php b/lib/Remote/RemoteTargetLocator.php
index 5b9881371..979b9c4f6 100644
--- a/lib/Remote/RemoteTargetLocator.php
+++ b/lib/Remote/RemoteTargetLocator.php
@@ -2,7 +2,7 @@
namespace Facebook\WebDriver\Remote;
-use Facebook\WebDriver\Exception\UnsupportedOperationException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\WebDriverAlert;
use Facebook\WebDriver\WebDriverElement;
use Facebook\WebDriver\WebDriverTargetLocator;
@@ -53,7 +53,7 @@ public function frame($frame)
} elseif (is_int($frame)) {
$id = $frame;
} else {
- throw new \InvalidArgumentException(
+ throw LogicException::forError(
'In W3C compliance mode frame must be either instance of WebDriverElement, integer or null'
);
}
@@ -111,17 +111,17 @@ public function window($handle)
* @param string $windowType The type of a new browser window that should be created. One of [tab, window].
* The created window is not guaranteed to be of the requested type; if the driver does not support the requested
* type, a new browser window will be created of whatever type the driver does support.
- * @throws UnsupportedOperationException
+ * @throws LogicException
* @return RemoteWebDriver This driver focused on the given window
*/
public function newWindow($windowType = self::WINDOW_TYPE_TAB)
{
if ($windowType !== self::WINDOW_TYPE_TAB && $windowType !== self::WINDOW_TYPE_WINDOW) {
- throw new \InvalidArgumentException('Window type must by either "tab" or "window"');
+ throw LogicException::forError('Window type must by either "tab" or "window"');
}
if (!$this->isW3cCompliant) {
- throw new UnsupportedOperationException('New window is only supported in W3C mode');
+ throw LogicException::forError('New window is only supported in W3C mode');
}
$response = $this->executor->execute(DriverCommand::NEW_WINDOW, ['type' => $windowType]);
diff --git a/lib/Remote/RemoteTouchScreen.php b/lib/Remote/RemoteTouchScreen.php
index 889c12e6c..951c8619a 100644
--- a/lib/Remote/RemoteTouchScreen.php
+++ b/lib/Remote/RemoteTouchScreen.php
@@ -15,17 +15,12 @@ class RemoteTouchScreen implements WebDriverTouchScreen
*/
private $executor;
- /**
- * @param RemoteExecuteMethod $executor
- */
public function __construct(RemoteExecuteMethod $executor)
{
$this->executor = $executor;
}
/**
- * @param WebDriverElement $element
- *
* @return RemoteTouchScreen The instance.
*/
public function tap(WebDriverElement $element)
@@ -39,8 +34,6 @@ public function tap(WebDriverElement $element)
}
/**
- * @param WebDriverElement $element
- *
* @return RemoteTouchScreen The instance.
*/
public function doubleTap(WebDriverElement $element)
@@ -86,7 +79,6 @@ public function flick($xspeed, $yspeed)
}
/**
- * @param WebDriverElement $element
* @param int $xoffset
* @param int $yoffset
* @param int $speed
@@ -106,8 +98,6 @@ public function flickFromElement(WebDriverElement $element, $xoffset, $yoffset,
}
/**
- * @param WebDriverElement $element
- *
* @return RemoteTouchScreen The instance.
*/
public function longPress(WebDriverElement $element)
@@ -153,7 +143,6 @@ public function scroll($xoffset, $yoffset)
}
/**
- * @param WebDriverElement $element
* @param int $xoffset
* @param int $yoffset
*
diff --git a/lib/Remote/RemoteWebDriver.php b/lib/Remote/RemoteWebDriver.php
index 9c18cb886..3d65aaf0b 100644
--- a/lib/Remote/RemoteWebDriver.php
+++ b/lib/Remote/RemoteWebDriver.php
@@ -2,7 +2,7 @@
namespace Facebook\WebDriver\Remote;
-use Facebook\WebDriver\Exception\UnknownErrorException;
+use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Interactions\WebDriverActions;
use Facebook\WebDriver\JavaScriptExecutor;
use Facebook\WebDriver\Support\IsElementDisplayedAtom;
@@ -24,7 +24,7 @@ class RemoteWebDriver implements WebDriver, JavaScriptExecutor, WebDriverHasInpu
*/
protected $executor;
/**
- * @var WebDriverCapabilities
+ * @var WebDriverCapabilities|null
*/
protected $capabilities;
@@ -54,24 +54,19 @@ class RemoteWebDriver implements WebDriver, JavaScriptExecutor, WebDriverHasInpu
protected $isW3cCompliant;
/**
- * @param HttpCommandExecutor $commandExecutor
* @param string $sessionId
- * @param WebDriverCapabilities|null $capabilities
* @param bool $isW3cCompliant false to use the legacy JsonWire protocol, true for the W3C WebDriver spec
*/
protected function __construct(
HttpCommandExecutor $commandExecutor,
$sessionId,
- WebDriverCapabilities $capabilities = null,
+ WebDriverCapabilities $capabilities,
$isW3cCompliant = false
) {
$this->executor = $commandExecutor;
$this->sessionID = $sessionId;
$this->isW3cCompliant = $isW3cCompliant;
-
- if ($capabilities !== null) {
- $this->capabilities = $capabilities;
- }
+ $this->capabilities = $capabilities;
}
/**
@@ -94,7 +89,7 @@ public static function create(
$request_timeout_in_ms = null,
$http_proxy = null,
$http_proxy_port = null,
- DesiredCapabilities $required_capabilities = null
+ ?DesiredCapabilities $required_capabilities = null
) {
$selenium_server_url = preg_replace('#/+$#', '', $selenium_server_url);
@@ -139,14 +134,22 @@ public static function create(
/**
* [Experimental] Construct the RemoteWebDriver by an existing session.
*
- * This constructor can boost the performance a lot by reusing the same browser for the whole test suite.
- * You cannot pass the desired capabilities because the session was created before.
+ * This constructor can boost the performance by reusing the same browser for the whole test suite. On the other
+ * hand, because the browser is not pristine, this may lead to flaky and dependent tests. So carefully
+ * consider the tradeoffs.
+ *
+ * To create the instance, we need to know Capabilities of the previously created session. You can either
+ * pass them in $existingCapabilities parameter, or we will attempt to receive them from the Selenium Grid server.
+ * However, if Capabilities were not provided and the attempt to get them was not successful,
+ * exception will be thrown.
*
* @param string $session_id The existing session id
* @param string $selenium_server_url The url of the remote Selenium WebDriver server
* @param int|null $connection_timeout_in_ms Set timeout for the connect phase to remote Selenium WebDriver server
* @param int|null $request_timeout_in_ms Set the maximum time of a request to remote Selenium WebDriver server
* @param bool $isW3cCompliant True to use W3C WebDriver (default), false to use the legacy JsonWire protocol
+ * @param WebDriverCapabilities|null $existingCapabilities Provide capabilities of the existing previously created
+ * session. If not provided, we will attempt to read them, but this will only work when using Selenium Grid.
* @return static
*/
public static function createBySessionID(
@@ -157,6 +160,7 @@ public static function createBySessionID(
) {
// BC layer to not break the method signature
$isW3cCompliant = func_num_args() > 4 ? func_get_arg(4) : true;
+ $existingCapabilities = func_num_args() > 5 ? func_get_arg(5) : null;
$executor = new HttpCommandExecutor($selenium_server_url, null, null);
if ($connection_timeout_in_ms !== null) {
@@ -170,7 +174,12 @@ public static function createBySessionID(
$executor->disableW3cCompliance();
}
- return new static($executor, $session_id, null, $isW3cCompliant);
+ // if capabilities were not provided, attempt to read them from the Selenium Grid API
+ if ($existingCapabilities === null) {
+ $existingCapabilities = self::readExistingCapabilitiesFromSeleniumGrid($session_id, $executor);
+ }
+
+ return new static($executor, $session_id, $existingCapabilities, $isW3cCompliant);
}
/**
@@ -200,7 +209,6 @@ public function newWindow()
/**
* Find the first WebDriverElement using the given mechanism.
*
- * @param WebDriverBy $by
* @return RemoteWebElement NoSuchElementException is thrown in HttpCommandExecutor if no element is found.
* @see WebDriverBy
*/
@@ -211,17 +219,12 @@ public function findElement(WebDriverBy $by)
JsonWireCompat::getUsing($by, $this->isW3cCompliant)
);
- if ($raw_element === null) {
- throw new UnknownErrorException('Unexpected server response to findElement command');
- }
-
return $this->newElement(JsonWireCompat::getElement($raw_element));
}
/**
* Find all WebDriverElements within the current page using the given mechanism.
*
- * @param WebDriverBy $by
* @return RemoteWebElement[] A list of all WebDriverElements, or an empty array if nothing matches
* @see WebDriverBy
*/
@@ -232,8 +235,8 @@ public function findElements(WebDriverBy $by)
JsonWireCompat::getUsing($by, $this->isW3cCompliant)
);
- if ($raw_elements === null) {
- throw new UnknownErrorException('Unexpected server response to findElements command');
+ if (!is_array($raw_elements)) {
+ throw UnexpectedResponseException::forError('Server response to findElements command is not an array');
}
$elements = [];
@@ -544,7 +547,7 @@ public function getSessionID()
/**
* Get capabilities of the RemoteWebDriver.
*
- * @return WebDriverCapabilities
+ * @return WebDriverCapabilities|null
*/
public function getCapabilities()
{
@@ -578,7 +581,13 @@ public function execute($command_name, $params = [])
// As we so far only use atom for IS_ELEMENT_DISPLAYED, this condition is hardcoded here. In case more atoms
// are used, this should be rewritten and separated from this class (e.g. to some abstract matcher logic).
if ($command_name === DriverCommand::IS_ELEMENT_DISPLAYED
- && IsElementDisplayedAtom::match($this->getCapabilities()->getBrowserName())) {
+ && (
+ // When capabilities are missing in php-webdriver 1.13.x, always fallback to use the atom
+ $this->getCapabilities() === null
+ // If capabilities are present, use the atom only if condition matches
+ || IsElementDisplayedAtom::match($this->getCapabilities()->getBrowserName())
+ )
+ ) {
return (new IsElementDisplayedAtom($this))->execute($params);
}
@@ -661,7 +670,6 @@ protected static function createFromResponse(WebDriverResponse $response, HttpCo
/**
* Prepare arguments for JavaScript injection
*
- * @param array $arguments
* @return array
*/
protected function prepareScriptArguments(array $arguments)
@@ -727,4 +735,26 @@ protected static function castToDesiredCapabilitiesObject($desired_capabilities
return $desired_capabilities;
}
+
+ protected static function readExistingCapabilitiesFromSeleniumGrid(
+ string $session_id,
+ HttpCommandExecutor $executor
+ ): DesiredCapabilities {
+ $getCapabilitiesCommand = new CustomWebDriverCommand($session_id, '/se/grid/session/:sessionId', 'GET', []);
+
+ try {
+ $capabilitiesResponse = $executor->execute($getCapabilitiesCommand);
+
+ $existingCapabilities = DesiredCapabilities::createFromW3cCapabilities(
+ $capabilitiesResponse->getValue()['capabilities']
+ );
+ if ($existingCapabilities === null) {
+ throw UnexpectedResponseException::forError('Empty capabilities received');
+ }
+ } catch (\Exception $e) {
+ throw UnexpectedResponseException::forCapabilitiesRetrievalError($e);
+ }
+
+ return $existingCapabilities;
+ }
}
diff --git a/lib/Remote/RemoteWebElement.php b/lib/Remote/RemoteWebElement.php
index 07c51e33f..e0ce43b55 100644
--- a/lib/Remote/RemoteWebElement.php
+++ b/lib/Remote/RemoteWebElement.php
@@ -3,8 +3,11 @@
namespace Facebook\WebDriver\Remote;
use Facebook\WebDriver\Exception\ElementNotInteractableException;
+use Facebook\WebDriver\Exception\Internal\IOException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
+use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
+use Facebook\WebDriver\Exception\PhpWebDriverExceptionInterface;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
-use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\Support\ScreenshotHelper;
@@ -38,7 +41,6 @@ class RemoteWebElement implements WebDriverElement, WebDriverLocatable
protected $isW3cCompliant;
/**
- * @param RemoteExecuteMethod $executor
* @param string $id
* @param bool $isW3cCompliant
*/
@@ -53,7 +55,7 @@ public function __construct(RemoteExecuteMethod $executor, $id, $isW3cCompliant
/**
* Clear content editable or resettable element
*
- * @return RemoteWebElement The current instance.
+ * @return $this The current instance.
*/
public function clear()
{
@@ -68,7 +70,7 @@ public function clear()
/**
* Click this element.
*
- * @return RemoteWebElement The current instance.
+ * @return $this The current instance.
*/
public function click()
{
@@ -94,8 +96,7 @@ public function click()
* search the entire document from the root, not just the children (relative context) of this current node.
* Use ".//" to limit your search to the children of this element.
*
- * @param WebDriverBy $by
- * @return RemoteWebElement NoSuchElementException is thrown in HttpCommandExecutor if no element is found.
+ * @return static NoSuchElementException is thrown in HttpCommandExecutor if no element is found.
* @see WebDriverBy
*/
public function findElement(WebDriverBy $by)
@@ -118,8 +119,7 @@ public function findElement(WebDriverBy $by)
* search the entire document from the root, not just the children (relative context) of this current node.
* Use ".//" to limit your search to the children of this element.
*
- * @param WebDriverBy $by
- * @return RemoteWebElement[] A list of all WebDriverElements, or an empty
+ * @return static[] A list of all WebDriverElements, or an empty
* array if nothing matches
* @see WebDriverBy
*/
@@ -132,6 +132,10 @@ public function findElements(WebDriverBy $by)
$params
);
+ if (!is_array($raw_elements)) {
+ throw UnexpectedResponseException::forError('Server response to findChildElements command is not an array');
+ }
+
$elements = [];
foreach ($raw_elements as $raw_element) {
$elements[] = $this->newElement(JsonWireCompat::getElement($raw_element));
@@ -241,11 +245,11 @@ public function getLocationOnScreenOnceScrolledIntoView()
{
if ($this->isW3cCompliant) {
$script = <<executor->execute(DriverCommand::EXECUTE_SCRIPT, [
'script' => $script,
@@ -377,7 +381,7 @@ public function isSelected()
* Simulate typing into an element, which may set its value.
*
* @param mixed $value The data to be typed.
- * @return RemoteWebElement The current instance.
+ * @return static The current instance.
*/
public function sendKeys($value)
{
@@ -408,7 +412,7 @@ public function sendKeys($value)
// This is so far non-W3C compliant method, so it may fail - if so, we just ignore the exception.
// @see https://github.com/w3c/webdriver/issues/1355
$fileName = $this->upload($local_file);
- } catch (WebDriverException $e) {
+ } catch (PhpWebDriverExceptionInterface $e) {
$fileName = $local_file;
}
@@ -439,8 +443,7 @@ public function sendKeys($value)
*
* eg. `$element->setFileDetector(new LocalFileDetector);`
*
- * @param FileDetector $detector
- * @return RemoteWebElement
+ * @return $this
* @see FileDetector
* @see LocalFileDetector
* @see UselessFileDetector
@@ -455,14 +458,14 @@ public function setFileDetector(FileDetector $detector)
/**
* If this current element is a form, or an element within a form, then this will be submitted to the remote server.
*
- * @return RemoteWebElement The current instance.
+ * @return $this The current instance.
*/
public function submit()
{
if ($this->isW3cCompliant) {
// Submit method cannot be called directly in case an input of this form is named "submit".
// We use this polyfill to trigger 'submit' event using form.dispatchEvent().
- $submitPolyfill = $script = <<executor->execute(DriverCommand::EXECUTE_SCRIPT, [
'script' => $submitPolyfill,
'args' => [[JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $this->id]],
@@ -515,7 +518,6 @@ public function takeElementScreenshot($save_as = null)
/**
* Test if two elements IDs refer to the same DOM element.
*
- * @param WebDriverElement $other
* @return bool
*/
public function equals(WebDriverElement $other)
@@ -602,13 +604,13 @@ protected function newElement($id)
*
* @param string $local_file
*
- * @throws WebDriverException
+ * @throws LogicException
* @return string The remote path of the file.
*/
protected function upload($local_file)
{
if (!is_file($local_file)) {
- throw new WebDriverException('You may only upload files: ' . $local_file);
+ throw LogicException::forError('You may only upload files: ' . $local_file);
}
$temp_zip_path = $this->createTemporaryZipArchive($local_file);
@@ -635,7 +637,7 @@ protected function createTemporaryZipArchive($fileToZip)
$zip = new ZipArchive();
if (($errorCode = $zip->open($tempZipPath, ZipArchive::CREATE)) !== true) {
- throw new WebDriverException(sprintf('Error creating zip archive: %s', $errorCode));
+ throw IOException::forFileError(sprintf('Error creating zip archive: %s', $errorCode), $tempZipPath);
}
$info = pathinfo($fileToZip);
diff --git a/lib/Remote/Service/DriverCommandExecutor.php b/lib/Remote/Service/DriverCommandExecutor.php
index d16ec943d..5e3ef8399 100644
--- a/lib/Remote/Service/DriverCommandExecutor.php
+++ b/lib/Remote/Service/DriverCommandExecutor.php
@@ -2,7 +2,7 @@
namespace Facebook\WebDriver\Remote\Service;
-use Facebook\WebDriver\Exception\DriverServerDiedException;
+use Facebook\WebDriver\Exception\Internal\DriverServerDiedException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\HttpCommandExecutor;
@@ -26,8 +26,6 @@ public function __construct(DriverService $service)
}
/**
- * @param WebDriverCommand $command
- *
* @throws \Exception
* @throws WebDriverException
* @return WebDriverResponse
diff --git a/lib/Remote/Service/DriverService.php b/lib/Remote/Service/DriverService.php
index 237ca46b9..028b8cd83 100644
--- a/lib/Remote/Service/DriverService.php
+++ b/lib/Remote/Service/DriverService.php
@@ -2,10 +2,10 @@
namespace Facebook\WebDriver\Remote\Service;
-use Exception;
+use Facebook\WebDriver\Exception\Internal\IOException;
+use Facebook\WebDriver\Exception\Internal\RuntimeException;
use Facebook\WebDriver\Net\URLChecker;
use Symfony\Component\Process\Process;
-use Symfony\Component\Process\ProcessBuilder;
/**
* Start local WebDriver service (when remote WebDriver server is not used).
@@ -123,7 +123,7 @@ protected static function checkExecutable($executable)
/**
* @param string $executable
- * @throws Exception
+ * @throws IOException
*/
protected function setExecutable($executable)
{
@@ -133,12 +133,10 @@ protected function setExecutable($executable)
return;
}
- throw new Exception(
- sprintf(
- '"%s" is not executable. Make sure the path is correct or use environment variable to specify'
- . ' location of the executable.',
- $executable
- )
+ throw IOException::forFileError(
+ 'File is not executable. Make sure the path is correct or use environment variable to specify'
+ . ' location of the executable.',
+ $executable
);
}
@@ -150,33 +148,12 @@ protected function checkWasStarted($process)
usleep(10000); // wait 10ms, otherwise the asynchronous process failure may not yet be propagated
if (!$process->isRunning()) {
- throw new Exception(
- sprintf(
- 'Error starting driver executable "%s": %s',
- $process->getCommandLine(),
- $process->getErrorOutput()
- )
- );
+ throw RuntimeException::forDriverError($process);
}
}
- /**
- * @return Process
- */
- private function createProcess()
+ private function createProcess(): Process
{
- // BC: ProcessBuilder deprecated since Symfony 3.4 and removed in Symfony 4.0.
- if (class_exists(ProcessBuilder::class)
- && mb_strpos('@deprecated', (new \ReflectionClass(ProcessBuilder::class))->getDocComment()) === false
- ) {
- $processBuilder = (new ProcessBuilder())
- ->setPrefix($this->executable)
- ->setArguments($this->args)
- ->addEnvironmentVariables($this->environment);
-
- return $processBuilder->getProcess();
- }
- // Safe to use since Symfony 3.3
$commandLine = array_merge([$this->executable], $this->args);
return new Process($commandLine, null, $this->environment);
@@ -184,11 +161,8 @@ private function createProcess()
/**
* Check whether given file is executable directly or using system PATH
- *
- * @param string $filename
- * @return bool
*/
- private function isExecutable($filename)
+ private function isExecutable(string $filename): bool
{
if (is_executable($filename)) {
return true;
diff --git a/lib/Remote/ShadowRoot.php b/lib/Remote/ShadowRoot.php
index 1ec13a217..5d419b19a 100644
--- a/lib/Remote/ShadowRoot.php
+++ b/lib/Remote/ShadowRoot.php
@@ -2,6 +2,7 @@
namespace Facebook\WebDriver\Remote;
+use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\UnknownErrorException;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverElement;
@@ -14,7 +15,7 @@ class ShadowRoot implements WebDriverSearchContext
*
* @see https://w3c.github.io/webdriver/#shadow-root
*/
- const SHADOW_ROOT_IDENTIFIER = 'shadow-6066-11e4-a52e-4f735466cecf';
+ public const SHADOW_ROOT_IDENTIFIER = 'shadow-6066-11e4-a52e-4f735466cecf';
/**
* @var RemoteExecuteMethod
@@ -33,8 +34,6 @@ public function __construct(RemoteExecuteMethod $executor, $id)
}
/**
- * @param RemoteExecuteMethod $executor
- * @param array $response
* @return self
*/
public static function createFromResponse(RemoteExecuteMethod $executor, array $response)
@@ -47,7 +46,6 @@ public static function createFromResponse(RemoteExecuteMethod $executor, array $
}
/**
- * @param WebDriverBy $locator
* @return RemoteWebElement
*/
public function findElement(WebDriverBy $locator)
@@ -64,7 +62,6 @@ public function findElement(WebDriverBy $locator)
}
/**
- * @param WebDriverBy $locator
* @return WebDriverElement[]
*/
public function findElements(WebDriverBy $locator)
@@ -77,6 +74,12 @@ public function findElements(WebDriverBy $locator)
$params
);
+ if (!is_array($rawElements)) {
+ throw UnexpectedResponseException::forError(
+ 'Server response to findElementsFromShadowRoot command is not an array'
+ );
+ }
+
$elements = [];
foreach ($rawElements as $rawElement) {
$elements[] = new RemoteWebElement($this->executor, JsonWireCompat::getElement($rawElement), true);
diff --git a/lib/Remote/WebDriverBrowserType.php b/lib/Remote/WebDriverBrowserType.php
index 77badd6cc..9e3f4e0ff 100644
--- a/lib/Remote/WebDriverBrowserType.php
+++ b/lib/Remote/WebDriverBrowserType.php
@@ -9,30 +9,30 @@
*/
class WebDriverBrowserType
{
- const FIREFOX = 'firefox';
- const FIREFOX_PROXY = 'firefoxproxy';
- const FIREFOX_CHROME = 'firefoxchrome';
- const GOOGLECHROME = 'googlechrome';
- const SAFARI = 'safari';
- const SAFARI_PROXY = 'safariproxy';
- const OPERA = 'opera';
- const MICROSOFT_EDGE = 'MicrosoftEdge';
- const IEXPLORE = 'iexplore';
- const IEXPLORE_PROXY = 'iexploreproxy';
- const CHROME = 'chrome';
- const KONQUEROR = 'konqueror';
- const MOCK = 'mock';
- const IE_HTA = 'iehta';
- const ANDROID = 'android';
- const HTMLUNIT = 'htmlunit';
- const IE = 'internet explorer';
- const IPHONE = 'iphone';
- const IPAD = 'iPad';
+ public const FIREFOX = 'firefox';
+ public const FIREFOX_PROXY = 'firefoxproxy';
+ public const FIREFOX_CHROME = 'firefoxchrome';
+ public const GOOGLECHROME = 'googlechrome';
+ public const SAFARI = 'safari';
+ public const SAFARI_PROXY = 'safariproxy';
+ public const OPERA = 'opera';
+ public const MICROSOFT_EDGE = 'MicrosoftEdge';
+ public const IEXPLORE = 'iexplore';
+ public const IEXPLORE_PROXY = 'iexploreproxy';
+ public const CHROME = 'chrome';
+ public const KONQUEROR = 'konqueror';
+ public const MOCK = 'mock';
+ public const IE_HTA = 'iehta';
+ public const ANDROID = 'android';
+ public const HTMLUNIT = 'htmlunit';
+ public const IE = 'internet explorer';
+ public const IPHONE = 'iphone';
+ public const IPAD = 'iPad';
/**
* @deprecated PhantomJS is no longer developed and its support will be removed in next major version.
* Use headless Chrome or Firefox instead.
*/
- const PHANTOMJS = 'phantomjs';
+ public const PHANTOMJS = 'phantomjs';
private function __construct()
{
diff --git a/lib/Remote/WebDriverCapabilityType.php b/lib/Remote/WebDriverCapabilityType.php
index 45833339a..ee2538061 100644
--- a/lib/Remote/WebDriverCapabilityType.php
+++ b/lib/Remote/WebDriverCapabilityType.php
@@ -9,22 +9,22 @@
*/
class WebDriverCapabilityType
{
- const BROWSER_NAME = 'browserName';
- const VERSION = 'version';
- const PLATFORM = 'platform';
- const JAVASCRIPT_ENABLED = 'javascriptEnabled';
- const TAKES_SCREENSHOT = 'takesScreenshot';
- const HANDLES_ALERTS = 'handlesAlerts';
- const DATABASE_ENABLED = 'databaseEnabled';
- const LOCATION_CONTEXT_ENABLED = 'locationContextEnabled';
- const APPLICATION_CACHE_ENABLED = 'applicationCacheEnabled';
- const BROWSER_CONNECTION_ENABLED = 'browserConnectionEnabled';
- const CSS_SELECTORS_ENABLED = 'cssSelectorsEnabled';
- const WEB_STORAGE_ENABLED = 'webStorageEnabled';
- const ROTATABLE = 'rotatable';
- const ACCEPT_SSL_CERTS = 'acceptSslCerts';
- const NATIVE_EVENTS = 'nativeEvents';
- const PROXY = 'proxy';
+ public const BROWSER_NAME = 'browserName';
+ public const VERSION = 'version';
+ public const PLATFORM = 'platform';
+ public const JAVASCRIPT_ENABLED = 'javascriptEnabled';
+ public const TAKES_SCREENSHOT = 'takesScreenshot';
+ public const HANDLES_ALERTS = 'handlesAlerts';
+ public const DATABASE_ENABLED = 'databaseEnabled';
+ public const LOCATION_CONTEXT_ENABLED = 'locationContextEnabled';
+ public const APPLICATION_CACHE_ENABLED = 'applicationCacheEnabled';
+ public const BROWSER_CONNECTION_ENABLED = 'browserConnectionEnabled';
+ public const CSS_SELECTORS_ENABLED = 'cssSelectorsEnabled';
+ public const WEB_STORAGE_ENABLED = 'webStorageEnabled';
+ public const ROTATABLE = 'rotatable';
+ public const ACCEPT_SSL_CERTS = 'acceptSslCerts';
+ public const NATIVE_EVENTS = 'nativeEvents';
+ public const PROXY = 'proxy';
private function __construct()
{
diff --git a/lib/Support/Events/EventFiringWebDriver.php b/lib/Support/Events/EventFiringWebDriver.php
index a4a74e346..21e5a6887 100644
--- a/lib/Support/Events/EventFiringWebDriver.php
+++ b/lib/Support/Events/EventFiringWebDriver.php
@@ -26,11 +26,7 @@ class EventFiringWebDriver implements WebDriver, JavaScriptExecutor
*/
protected $dispatcher;
- /**
- * @param WebDriver $driver
- * @param WebDriverDispatcher $dispatcher
- */
- public function __construct(WebDriver $driver, WebDriverDispatcher $dispatcher = null)
+ public function __construct(WebDriver $driver, ?WebDriverDispatcher $dispatcher = null)
{
$this->dispatcher = $dispatcher ?: new WebDriverDispatcher();
if (!$this->dispatcher->getDefaultDriver()) {
@@ -76,7 +72,6 @@ public function get($url)
}
/**
- * @param WebDriverBy $by
* @throws WebDriverException
* @return array
*/
@@ -100,7 +95,6 @@ public function findElements(WebDriverBy $by)
}
/**
- * @param WebDriverBy $by
* @throws WebDriverException
* @return EventFiringWebElement
*/
@@ -122,7 +116,6 @@ public function findElement(WebDriverBy $by)
/**
* @param string $script
- * @param array $arguments
* @throws WebDriverException
* @return mixed
*/
@@ -150,7 +143,6 @@ public function executeScript($script, array $arguments = [])
/**
* @param string $script
- * @param array $arguments
* @throws WebDriverException
* @return mixed
*/
@@ -375,7 +367,6 @@ public function execute($name, $params)
}
/**
- * @param WebDriverElement $element
* @return EventFiringWebElement
*/
protected function newElement(WebDriverElement $element)
@@ -396,9 +387,6 @@ protected function dispatch($method, ...$arguments)
$this->dispatcher->dispatch($method, $arguments);
}
- /**
- * @param WebDriverException $exception
- */
protected function dispatchOnException(WebDriverException $exception)
{
$this->dispatch('onException', $exception, $this);
diff --git a/lib/Support/Events/EventFiringWebDriverNavigation.php b/lib/Support/Events/EventFiringWebDriverNavigation.php
index 5070076d8..27ea34205 100644
--- a/lib/Support/Events/EventFiringWebDriverNavigation.php
+++ b/lib/Support/Events/EventFiringWebDriverNavigation.php
@@ -17,10 +17,6 @@ class EventFiringWebDriverNavigation implements WebDriverNavigationInterface
*/
protected $dispatcher;
- /**
- * @param WebDriverNavigationInterface $navigator
- * @param WebDriverDispatcher $dispatcher
- */
public function __construct(WebDriverNavigationInterface $navigator, WebDriverDispatcher $dispatcher)
{
$this->navigator = $navigator;
@@ -132,9 +128,6 @@ protected function dispatch($method, ...$arguments)
$this->dispatcher->dispatch($method, $arguments);
}
- /**
- * @param WebDriverException $exception
- */
protected function dispatchOnException(WebDriverException $exception)
{
$this->dispatch('onException', $exception);
diff --git a/lib/Support/Events/EventFiringWebElement.php b/lib/Support/Events/EventFiringWebElement.php
index fa18b1a93..6caa08684 100644
--- a/lib/Support/Events/EventFiringWebElement.php
+++ b/lib/Support/Events/EventFiringWebElement.php
@@ -22,10 +22,6 @@ class EventFiringWebElement implements WebDriverElement, WebDriverLocatable
*/
protected $dispatcher;
- /**
- * @param WebDriverElement $element
- * @param WebDriverDispatcher $dispatcher
- */
public function __construct(WebDriverElement $element, WebDriverDispatcher $dispatcher)
{
$this->element = $element;
@@ -88,7 +84,6 @@ public function click()
}
/**
- * @param WebDriverBy $by
* @throws WebDriverException
* @return EventFiringWebElement
*/
@@ -119,7 +114,6 @@ public function findElement(WebDriverBy $by)
}
/**
- * @param WebDriverBy $by
* @throws WebDriverException
* @return array
*/
@@ -355,7 +349,6 @@ public function getID()
/**
* Test if two element IDs refer to the same DOM element.
*
- * @param WebDriverElement $other
* @return bool
*/
public function equals(WebDriverElement $other)
@@ -388,9 +381,6 @@ public function getShadowRoot()
}
}
- /**
- * @param WebDriverException $exception
- */
protected function dispatchOnException(WebDriverException $exception)
{
$this->dispatch(
@@ -414,7 +404,6 @@ protected function dispatch($method, ...$arguments)
}
/**
- * @param WebDriverElement $element
* @return static
*/
protected function newElement(WebDriverElement $element)
diff --git a/lib/Support/IsElementDisplayedAtom.php b/lib/Support/IsElementDisplayedAtom.php
index b27e9cca7..d95e4f01f 100644
--- a/lib/Support/IsElementDisplayedAtom.php
+++ b/lib/Support/IsElementDisplayedAtom.php
@@ -24,7 +24,7 @@ class IsElementDisplayedAtom
*
* @var array
*/
- const BROWSERS_WITH_ENDPOINT_SUPPORT = [
+ public const BROWSERS_WITH_ENDPOINT_SUPPORT = [
WebDriverBrowserType::CHROME,
WebDriverBrowserType::FIREFOX,
WebDriverBrowserType::MICROSOFT_EDGE,
diff --git a/lib/Support/ScreenshotHelper.php b/lib/Support/ScreenshotHelper.php
index 5fe18efd1..956f56147 100644
--- a/lib/Support/ScreenshotHelper.php
+++ b/lib/Support/ScreenshotHelper.php
@@ -2,6 +2,8 @@
namespace Facebook\WebDriver\Support;
+use Facebook\WebDriver\Exception\Internal\IOException;
+use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\RemoteExecuteMethod;
@@ -43,13 +45,15 @@ private function takeScreenshot(array $commandToExecute, $saveAs = null)
$response = $this->executor->execute(...$commandToExecute);
if (!is_string($response)) {
- throw new WebDriverException('Error taking screenshot, no data received from the remote end');
+ throw UnexpectedResponseException::forError(
+ 'Error taking screenshot, no data received from the remote end'
+ );
}
$screenshot = base64_decode($response, true);
if ($screenshot === false) {
- throw new WebDriverException('Error decoding screenshot data');
+ throw UnexpectedResponseException::forError('Error decoding screenshot data');
}
if ($saveAs !== null) {
@@ -70,7 +74,7 @@ private function createDirectoryIfNotExists($directoryPath)
{
if (!file_exists($directoryPath)) {
if (!mkdir($directoryPath, 0777, true) && !is_dir($directoryPath)) {
- throw new WebDriverException(sprintf('Directory "%s" was not created', $directoryPath));
+ throw IOException::forFileError('Directory cannot be not created', $directoryPath);
}
}
}
diff --git a/lib/WebDriverCheckboxes.php b/lib/WebDriverCheckboxes.php
index 5467589c8..abc8cc46c 100644
--- a/lib/WebDriverCheckboxes.php
+++ b/lib/WebDriverCheckboxes.php
@@ -2,7 +2,7 @@
namespace Facebook\WebDriver;
-use Facebook\WebDriver\Exception\WebDriverException;
+use Facebook\WebDriver\Exception\InvalidElementStateException;
/**
* Provides helper methods for checkboxes.
@@ -15,7 +15,7 @@ public function __construct(WebDriverElement $element)
$this->type = $element->getAttribute('type');
if ($this->type !== 'checkbox') {
- throw new WebDriverException('The input must be of type "checkbox".');
+ throw new InvalidElementStateException('The input must be of type "checkbox".');
}
}
diff --git a/lib/WebDriverCommandExecutor.php b/lib/WebDriverCommandExecutor.php
index 4031c235d..7f6bb3ece 100644
--- a/lib/WebDriverCommandExecutor.php
+++ b/lib/WebDriverCommandExecutor.php
@@ -11,8 +11,6 @@
interface WebDriverCommandExecutor
{
/**
- * @param WebDriverCommand $command
- *
* @return WebDriverResponse
*/
public function execute(WebDriverCommand $command);
diff --git a/lib/WebDriverDispatcher.php b/lib/WebDriverDispatcher.php
index 851214719..fe1ecb0f4 100644
--- a/lib/WebDriverDispatcher.php
+++ b/lib/WebDriverDispatcher.php
@@ -19,7 +19,6 @@ class WebDriverDispatcher
* this is needed so that EventFiringWebElement can pass the driver to the
* exception handling
*
- * @param EventFiringWebDriver $driver
* @return $this
*/
public function setDefaultDriver(EventFiringWebDriver $driver)
@@ -38,7 +37,6 @@ public function getDefaultDriver()
}
/**
- * @param WebDriverEventListener $listener
* @return $this
*/
public function register(WebDriverEventListener $listener)
@@ -49,7 +47,6 @@ public function register(WebDriverEventListener $listener)
}
/**
- * @param WebDriverEventListener $listener
* @return $this
*/
public function unregister(WebDriverEventListener $listener)
diff --git a/lib/WebDriverEventListener.php b/lib/WebDriverEventListener.php
index 673d85bee..9ef98d8f3 100644
--- a/lib/WebDriverEventListener.php
+++ b/lib/WebDriverEventListener.php
@@ -10,85 +10,43 @@ interface WebDriverEventListener
{
/**
* @param string $url
- * @param EventFiringWebDriver $driver
*/
public function beforeNavigateTo($url, EventFiringWebDriver $driver);
/**
* @param string $url
- * @param EventFiringWebDriver $driver
*/
public function afterNavigateTo($url, EventFiringWebDriver $driver);
- /**
- * @param EventFiringWebDriver $driver
- */
public function beforeNavigateBack(EventFiringWebDriver $driver);
- /**
- * @param EventFiringWebDriver $driver
- */
public function afterNavigateBack(EventFiringWebDriver $driver);
- /**
- * @param EventFiringWebDriver $driver
- */
public function beforeNavigateForward(EventFiringWebDriver $driver);
- /**
- * @param EventFiringWebDriver $driver
- */
public function afterNavigateForward(EventFiringWebDriver $driver);
- /**
- * @param WebDriverBy $by
- * @param EventFiringWebElement|null $element
- * @param EventFiringWebDriver $driver
- */
public function beforeFindBy(WebDriverBy $by, $element, EventFiringWebDriver $driver);
- /**
- * @param WebDriverBy $by
- * @param EventFiringWebElement|null $element
- * @param EventFiringWebDriver $driver
- */
public function afterFindBy(WebDriverBy $by, $element, EventFiringWebDriver $driver);
/**
* @param string $script
- * @param EventFiringWebDriver $driver
*/
public function beforeScript($script, EventFiringWebDriver $driver);
/**
* @param string $script
- * @param EventFiringWebDriver $driver
*/
public function afterScript($script, EventFiringWebDriver $driver);
- /**
- * @param EventFiringWebElement $element
- */
public function beforeClickOn(EventFiringWebElement $element);
- /**
- * @param EventFiringWebElement $element
- */
public function afterClickOn(EventFiringWebElement $element);
- /**
- * @param EventFiringWebElement $element
- */
public function beforeChangeValueOf(EventFiringWebElement $element);
- /**
- * @param EventFiringWebElement $element
- */
public function afterChangeValueOf(EventFiringWebElement $element);
- /**
- * @param WebDriverException $exception
- * @param EventFiringWebDriver $driver
- */
- public function onException(WebDriverException $exception, EventFiringWebDriver $driver = null);
+ public function onException(WebDriverException $exception, ?EventFiringWebDriver $driver = null);
}
diff --git a/lib/WebDriverExpectedCondition.php b/lib/WebDriverExpectedCondition.php
index 26914a161..188f3c4d8 100644
--- a/lib/WebDriverExpectedCondition.php
+++ b/lib/WebDriverExpectedCondition.php
@@ -2,6 +2,7 @@
namespace Facebook\WebDriver;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\NoSuchAlertException;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\NoSuchFrameException;
@@ -375,9 +376,7 @@ public static function invisibilityOfElementLocated(WebDriverBy $by)
function (WebDriver $driver) use ($by) {
try {
return !$driver->findElement($by)->isDisplayed();
- } catch (NoSuchElementException $e) {
- return true;
- } catch (StaleElementReferenceException $e) {
+ } catch (NoSuchElementException|StaleElementReferenceException $e) {
return true;
}
}
@@ -397,9 +396,7 @@ public static function invisibilityOfElementWithText(WebDriverBy $by, $text)
function (WebDriver $driver) use ($by, $text) {
try {
return !($driver->findElement($by)->getText() === $text);
- } catch (NoSuchElementException $e) {
- return true;
- } catch (StaleElementReferenceException $e) {
+ } catch (NoSuchElementException|StaleElementReferenceException $e) {
return true;
}
}
@@ -526,7 +523,7 @@ function (WebDriver $driver) use ($element_or_by, $selected) {
);
}
- throw new \InvalidArgumentException('Instance of either WebDriverElement or WebDriverBy must be given');
+ throw LogicException::forError('Instance of either WebDriverElement or WebDriverBy must be given');
}
/**
diff --git a/lib/WebDriverKeys.php b/lib/WebDriverKeys.php
index 5bba2eff0..40f3ffbf5 100644
--- a/lib/WebDriverKeys.php
+++ b/lib/WebDriverKeys.php
@@ -9,85 +9,85 @@
*/
class WebDriverKeys
{
- const NULL = "\xEE\x80\x80";
- const CANCEL = "\xEE\x80\x81";
- const HELP = "\xEE\x80\x82";
- const BACKSPACE = "\xEE\x80\x83";
- const TAB = "\xEE\x80\x84";
- const CLEAR = "\xEE\x80\x85";
- const RETURN_KEY = "\xEE\x80\x86";
- const ENTER = "\xEE\x80\x87";
- const SHIFT = "\xEE\x80\x88";
- const CONTROL = "\xEE\x80\x89";
- const ALT = "\xEE\x80\x8A";
- const PAUSE = "\xEE\x80\x8B";
- const ESCAPE = "\xEE\x80\x8C";
- const SPACE = "\xEE\x80\x8D";
- const PAGE_UP = "\xEE\x80\x8E";
- const PAGE_DOWN = "\xEE\x80\x8F";
- const END = "\xEE\x80\x90";
- const HOME = "\xEE\x80\x91";
- const ARROW_LEFT = "\xEE\x80\x92";
- const ARROW_UP = "\xEE\x80\x93";
- const ARROW_RIGHT = "\xEE\x80\x94";
- const ARROW_DOWN = "\xEE\x80\x95";
- const INSERT = "\xEE\x80\x96";
- const DELETE = "\xEE\x80\x97";
- const SEMICOLON = "\xEE\x80\x98";
- const EQUALS = "\xEE\x80\x99";
- const NUMPAD0 = "\xEE\x80\x9A";
- const NUMPAD1 = "\xEE\x80\x9B";
- const NUMPAD2 = "\xEE\x80\x9C";
- const NUMPAD3 = "\xEE\x80\x9D";
- const NUMPAD4 = "\xEE\x80\x9E";
- const NUMPAD5 = "\xEE\x80\x9F";
- const NUMPAD6 = "\xEE\x80\xA0";
- const NUMPAD7 = "\xEE\x80\xA1";
- const NUMPAD8 = "\xEE\x80\xA2";
- const NUMPAD9 = "\xEE\x80\xA3";
- const MULTIPLY = "\xEE\x80\xA4";
- const ADD = "\xEE\x80\xA5";
- const SEPARATOR = "\xEE\x80\xA6";
- const SUBTRACT = "\xEE\x80\xA7";
- const DECIMAL = "\xEE\x80\xA8";
- const DIVIDE = "\xEE\x80\xA9";
- const F1 = "\xEE\x80\xB1";
- const F2 = "\xEE\x80\xB2";
- const F3 = "\xEE\x80\xB3";
- const F4 = "\xEE\x80\xB4";
- const F5 = "\xEE\x80\xB5";
- const F6 = "\xEE\x80\xB6";
- const F7 = "\xEE\x80\xB7";
- const F8 = "\xEE\x80\xB8";
- const F9 = "\xEE\x80\xB9";
- const F10 = "\xEE\x80\xBA";
- const F11 = "\xEE\x80\xBB";
- const F12 = "\xEE\x80\xBC";
- const META = "\xEE\x80\xBD";
- const ZENKAKU_HANKAKU = "\xEE\x80\xC0";
- const RIGHT_SHIFT = "\xEE\x81\x90";
- const RIGHT_CONTROL = "\xEE\x81\x91";
- const RIGHT_ALT = "\xEE\x81\x92";
- const RIGHT_META = "\xEE\x81\x93";
- const NUMPAD_PAGE_UP = "\xEE\x81\x94";
- const NUMPAD_PAGE_DOWN = "\xEE\x81\x95";
- const NUMPAD_END = "\xEE\x81\x96";
- const NUMPAD_HOME = "\xEE\x81\x97";
- const NUMPAD_ARROW_LEFT = "\xEE\x81\x98";
- const NUMPAD_ARROW_UP = "\xEE\x81\x99";
- const NUMPAD_ARROW_RIGHT = "\xEE\x81\x9A";
- const NUMPAD_ARROW_DOWN = "\xEE\x81\x9B";
- const NUMPAD_ARROW_INSERT = "\xEE\x81\x9C";
- const NUMPAD_ARROW_DELETE = "\xEE\x81\x9D";
+ public const NULL = "\xEE\x80\x80";
+ public const CANCEL = "\xEE\x80\x81";
+ public const HELP = "\xEE\x80\x82";
+ public const BACKSPACE = "\xEE\x80\x83";
+ public const TAB = "\xEE\x80\x84";
+ public const CLEAR = "\xEE\x80\x85";
+ public const RETURN_KEY = "\xEE\x80\x86";
+ public const ENTER = "\xEE\x80\x87";
+ public const SHIFT = "\xEE\x80\x88";
+ public const CONTROL = "\xEE\x80\x89";
+ public const ALT = "\xEE\x80\x8A";
+ public const PAUSE = "\xEE\x80\x8B";
+ public const ESCAPE = "\xEE\x80\x8C";
+ public const SPACE = "\xEE\x80\x8D";
+ public const PAGE_UP = "\xEE\x80\x8E";
+ public const PAGE_DOWN = "\xEE\x80\x8F";
+ public const END = "\xEE\x80\x90";
+ public const HOME = "\xEE\x80\x91";
+ public const ARROW_LEFT = "\xEE\x80\x92";
+ public const ARROW_UP = "\xEE\x80\x93";
+ public const ARROW_RIGHT = "\xEE\x80\x94";
+ public const ARROW_DOWN = "\xEE\x80\x95";
+ public const INSERT = "\xEE\x80\x96";
+ public const DELETE = "\xEE\x80\x97";
+ public const SEMICOLON = "\xEE\x80\x98";
+ public const EQUALS = "\xEE\x80\x99";
+ public const NUMPAD0 = "\xEE\x80\x9A";
+ public const NUMPAD1 = "\xEE\x80\x9B";
+ public const NUMPAD2 = "\xEE\x80\x9C";
+ public const NUMPAD3 = "\xEE\x80\x9D";
+ public const NUMPAD4 = "\xEE\x80\x9E";
+ public const NUMPAD5 = "\xEE\x80\x9F";
+ public const NUMPAD6 = "\xEE\x80\xA0";
+ public const NUMPAD7 = "\xEE\x80\xA1";
+ public const NUMPAD8 = "\xEE\x80\xA2";
+ public const NUMPAD9 = "\xEE\x80\xA3";
+ public const MULTIPLY = "\xEE\x80\xA4";
+ public const ADD = "\xEE\x80\xA5";
+ public const SEPARATOR = "\xEE\x80\xA6";
+ public const SUBTRACT = "\xEE\x80\xA7";
+ public const DECIMAL = "\xEE\x80\xA8";
+ public const DIVIDE = "\xEE\x80\xA9";
+ public const F1 = "\xEE\x80\xB1";
+ public const F2 = "\xEE\x80\xB2";
+ public const F3 = "\xEE\x80\xB3";
+ public const F4 = "\xEE\x80\xB4";
+ public const F5 = "\xEE\x80\xB5";
+ public const F6 = "\xEE\x80\xB6";
+ public const F7 = "\xEE\x80\xB7";
+ public const F8 = "\xEE\x80\xB8";
+ public const F9 = "\xEE\x80\xB9";
+ public const F10 = "\xEE\x80\xBA";
+ public const F11 = "\xEE\x80\xBB";
+ public const F12 = "\xEE\x80\xBC";
+ public const META = "\xEE\x80\xBD";
+ public const ZENKAKU_HANKAKU = "\xEE\x80\xC0";
+ public const RIGHT_SHIFT = "\xEE\x81\x90";
+ public const RIGHT_CONTROL = "\xEE\x81\x91";
+ public const RIGHT_ALT = "\xEE\x81\x92";
+ public const RIGHT_META = "\xEE\x81\x93";
+ public const NUMPAD_PAGE_UP = "\xEE\x81\x94";
+ public const NUMPAD_PAGE_DOWN = "\xEE\x81\x95";
+ public const NUMPAD_END = "\xEE\x81\x96";
+ public const NUMPAD_HOME = "\xEE\x81\x97";
+ public const NUMPAD_ARROW_LEFT = "\xEE\x81\x98";
+ public const NUMPAD_ARROW_UP = "\xEE\x81\x99";
+ public const NUMPAD_ARROW_RIGHT = "\xEE\x81\x9A";
+ public const NUMPAD_ARROW_DOWN = "\xEE\x81\x9B";
+ public const NUMPAD_ARROW_INSERT = "\xEE\x81\x9C";
+ public const NUMPAD_ARROW_DELETE = "\xEE\x81\x9D";
// Aliases
- const LEFT_SHIFT = self::SHIFT;
- const LEFT_CONTROL = self::CONTROL;
- const LEFT_ALT = self::ALT;
- const LEFT = self::ARROW_LEFT;
- const UP = self::ARROW_UP;
- const RIGHT = self::ARROW_RIGHT;
- const DOWN = self::ARROW_DOWN;
- const COMMAND = self::META;
+ public const LEFT_SHIFT = self::SHIFT;
+ public const LEFT_CONTROL = self::CONTROL;
+ public const LEFT_ALT = self::ALT;
+ public const LEFT = self::ARROW_LEFT;
+ public const UP = self::ARROW_UP;
+ public const RIGHT = self::ARROW_RIGHT;
+ public const DOWN = self::ARROW_DOWN;
+ public const COMMAND = self::META;
/**
* Encode input of `sendKeys()` to appropriate format according to protocol.
diff --git a/lib/WebDriverMouse.php b/lib/WebDriverMouse.php
index 5178a314b..7c9362329 100644
--- a/lib/WebDriverMouse.php
+++ b/lib/WebDriverMouse.php
@@ -10,31 +10,26 @@
interface WebDriverMouse
{
/**
- * @param WebDriverCoordinates $where
* @return WebDriverMouse
*/
public function click(WebDriverCoordinates $where);
/**
- * @param WebDriverCoordinates $where
* @return WebDriverMouse
*/
public function contextClick(WebDriverCoordinates $where);
/**
- * @param WebDriverCoordinates $where
* @return WebDriverMouse
*/
public function doubleClick(WebDriverCoordinates $where);
/**
- * @param WebDriverCoordinates $where
* @return WebDriverMouse
*/
public function mouseDown(WebDriverCoordinates $where);
/**
- * @param WebDriverCoordinates $where
* @param int $x_offset
* @param int $y_offset
* @return WebDriverMouse
@@ -46,7 +41,6 @@ public function mouseMove(
);
/**
- * @param WebDriverCoordinates $where
* @return WebDriverMouse
*/
public function mouseUp(WebDriverCoordinates $where);
diff --git a/lib/WebDriverOptions.php b/lib/WebDriverOptions.php
index 84f2270eb..c84757fe8 100644
--- a/lib/WebDriverOptions.php
+++ b/lib/WebDriverOptions.php
@@ -2,10 +2,10 @@
namespace Facebook\WebDriver;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\NoSuchCookieException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;
-use InvalidArgumentException;
/**
* Managing stuff you would do in a browser.
@@ -40,7 +40,7 @@ public function addCookie($cookie)
$cookie = Cookie::createFromArray($cookie);
}
if (!$cookie instanceof Cookie) {
- throw new InvalidArgumentException('Cookie must be set from instance of Cookie class or from array.');
+ throw LogicException::forError('Cookie must be set from instance of Cookie class or from array.');
}
$this->executor->execute(
diff --git a/lib/WebDriverPlatform.php b/lib/WebDriverPlatform.php
index d7194e05e..a589f3013 100644
--- a/lib/WebDriverPlatform.php
+++ b/lib/WebDriverPlatform.php
@@ -9,15 +9,15 @@
*/
class WebDriverPlatform
{
- const ANDROID = 'ANDROID';
+ public const ANDROID = 'ANDROID';
/** @deprecated ANY has no meaning in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/731 */
- const ANY = 'ANY';
- const LINUX = 'LINUX';
- const MAC = 'MAC';
- const UNIX = 'UNIX';
- const VISTA = 'VISTA';
- const WINDOWS = 'WINDOWS';
- const XP = 'XP';
+ public const ANY = 'ANY';
+ public const LINUX = 'LINUX';
+ public const MAC = 'MAC';
+ public const UNIX = 'UNIX';
+ public const VISTA = 'VISTA';
+ public const WINDOWS = 'WINDOWS';
+ public const XP = 'XP';
private function __construct()
{
diff --git a/lib/WebDriverRadios.php b/lib/WebDriverRadios.php
index e1687983e..aeaaaecac 100644
--- a/lib/WebDriverRadios.php
+++ b/lib/WebDriverRadios.php
@@ -2,8 +2,8 @@
namespace Facebook\WebDriver;
+use Facebook\WebDriver\Exception\InvalidElementStateException;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
-use Facebook\WebDriver\Exception\WebDriverException;
/**
* Provides helper methods for radio buttons.
@@ -16,7 +16,7 @@ public function __construct(WebDriverElement $element)
$this->type = $element->getAttribute('type');
if ($this->type !== 'radio') {
- throw new WebDriverException('The input must be of type "radio".');
+ throw new InvalidElementStateException('The input must be of type "radio".');
}
}
diff --git a/lib/WebDriverSearchContext.php b/lib/WebDriverSearchContext.php
index 82a5bde8d..5fb1daaf9 100644
--- a/lib/WebDriverSearchContext.php
+++ b/lib/WebDriverSearchContext.php
@@ -2,6 +2,8 @@
namespace Facebook\WebDriver;
+use Facebook\WebDriver\Exception\NoSuchElementException;
+
/**
* The interface for WebDriver and WebDriverElement which is able to search for WebDriverElement inside.
*/
@@ -10,8 +12,8 @@ interface WebDriverSearchContext
/**
* Find the first WebDriverElement within this element using the given mechanism.
*
- * @param WebDriverBy $locator
- * @return WebDriverElement NoSuchElementException is thrown in HttpCommandExecutor if no element is found.
+ * @throws NoSuchElementException If no element is found
+ * @return WebDriverElement
* @see WebDriverBy
*/
public function findElement(WebDriverBy $locator);
@@ -19,7 +21,6 @@ public function findElement(WebDriverBy $locator);
/**
* Find all WebDriverElements within this element using the given mechanism.
*
- * @param WebDriverBy $locator
* @return WebDriverElement[] A list of all WebDriverElements, or an empty array if nothing matches
* @see WebDriverBy
*/
diff --git a/lib/WebDriverSelect.php b/lib/WebDriverSelect.php
index 53d894b96..85e1d26e4 100644
--- a/lib/WebDriverSelect.php
+++ b/lib/WebDriverSelect.php
@@ -26,7 +26,14 @@ public function __construct(WebDriverElement $element)
}
$this->element = $element;
$value = $element->getAttribute('multiple');
- $this->isMulti = $value === 'true';
+
+ /**
+ * There is a bug in safari webdriver that returns 'multiple' instead of 'true' which does not match the spec.
+ * Apple Feedback #FB12760673
+ *
+ * @see https://www.w3.org/TR/webdriver2/#get-element-attribute
+ */
+ $this->isMulti = $value === 'true' || $value === 'multiple';
}
public function isMultiple()
@@ -223,7 +230,6 @@ public function deselectByVisiblePartialText($text)
/**
* Mark option selected
- * @param WebDriverElement $option
*/
protected function selectOption(WebDriverElement $option)
{
@@ -234,7 +240,6 @@ protected function selectOption(WebDriverElement $option)
/**
* Mark option not selected
- * @param WebDriverElement $option
*/
protected function deselectOption(WebDriverElement $option)
{
diff --git a/lib/WebDriverTargetLocator.php b/lib/WebDriverTargetLocator.php
index 08096bcab..8787f66c5 100644
--- a/lib/WebDriverTargetLocator.php
+++ b/lib/WebDriverTargetLocator.php
@@ -8,9 +8,9 @@
interface WebDriverTargetLocator
{
/** @var string */
- const WINDOW_TYPE_WINDOW = 'window';
+ public const WINDOW_TYPE_WINDOW = 'window';
/** @var string */
- const WINDOW_TYPE_TAB = 'tab';
+ public const WINDOW_TYPE_TAB = 'tab';
/**
* Set the current browsing context to the current top-level browsing context.
diff --git a/lib/WebDriverTimeouts.php b/lib/WebDriverTimeouts.php
index add6a0900..73a5cddf8 100644
--- a/lib/WebDriverTimeouts.php
+++ b/lib/WebDriverTimeouts.php
@@ -28,7 +28,7 @@ public function __construct(ExecuteMethod $executor, $isW3cCompliant = false)
/**
* Specify the amount of time the driver should wait when searching for an element if it is not immediately present.
*
- * @param int $seconds Wait time in second.
+ * @param null|int|float $seconds Wait time in seconds.
* @return WebDriverTimeouts The current instance.
*/
public function implicitlyWait($seconds)
@@ -36,15 +36,18 @@ public function implicitlyWait($seconds)
if ($this->isW3cCompliant) {
$this->executor->execute(
DriverCommand::IMPLICITLY_WAIT,
- ['implicit' => $seconds * 1000]
+ ['implicit' => $seconds === null ? null : floor($seconds * 1000)]
);
return $this;
}
+ if ($seconds === null) {
+ throw new \InvalidArgumentException('JsonWire Protocol implicit-wait-timeout value cannot be null');
+ }
$this->executor->execute(
DriverCommand::IMPLICITLY_WAIT,
- ['ms' => $seconds * 1000]
+ ['ms' => floor($seconds * 1000)]
);
return $this;
@@ -53,7 +56,7 @@ public function implicitlyWait($seconds)
/**
* Set the amount of time to wait for an asynchronous script to finish execution before throwing an error.
*
- * @param int $seconds Wait time in second.
+ * @param null|int|float $seconds Wait time in seconds.
* @return WebDriverTimeouts The current instance.
*/
public function setScriptTimeout($seconds)
@@ -61,15 +64,18 @@ public function setScriptTimeout($seconds)
if ($this->isW3cCompliant) {
$this->executor->execute(
DriverCommand::SET_SCRIPT_TIMEOUT,
- ['script' => $seconds * 1000]
+ ['script' => $seconds === null ? null : floor($seconds * 1000)]
);
return $this;
}
+ if ($seconds === null) {
+ throw new \InvalidArgumentException('JsonWire Protocol script-timeout value cannot be null');
+ }
$this->executor->execute(
DriverCommand::SET_SCRIPT_TIMEOUT,
- ['ms' => $seconds * 1000]
+ ['ms' => floor($seconds * 1000)]
);
return $this;
@@ -78,7 +84,7 @@ public function setScriptTimeout($seconds)
/**
* Set the amount of time to wait for a page load to complete before throwing an error.
*
- * @param int $seconds Wait time in second.
+ * @param null|int|float $seconds Wait time in seconds.
* @return WebDriverTimeouts The current instance.
*/
public function pageLoadTimeout($seconds)
@@ -86,15 +92,18 @@ public function pageLoadTimeout($seconds)
if ($this->isW3cCompliant) {
$this->executor->execute(
DriverCommand::SET_SCRIPT_TIMEOUT,
- ['pageLoad' => $seconds * 1000]
+ ['pageLoad' => $seconds === null ? null : floor($seconds * 1000)]
);
return $this;
}
+ if ($seconds === null) {
+ throw new \InvalidArgumentException('JsonWire Protocol page-load-timeout value cannot be null');
+ }
$this->executor->execute(DriverCommand::SET_TIMEOUT, [
'type' => 'page load',
- 'ms' => $seconds * 1000,
+ 'ms' => floor($seconds * 1000),
]);
return $this;
diff --git a/lib/WebDriverUpAction.php b/lib/WebDriverUpAction.php
index 62d275dea..3b045df4b 100644
--- a/lib/WebDriverUpAction.php
+++ b/lib/WebDriverUpAction.php
@@ -11,7 +11,6 @@ class WebDriverUpAction extends WebDriverTouchAction implements WebDriverAction
private $y;
/**
- * @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
diff --git a/lib/WebDriverWait.php b/lib/WebDriverWait.php
index 5b2009b3c..d2176b92a 100644
--- a/lib/WebDriverWait.php
+++ b/lib/WebDriverWait.php
@@ -28,7 +28,7 @@ class WebDriverWait
public function __construct(WebDriver $driver, $timeout_in_second = null, $interval_in_millisecond = null)
{
$this->driver = $driver;
- $this->timeout = isset($timeout_in_second) ? $timeout_in_second : 30;
+ $this->timeout = $timeout_in_second ?? 30;
$this->interval = $interval_in_millisecond ?: 250;
}
diff --git a/lib/WebDriverWindow.php b/lib/WebDriverWindow.php
index 57754eb76..2a69fe240 100644
--- a/lib/WebDriverWindow.php
+++ b/lib/WebDriverWindow.php
@@ -3,6 +3,7 @@
namespace Facebook\WebDriver;
use Facebook\WebDriver\Exception\IndexOutOfBoundsException;
+use Facebook\WebDriver\Exception\Internal\LogicException;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\ExecuteMethod;
@@ -120,7 +121,6 @@ public function fullscreen()
* Set the size of the current window. This will change the outer window
* dimension, not just the view port.
*
- * @param WebDriverDimension $size
* @return WebDriverWindow The instance.
*/
public function setSize(WebDriverDimension $size)
@@ -139,7 +139,6 @@ public function setSize(WebDriverDimension $size)
* Set the position of the current window. This is relative to the upper left
* corner of the screen.
*
- * @param WebDriverPoint $position
* @return WebDriverWindow The instance.
*/
public function setPosition(WebDriverPoint $position)
@@ -176,9 +175,7 @@ public function setScreenOrientation($orientation)
{
$orientation = mb_strtoupper($orientation);
if (!in_array($orientation, ['PORTRAIT', 'LANDSCAPE'], true)) {
- throw new IndexOutOfBoundsException(
- 'Orientation must be either PORTRAIT, or LANDSCAPE'
- );
+ throw LogicException::forError('Orientation must be either PORTRAIT, or LANDSCAPE');
}
$this->executor->execute(
diff --git a/mlc_config.json b/mlc_config.json
new file mode 100644
index 000000000..5d70a03e0
--- /dev/null
+++ b/mlc_config.json
@@ -0,0 +1,7 @@
+{
+ "ignorePatterns": [
+ {
+ "pattern": "^https://stackoverflow\\.com/questions/tagged/php\\+selenium-webdriver"
+ }
+ ]
+}
diff --git a/phpstan.neon b/phpstan.neon
index 747193ddf..b4c1e307d 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -23,5 +23,7 @@ parameters:
# Parameter is intentionally not part of signature to not break BC
- message: '#PHPDoc tag \@param references unknown parameter: \$isW3cCompliant#'
path: 'lib/Remote/RemoteWebDriver.php'
+ - message: '#PHPDoc tag \@param references unknown parameter: \$existingCapabilities#'
+ path: 'lib/Remote/RemoteWebDriver.php'
inferPrivatePropertyTypeFromConstructor: true
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 2e81aef6d..c58c72eaa 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,14 +1,9 @@
-
-
-
-
+
tests/unit
@@ -18,14 +13,13 @@
-
-
+
+
./lib
-
-
+
+
-
diff --git a/scripts/apply-phpunit-patches.sh b/scripts/apply-phpunit-patches.sh
deleted file mode 100755
index 478e01c74..000000000
--- a/scripts/apply-phpunit-patches.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-
-# All commands below must no fail
-set -e
-
-# Be in the root dir
-cd "$(dirname $0)/../"
-
-find tests/ -type f -print0 | xargs -0 sed -i 's/function setUpBeforeClass(): void/function setUpBeforeClass()/g';
-find tests/ -type f -print0 | xargs -0 sed -i 's/function setUp(): void/function setUp()/g';
-find tests/ -type f -print0 | xargs -0 sed -i 's/function tearDown(): void/function tearDown()/g';
-
-sed -i 's/endTest(\\PHPUnit\\Framework\\Test \$test, float \$time): void/endTest(\\PHPUnit_Framework_Test \$test, \$time)/g' tests/functional/ReportSauceLabsStatusListener.php;
-sed -i 's/: void/ /g' tests/functional/ReportSauceLabsStatusListener.php;
-# Drop the listener from the config file
-sed -i '//,+2d' phpunit.xml.dist;
-sed -i 's/function runBare(): void/function runBare()/g' tests/functional/WebDriverTestCase.php;
-
-# Return back to original dir
-cd - > /dev/null
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 6c8c4f51b..d9cb0c2c7 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,3 +1,3 @@
-driver);
@@ -31,7 +31,7 @@ public function testShouldExecuteDevToolsCommandWithoutParameters()
$this->assertSame([], $result);
}
- public function testShouldExecuteDevToolsCommandWithParameters()
+ public function testShouldExecuteDevToolsCommandWithParameters(): void
{
$devTools = new ChromeDevToolsDriver($this->driver);
diff --git a/tests/functional/Chrome/ChromeDriverServiceTest.php b/tests/functional/Chrome/ChromeDriverServiceTest.php
index d83ca6af4..9b6c46336 100644
--- a/tests/functional/Chrome/ChromeDriverServiceTest.php
+++ b/tests/functional/Chrome/ChromeDriverServiceTest.php
@@ -1,4 +1,4 @@
-assertInstanceOf(ChromeDriverService::class, $this->driverService->stop());
}
- public function testShouldStartAndStopServiceCreatedUsingDefaultConstructor()
+ public function testShouldStartAndStopServiceCreatedUsingDefaultConstructor(): void
{
$this->driverService = new ChromeDriverService(getenv('CHROMEDRIVER_PATH'), 9515, ['--port=9515']);
@@ -63,7 +63,7 @@ public function testShouldStartAndStopServiceCreatedUsingDefaultConstructor()
$this->assertFalse($this->driverService->isRunning());
}
- public function testShouldThrowExceptionIfExecutableIsNotExecutable()
+ public function testShouldThrowExceptionIfExecutableIsNotExecutable(): void
{
putenv(ChromeDriverService::CHROME_DRIVER_EXECUTABLE . '=' . __FILE__);
@@ -72,7 +72,7 @@ public function testShouldThrowExceptionIfExecutableIsNotExecutable()
ChromeDriverService::createDefaultService();
}
- public function testShouldUseDefaultExecutableIfNoneProvided()
+ public function testShouldUseDefaultExecutableIfNoneProvided(): void
{
// Put path where ChromeDriver binary is actually located to system PATH, to make sure we can locate it
putenv('PATH=' . getenv('PATH') . ':' . dirname(getenv('CHROMEDRIVER_PATH')));
diff --git a/tests/functional/Chrome/ChromeDriverTest.php b/tests/functional/Chrome/ChromeDriverTest.php
index 695fa57e3..407b65bd8 100644
--- a/tests/functional/Chrome/ChromeDriverTest.php
+++ b/tests/functional/Chrome/ChromeDriverTest.php
@@ -1,4 +1,4 @@
-startChromeDriver($isW3cDialect);
$this->assertInstanceOf(ChromeDriver::class, $this->driver);
@@ -55,7 +54,7 @@ public function testShouldStartChromeDriver($isW3cDialect)
/**
* @return array[]
*/
- public function provideDialect()
+ public function provideDialect(): array
{
return [
'w3c' => [true],
@@ -63,7 +62,7 @@ public function provideDialect()
];
}
- public function testShouldInstantiateDevTools()
+ public function testShouldInstantiateDevTools(): void
{
$this->startChromeDriver();
@@ -81,14 +80,14 @@ public function testShouldInstantiateDevTools()
$this->assertSame(['result' => ['type' => 'string', 'value' => 'http://localhost:8000/']], $cdpResult);
}
- private function startChromeDriver($w3cDialect = true)
+ private function startChromeDriver($w3cDialect = true): void
{
// The createDefaultService() method expect path to the executable to be present in the environment variable
putenv(ChromeDriverService::CHROME_DRIVER_EXECUTABLE . '=' . getenv('CHROMEDRIVER_PATH'));
// Add --no-sandbox as a workaround for Chrome crashing: https://github.com/SeleniumHQ/selenium/issues/4961
$chromeOptions = new ChromeOptions();
- $chromeOptions->addArguments(['--no-sandbox', '--headless']);
+ $chromeOptions->addArguments(['--no-sandbox', '--headless', '--disable-search-engine-choice-screen']);
$chromeOptions->setExperimentalOption('w3c', $w3cDialect);
$desiredCapabilities = DesiredCapabilities::chrome();
$desiredCapabilities->setCapability(ChromeOptions::CAPABILITY, $chromeOptions);
diff --git a/tests/functional/FileUploadTest.php b/tests/functional/FileUploadTest.php
index 4ca5e1e60..e812353ea 100644
--- a/tests/functional/FileUploadTest.php
+++ b/tests/functional/FileUploadTest.php
@@ -1,4 +1,4 @@
-driver->get($this->getTestPageUrl(TestPage::UPLOAD));
@@ -43,7 +45,7 @@ public function testShouldUploadAFile()
$this->assertSame('10', $uploadedFileSize);
}
- private function getTestFilePath()
+ private function getTestFilePath(): string
{
return __DIR__ . '/Fixtures/FileUploadTestFile.txt';
}
diff --git a/tests/functional/Firefox/FirefoxDriverServiceTest.php b/tests/functional/Firefox/FirefoxDriverServiceTest.php
index 190286847..feabe0dbf 100644
--- a/tests/functional/Firefox/FirefoxDriverServiceTest.php
+++ b/tests/functional/Firefox/FirefoxDriverServiceTest.php
@@ -1,4 +1,4 @@
-assertInstanceOf(FirefoxDriverService::class, $this->driverService->stop());
}
- public function testShouldStartAndStopServiceCreatedUsingDefaultConstructor()
+ public function testShouldStartAndStopServiceCreatedUsingDefaultConstructor(): void
{
$this->driverService = new FirefoxDriverService(getenv('GECKODRIVER_PATH'), 9515, ['-p=9515']);
@@ -65,7 +65,7 @@ public function testShouldStartAndStopServiceCreatedUsingDefaultConstructor()
$this->assertFalse($this->driverService->isRunning());
}
- public function testShouldUseDefaultExecutableIfNoneProvided()
+ public function testShouldUseDefaultExecutableIfNoneProvided(): void
{
// Put path where geckodriver binary is actually located to system PATH, to make sure we can locate it
putenv('PATH=' . getenv('PATH') . ':' . dirname(getenv('GECKODRIVER_PATH')));
diff --git a/tests/functional/Firefox/FirefoxDriverTest.php b/tests/functional/Firefox/FirefoxDriverTest.php
index 7c7b91370..900746d2f 100644
--- a/tests/functional/Firefox/FirefoxDriverTest.php
+++ b/tests/functional/Firefox/FirefoxDriverTest.php
@@ -1,4 +1,4 @@
-startFirefoxDriver();
$this->assertInstanceOf(FirefoxDriver::class, $this->driver);
@@ -53,7 +53,7 @@ public function testShouldStartFirefoxDriver()
$this->assertSame('http://localhost:8000/', $this->driver->getCurrentURL());
}
- public function testShouldSetPreferenceWithFirefoxOptions()
+ public function testShouldSetPreferenceWithFirefoxOptions(): void
{
$firefoxOptions = new FirefoxOptions();
$firefoxOptions->setPreference('javascript.enabled', false);
@@ -69,7 +69,7 @@ public function testShouldSetPreferenceWithFirefoxOptions()
);
}
- private function startFirefoxDriver(FirefoxOptions $firefoxOptions = null)
+ private function startFirefoxDriver(?FirefoxOptions $firefoxOptions = null): void
{
// The createDefaultService() method expect path to the executable to be present in the environment variable
putenv(FirefoxDriverService::WEBDRIVER_FIREFOX_DRIVER . '=' . getenv('GECKODRIVER_PATH'));
diff --git a/tests/functional/Firefox/FirefoxProfileTest.php b/tests/functional/Firefox/FirefoxProfileTest.php
index 4e16dd096..a8ff2c62d 100644
--- a/tests/functional/Firefox/FirefoxProfileTest.php
+++ b/tests/functional/Firefox/FirefoxProfileTest.php
@@ -1,17 +1,4 @@
-startFirefoxDriverWithProfile($firefoxProfile);
@@ -76,7 +64,7 @@ public function testShouldStartDriverWithEmptyProfile()
);
}
- public function testShouldInstallExtension()
+ public function testShouldInstallExtension(): void
{
$firefoxProfile = new FirefoxProfile();
$firefoxProfile->addExtension($this->firefoxTestExtensionFilename);
@@ -86,11 +74,15 @@ public function testShouldInstallExtension()
$this->assertInstanceOf(RemoteWebDriver::class, $this->driver);
- $element = $this->driver->findElement(WebDriverBy::id('webDriverExtensionTest'));
+ // it sometimes takes split of a second for the extension to render the element, so we must use wait
+ $element = $this->driver->wait(5, 1)->until(
+ WebDriverExpectedCondition::presenceOfElementLocated(WebDriverBy::id('webDriverExtensionTest'))
+ );
+
$this->assertEquals('This element was added by browser extension', $element->getText());
}
- public function testShouldUseProfilePreferences()
+ public function testShouldUseProfilePreferences(): void
{
$firefoxProfile = new FirefoxProfile();
@@ -110,7 +102,7 @@ public function testShouldUseProfilePreferences()
);
}
- protected function getTestPageUrl($path)
+ protected function getTestPageUrl($path): string
{
$host = 'http://localhost:8000/';
if ($alternateHost = getenv('FIXTURES_HOST')) {
@@ -120,7 +112,7 @@ protected function getTestPageUrl($path)
return $host . '/' . $path;
}
- private function startFirefoxDriverWithProfile(FirefoxProfile $firefoxProfile)
+ private function startFirefoxDriverWithProfile(FirefoxProfile $firefoxProfile): void
{
// The createDefaultService() method expect path to the executable to be present in the environment variable
putenv(FirefoxDriverService::WEBDRIVER_FIREFOX_DRIVER . '=' . getenv('GECKODRIVER_PATH'));
diff --git a/tests/functional/Remote/JsonWireCompatTest.php b/tests/functional/Remote/JsonWireCompatTest.php
new file mode 100644
index 000000000..8dfb5e29a
--- /dev/null
+++ b/tests/functional/Remote/JsonWireCompatTest.php
@@ -0,0 +1,17 @@
+expectException(UnexpectedResponseException::class);
+ $this->expectExceptionMessage('Unexpected server response for getting an element. Expected array');
+
+ JsonWireCompat::getElement(null);
+ }
+}
diff --git a/tests/functional/RemoteKeyboardTest.php b/tests/functional/RemoteKeyboardTest.php
index dfab3a687..897d33cf7 100644
--- a/tests/functional/RemoteKeyboardTest.php
+++ b/tests/functional/RemoteKeyboardTest.php
@@ -1,4 +1,4 @@
-driver->get($this->getTestPageUrl(TestPage::EVENTS));
diff --git a/tests/functional/RemoteTargetLocatorTest.php b/tests/functional/RemoteTargetLocatorTest.php
index f3a098be4..a3080e67a 100644
--- a/tests/functional/RemoteTargetLocatorTest.php
+++ b/tests/functional/RemoteTargetLocatorTest.php
@@ -1,7 +1,8 @@
-driver->get($this->getTestPageUrl(TestPage::OPEN_NEW_WINDOW));
$originalWindowHandle = $this->driver->getWindowHandle();
@@ -23,7 +24,7 @@ public function testShouldSwitchToWindow()
);
// At first the window should not be switched
- $this->compatAssertStringContainsString('open_new_window.html', $this->driver->getCurrentURL());
+ $this->assertStringContainsString('open_new_window.html', $this->driver->getCurrentURL());
$this->assertSame($originalWindowHandle, $this->driver->getWindowHandle());
/**
@@ -43,11 +44,11 @@ public function testShouldSwitchToWindow()
});
// After switchTo() is called, the active window should be changed
- $this->compatAssertStringContainsString('index.html', $this->driver->getCurrentURL());
+ $this->assertStringContainsString('index.html', $this->driver->getCurrentURL());
$this->assertNotSame($originalWindowHandle, $this->driver->getWindowHandle());
}
- public function testActiveElement()
+ public function testActiveElement(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -61,7 +62,7 @@ public function testActiveElement()
$this->assertSame('test_name', $activeElement->getAttribute('name'));
}
- public function testShouldSwitchToFrameByItsId()
+ public function testShouldSwitchToFrameByItsId(): void
{
$parentPage = 'This is the host page which contains an iFrame';
$firstChildFrame = 'This is the content of the iFrame';
@@ -69,57 +70,57 @@ public function testShouldSwitchToFrameByItsId()
$this->driver->get($this->getTestPageUrl(TestPage::PAGE_WITH_FRAME));
- $this->compatAssertStringContainsString($parentPage, $this->driver->getPageSource());
+ $this->assertStringContainsString($parentPage, $this->driver->getPageSource());
$this->driver->switchTo()->frame(0);
- $this->compatAssertStringContainsString($firstChildFrame, $this->driver->getPageSource());
+ $this->assertStringContainsString($firstChildFrame, $this->driver->getPageSource());
$this->driver->switchTo()->frame(null);
- $this->compatAssertStringContainsString($parentPage, $this->driver->getPageSource());
+ $this->assertStringContainsString($parentPage, $this->driver->getPageSource());
$this->driver->switchTo()->frame(1);
- $this->compatAssertStringContainsString($secondChildFrame, $this->driver->getPageSource());
+ $this->assertStringContainsString($secondChildFrame, $this->driver->getPageSource());
$this->driver->switchTo()->frame(null);
- $this->compatAssertStringContainsString($parentPage, $this->driver->getPageSource());
+ $this->assertStringContainsString($parentPage, $this->driver->getPageSource());
$this->driver->switchTo()->frame(0);
- $this->compatAssertStringContainsString($firstChildFrame, $this->driver->getPageSource());
+ $this->assertStringContainsString($firstChildFrame, $this->driver->getPageSource());
$this->driver->switchTo()->defaultContent();
- $this->compatAssertStringContainsString($parentPage, $this->driver->getPageSource());
+ $this->assertStringContainsString($parentPage, $this->driver->getPageSource());
}
- public function testShouldSwitchToParentFrame()
+ public function testShouldSwitchToParentFrame(): void
{
$parentPage = 'This is the host page which contains an iFrame';
$firstChildFrame = 'This is the content of the iFrame';
$this->driver->get($this->getTestPageUrl(TestPage::PAGE_WITH_FRAME));
- $this->compatAssertStringContainsString($parentPage, $this->driver->getPageSource());
+ $this->assertStringContainsString($parentPage, $this->driver->getPageSource());
$this->driver->switchTo()->frame(0);
- $this->compatAssertStringContainsString($firstChildFrame, $this->driver->getPageSource());
+ $this->assertStringContainsString($firstChildFrame, $this->driver->getPageSource());
$this->driver->switchTo()->parent();
- $this->compatAssertStringContainsString($parentPage, $this->driver->getPageSource());
+ $this->assertStringContainsString($parentPage, $this->driver->getPageSource());
}
- public function testShouldSwitchToFrameByElement()
+ public function testShouldSwitchToFrameByElement(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::PAGE_WITH_FRAME));
$element = $this->driver->findElement(WebDriverBy::id('iframe_content'));
$this->driver->switchTo()->frame($element);
- $this->compatAssertStringContainsString('This is the content of the iFrame', $this->driver->getPageSource());
+ $this->assertStringContainsString('This is the content of the iFrame', $this->driver->getPageSource());
}
/**
* @group exclude-saucelabs
*/
- public function testShouldCreateNewWindow()
+ public function testShouldCreateNewWindow(): void
{
self::skipForJsonWireProtocol('Create new window is not supported in JsonWire protocol');
@@ -128,8 +129,8 @@ public function testShouldCreateNewWindow()
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$this->assertEquals($this->getTestPageUrl(TestPage::INDEX), $this->driver->getCurrentUrl());
$source = $this->driver->getPageSource();
- $this->compatAssertStringContainsString('', $source);
- $this->compatAssertStringContainsString('Welcome to the php-webdriver testing page.', $source);
+ $this->assertStringContainsString('', $source);
+ $this->assertStringContainsString('Welcome to the php-webdriver testing page.', $source);
$windowHandles = $this->driver->getWindowHandles();
$this->assertCount(1, $windowHandles);
@@ -152,13 +153,13 @@ public function testShouldCreateNewWindow()
/**
* @group exclude-saucelabs
*/
- public function testShouldNotAcceptStringAsFrameIdInW3cMode()
+ public function testShouldNotAcceptStringAsFrameIdInW3cMode(): void
{
self::skipForJsonWireProtocol();
$this->driver->get($this->getTestPageUrl(TestPage::PAGE_WITH_FRAME));
- $this->expectException(\InvalidArgumentException::class);
+ $this->expectException(LogicException::class);
$this->expectExceptionMessage(
'In W3C compliance mode frame must be either instance of WebDriverElement, integer or null'
);
@@ -169,7 +170,7 @@ public function testShouldNotAcceptStringAsFrameIdInW3cMode()
/**
* @group exclude-saucelabs
*/
- public function testShouldAcceptStringAsFrameIdInJsonWireMode()
+ public function testShouldAcceptStringAsFrameIdInJsonWireMode(): void
{
self::skipForW3cProtocol();
@@ -177,6 +178,6 @@ public function testShouldAcceptStringAsFrameIdInJsonWireMode()
$this->driver->switchTo()->frame('iframe_content');
- $this->compatAssertStringContainsString('This is the content of the iFrame', $this->driver->getPageSource());
+ $this->assertStringContainsString('This is the content of the iFrame', $this->driver->getPageSource());
}
}
diff --git a/tests/functional/RemoteWebDriverCreateTest.php b/tests/functional/RemoteWebDriverCreateTest.php
index 3de4b2c93..a39a91070 100644
--- a/tests/functional/RemoteWebDriverCreateTest.php
+++ b/tests/functional/RemoteWebDriverCreateTest.php
@@ -1,19 +1,21 @@
-driver = RemoteWebDriver::create(
$this->serverUrl,
@@ -30,7 +32,7 @@ public function testShouldStartBrowserAndCreateInstanceOfRemoteWebDriver()
$this->assertInstanceOf(HttpCommandExecutor::class, $this->driver->getCommandExecutor());
$this->assertNotEmpty($this->driver->getCommandExecutor()->getAddressOfRemoteServer());
- $this->assertTrue(is_string($this->driver->getSessionID()));
+ $this->assertIsString($this->driver->getSessionID());
$this->assertNotEmpty($this->driver->getSessionID());
$returnedCapabilities = $this->driver->getCapabilities();
@@ -49,7 +51,7 @@ public function testShouldStartBrowserAndCreateInstanceOfRemoteWebDriver()
$this->assertNotEmpty($returnedCapabilities->getVersion());
}
- public function testShouldAcceptCapabilitiesAsAnArray()
+ public function testShouldAcceptCapabilitiesAsAnArray(): void
{
// Method has a side-effect of converting whole content of desiredCapabilities to an array
$this->desiredCapabilities->toArray();
@@ -64,7 +66,7 @@ public function testShouldAcceptCapabilitiesAsAnArray()
$this->assertNotNull($this->driver->getCapabilities());
}
- public function testShouldCreateWebDriverWithRequiredCapabilities()
+ public function testShouldCreateWebDriverWithRequiredCapabilities(): void
{
$requiredCapabilities = new DesiredCapabilities();
@@ -87,7 +89,7 @@ public function testShouldCreateWebDriverWithRequiredCapabilities()
* However the the browser driver must be able to create non-headless instance (eg. inside xvfb).
* @group exclude-saucelabs
*/
- public function testShouldCreateWebDriverWithoutCapabilities()
+ public function testShouldCreateWebDriverWithoutCapabilities(): void
{
if (getenv('GECKODRIVER') !== '1' && empty(getenv('CHROMEDRIVER_PATH'))) {
$this->markTestSkipped('This test makes sense only when run directly via specific browser driver');
@@ -99,7 +101,7 @@ public function testShouldCreateWebDriverWithoutCapabilities()
$this->assertNotEmpty($this->driver->getSessionID());
}
- public function testShouldCreateInstanceFromExistingSessionId()
+ public function testShouldCreateInstanceFromExistingSessionId(): void
{
// Create driver instance and load page "index.html"
$originalDriver = RemoteWebDriver::create(
@@ -109,23 +111,65 @@ public function testShouldCreateInstanceFromExistingSessionId()
$this->requestTimeout
);
$originalDriver->get($this->getTestPageUrl(TestPage::INDEX));
- $this->compatAssertStringContainsString('/index.html', $originalDriver->getCurrentURL());
+ $this->assertStringContainsString('/index.html', $originalDriver->getCurrentURL());
- // Store session ID
+ // Store session attributes
$sessionId = $originalDriver->getSessionID();
$isW3cCompliant = $originalDriver->isW3cCompliant();
+ $originalCapabilities = $originalDriver->getCapabilities();
+
+ $capabilitiesForSessionReuse = $originalCapabilities;
+ if ($this->isSeleniumServerUsed()) {
+ // do not provide capabilities when selenium server is used, to test they are read from selenium server
+ $capabilitiesForSessionReuse = null;
+ }
// Create new RemoteWebDriver instance based on the session ID
- $this->driver = RemoteWebDriver::createBySessionID($sessionId, $this->serverUrl, null, null, $isW3cCompliant);
+ $this->driver = RemoteWebDriver::createBySessionID(
+ $sessionId,
+ $this->serverUrl,
+ null,
+ null,
+ $isW3cCompliant,
+ $capabilitiesForSessionReuse
+ );
+
+ // Capabilities should be retrieved and be set to the driver instance
+ $returnedCapabilities = $this->driver->getCapabilities();
+ $this->assertInstanceOf(WebDriverCapabilities::class, $returnedCapabilities);
+
+ $expectedBrowserName = $this->desiredCapabilities->getBrowserName();
+
+ $this->assertEqualsIgnoringCase(
+ $expectedBrowserName,
+ $returnedCapabilities->getBrowserName()
+ );
+ $this->assertEqualsCanonicalizing($originalCapabilities, $this->driver->getCapabilities());
// Check we reused the previous instance (window) and it has the same URL
- $this->compatAssertStringContainsString('/index.html', $this->driver->getCurrentURL());
+ $this->assertStringContainsString('/index.html', $this->driver->getCurrentURL());
// Do some interaction with the new driver
$this->assertNotEmpty($this->driver->findElement(WebDriverBy::id('id_test'))->getText());
}
- protected function createWebDriver()
+ public function testShouldRequireCapabilitiesToBeSetToReuseExistingSession(): void
+ {
+ $this->expectException(UnexpectedResponseException::class);
+ $this->expectExceptionMessage(
+ 'Existing Capabilities were not provided, and they also cannot be read from Selenium Grid'
+ );
+
+ // Do not provide capabilities, they also cannot be retrieved from the Selenium Grid
+ RemoteWebDriver::createBySessionID(
+ 'sessionId',
+ 'http://localhost:332/', // nothing should be running there
+ null,
+ null
+ );
+ }
+
+ protected function createWebDriver(): void
{
}
}
diff --git a/tests/functional/RemoteWebDriverFindElementTest.php b/tests/functional/RemoteWebDriverFindElementTest.php
index 5887b4fba..f638aa527 100644
--- a/tests/functional/RemoteWebDriverFindElementTest.php
+++ b/tests/functional/RemoteWebDriverFindElementTest.php
@@ -1,4 +1,4 @@
-driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -19,7 +19,7 @@ public function testShouldThrowExceptionIfElementCannotBeFound()
$this->driver->findElement(WebDriverBy::id('not_existing'));
}
- public function testShouldFindElementIfExistsOnAPage()
+ public function testShouldFindElementIfExistsOnAPage(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -28,23 +28,23 @@ public function testShouldFindElementIfExistsOnAPage()
$this->assertInstanceOf(RemoteWebElement::class, $element);
}
- public function testShouldReturnEmptyArrayIfElementsCannotBeFound()
+ public function testShouldReturnEmptyArrayIfElementsCannotBeFound(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$elements = $this->driver->findElements(WebDriverBy::cssSelector('not_existing'));
- $this->assertTrue(is_array($elements));
+ $this->assertIsArray($elements);
$this->assertCount(0, $elements);
}
- public function testShouldFindMultipleElements()
+ public function testShouldFindMultipleElements(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$elements = $this->driver->findElements(WebDriverBy::cssSelector('ul > li'));
- $this->assertTrue(is_array($elements));
+ $this->assertIsArray($elements);
$this->assertCount(5, $elements);
$this->assertContainsOnlyInstancesOf(RemoteWebElement::class, $elements);
}
@@ -52,7 +52,7 @@ public function testShouldFindMultipleElements()
/**
* @group exclude-saucelabs
*/
- public function testEscapeCssSelector()
+ public function testEscapeCssSelector(): void
{
self::skipForJsonWireProtocol(
'CSS selectors containing special characters are not supported by the legacy protocol'
diff --git a/tests/functional/RemoteWebDriverTest.php b/tests/functional/RemoteWebDriverTest.php
index 81dbbd953..95a01b9b7 100644
--- a/tests/functional/RemoteWebDriverTest.php
+++ b/tests/functional/RemoteWebDriverTest.php
@@ -1,4 +1,4 @@
-driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -27,7 +27,7 @@ public function testShouldGetPageTitle()
* @covers ::get
* @covers ::getCurrentURL
*/
- public function testShouldGetCurrentUrl()
+ public function testShouldGetCurrentUrl(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -37,20 +37,20 @@ public function testShouldGetCurrentUrl()
/**
* @covers ::getPageSource
*/
- public function testShouldGetPageSource()
+ public function testShouldGetPageSource(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$source = $this->driver->getPageSource();
- $this->compatAssertStringContainsString('', $source);
- $this->compatAssertStringContainsString('Welcome to the php-webdriver testing page.', $source);
+ $this->assertStringContainsString('', $source);
+ $this->assertStringContainsString('Welcome to the php-webdriver testing page.', $source);
}
/**
* @covers ::getSessionID
* @covers ::isW3cCompliant
*/
- public function testShouldGetSessionId()
+ public function testShouldGetSessionId(): void
{
// This tests is intentionally included in another test, to not slow down build.
// @TODO Remove following in 2.0
@@ -62,7 +62,7 @@ public function testShouldGetSessionId()
$sessionId = $this->driver->getSessionID();
- $this->assertTrue(is_string($sessionId));
+ $this->assertIsString($sessionId);
$this->assertNotEmpty($sessionId);
}
@@ -70,13 +70,13 @@ public function testShouldGetSessionId()
* @group exclude-saucelabs
* @covers ::getAllSessions
*/
- public function testShouldGetAllSessions()
+ public function testShouldGetAllSessions(): void
{
self::skipForW3cProtocol();
$sessions = RemoteWebDriver::getAllSessions($this->serverUrl, 30000);
- $this->assertTrue(is_array($sessions));
+ $this->assertIsArray($sessions);
$this->assertCount(1, $sessions);
$this->assertArrayHasKey('capabilities', $sessions[0]);
@@ -89,7 +89,7 @@ public function testShouldGetAllSessions()
* @covers ::getCommandExecutor
* @covers ::quit
*/
- public function testShouldQuitAndUnsetExecutor()
+ public function testShouldQuitAndUnsetExecutor(): void
{
self::skipForW3cProtocol();
@@ -116,14 +116,14 @@ public function testShouldQuitAndUnsetExecutor()
* @covers ::getWindowHandle
* @covers ::getWindowHandles
*/
- public function testShouldGetWindowHandles()
+ public function testShouldGetWindowHandles(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::OPEN_NEW_WINDOW));
$windowHandle = $this->driver->getWindowHandle();
$windowHandles = $this->driver->getWindowHandles();
- $this->assertTrue(is_string($windowHandle));
+ $this->assertIsString($windowHandle);
$this->assertNotEmpty($windowHandle);
$this->assertSame([$windowHandle], $windowHandles);
@@ -140,7 +140,7 @@ public function testShouldGetWindowHandles()
/**
* @covers ::close
*/
- public function testShouldCloseWindow()
+ public function testShouldCloseWindow(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::OPEN_NEW_WINDOW));
$this->driver->findElement(WebDriverBy::cssSelector('a'))->click();
@@ -158,7 +158,7 @@ public function testShouldCloseWindow()
* @covers ::executeScript
* @group exclude-saucelabs
*/
- public function testShouldExecuteScriptAndDoNotBlockExecution()
+ public function testShouldExecuteScriptAndDoNotBlockExecution(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -188,7 +188,7 @@ function(){document.getElementById("id_test").innerHTML = "Text changed by scrip
* @covers ::executeAsyncScript
* @covers \Facebook\WebDriver\WebDriverTimeouts::setScriptTimeout
*/
- public function testShouldExecuteAsyncScriptAndWaitUntilItIsFinished()
+ public function testShouldExecuteAsyncScriptAndWaitUntilItIsFinished(): void
{
$this->driver->manage()->timeouts()->setScriptTimeout(1);
@@ -228,7 +228,7 @@ function(){
* @covers ::prepareScriptArguments
* @group exclude-saucelabs
*/
- public function testShouldExecuteScriptWithParamsAndReturnValue()
+ public function testShouldExecuteScriptWithParamsAndReturnValue(): void
{
$this->driver->manage()->timeouts()->setScriptTimeout(1);
@@ -252,7 +252,7 @@ public function testShouldExecuteScriptWithParamsAndReturnValue()
* @covers ::takeScreenshot
* @covers \Facebook\WebDriver\Support\ScreenshotHelper
*/
- public function testShouldTakeScreenshot()
+ public function testShouldTakeScreenshot(): void
{
if (!extension_loaded('gd')) {
$this->markTestSkipped('GD extension must be enabled');
@@ -274,7 +274,7 @@ public function testShouldTakeScreenshot()
* @group exclude-safari
* Safari is returning different color profile and it does not have way to configure "force-color-profile"
*/
- public function testShouldSaveScreenshotToFile()
+ public function testShouldSaveScreenshotToFile(): void
{
if (!extension_loaded('gd')) {
$this->markTestSkipped('GD extension must be enabled');
@@ -314,12 +314,12 @@ public function testShouldSaveScreenshotToFile()
* @group exclude-saucelabs
* Status endpoint is not supported on Sauce Labs
*/
- public function testShouldGetRemoteEndStatus()
+ public function testShouldGetRemoteEndStatus(): void
{
$status = $this->driver->getStatus();
- $this->assertTrue(is_bool($status->isReady()));
- $this->assertTrue(is_array($status->getMeta()));
+ $this->assertIsBool($status->isReady());
+ $this->assertIsArray($status->getMeta());
if (getenv('BROWSER_NAME') !== 'safari') {
$this->assertNotEmpty($status->getMessage());
diff --git a/tests/functional/RemoteWebElementTest.php b/tests/functional/RemoteWebElementTest.php
index b8e9f1cf8..6a21cb36c 100644
--- a/tests/functional/RemoteWebElementTest.php
+++ b/tests/functional/RemoteWebElementTest.php
@@ -1,4 +1,4 @@
-driver->get($this->getTestPageUrl(TestPage::INDEX));
$elementWithSimpleText = $this->driver->findElement(WebDriverBy::id('text-simple'));
@@ -33,7 +33,7 @@ public function testShouldGetText()
/**
* @covers ::getAttribute
*/
- public function testShouldGetAttributeValue()
+ public function testShouldGetAttributeValue(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -48,7 +48,7 @@ public function testShouldGetAttributeValue()
/**
* @covers ::getDomProperty
*/
- public function testShouldGetDomPropertyValue()
+ public function testShouldGetDomPropertyValue(): void
{
self::skipForJsonWireProtocol();
@@ -70,7 +70,7 @@ public function testShouldGetDomPropertyValue()
/**
* @covers ::getLocation
*/
- public function testShouldGetLocation()
+ public function testShouldGetLocation(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -85,7 +85,7 @@ public function testShouldGetLocation()
/**
* @covers ::getLocationOnScreenOnceScrolledIntoView
*/
- public function testShouldGetLocationOnScreenOnceScrolledIntoView()
+ public function testShouldGetLocationOnScreenOnceScrolledIntoView(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -110,7 +110,7 @@ public function testShouldGetLocationOnScreenOnceScrolledIntoView()
/**
* @covers ::getSize
*/
- public function testShouldGetSize()
+ public function testShouldGetSize(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -125,7 +125,7 @@ public function testShouldGetSize()
/**
* @covers ::getCSSValue
*/
- public function testShouldGetCssValue()
+ public function testShouldGetCssValue(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -145,7 +145,7 @@ public function testShouldGetCssValue()
/**
* @covers ::getTagName
*/
- public function testShouldGetTagName()
+ public function testShouldGetTagName(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -157,7 +157,7 @@ public function testShouldGetTagName()
/**
* @covers ::click
*/
- public function testShouldClick()
+ public function testShouldClick(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$linkElement = $this->driver->findElement(WebDriverBy::id('a-form'));
@@ -180,7 +180,7 @@ public function testShouldClick()
* @group exclude-chrome
* @group exclude-edge
*/
- public function testGeckoDriverShouldClickOnBlockLevelElement()
+ public function testGeckoDriverShouldClickOnBlockLevelElement(): void
{
self::skipForUnmatchedBrowsers(['firefox']);
@@ -209,7 +209,7 @@ public function testGeckoDriverShouldClickOnBlockLevelElement()
* @group exclude-chrome
* @group exclude-edge
*/
- public function testGeckoDriverShouldClickNotInteractable()
+ public function testGeckoDriverShouldClickNotInteractable(): void
{
self::skipForUnmatchedBrowsers(['firefox']);
@@ -237,7 +237,7 @@ public function testGeckoDriverShouldClickNotInteractable()
/**
* @covers ::clear
*/
- public function testShouldClearFormElementText()
+ public function testShouldClearFormElementText(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -256,7 +256,7 @@ public function testShouldClearFormElementText()
/**
* @covers ::sendKeys
*/
- public function testShouldSendKeysToFormElement()
+ public function testShouldSendKeysToFormElement(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -294,7 +294,7 @@ public function testShouldSendKeysToFormElement()
* @covers \Facebook\WebDriver\Remote\RemoteWebDriver::execute
* @covers \Facebook\WebDriver\Support\IsElementDisplayedAtom
*/
- public function testShouldDetectElementDisplayedness()
+ public function testShouldDetectElementDisplayedness(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -310,7 +310,7 @@ public function testShouldDetectElementDisplayedness()
/**
* @covers ::isEnabled
*/
- public function testShouldDetectEnabledInputs()
+ public function testShouldDetectEnabledInputs(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -324,7 +324,7 @@ public function testShouldDetectEnabledInputs()
/**
* @covers ::isSelected
*/
- public function testShouldSelectedInputsOrOptions()
+ public function testShouldSelectedInputsOrOptions(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -352,7 +352,7 @@ public function testShouldSelectedInputsOrOptions()
* @covers ::submit
* @group exclude-edge
*/
- public function testShouldSubmitFormBySubmitEventOnForm()
+ public function testShouldSubmitFormBySubmitEventOnForm(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -370,7 +370,7 @@ public function testShouldSubmitFormBySubmitEventOnForm()
/**
* @covers ::submit
*/
- public function testShouldSubmitFormBySubmitEventOnFormInputElement()
+ public function testShouldSubmitFormBySubmitEventOnFormInputElement(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -388,7 +388,7 @@ public function testShouldSubmitFormBySubmitEventOnFormInputElement()
/**
* @covers ::click
*/
- public function testShouldSubmitFormByClickOnSubmitInput()
+ public function testShouldSubmitFormByClickOnSubmitInput(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::FORM));
@@ -406,7 +406,7 @@ public function testShouldSubmitFormByClickOnSubmitInput()
/**
* @covers ::equals
*/
- public function testShouldCompareEqualsElement()
+ public function testShouldCompareEqualsElement(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
@@ -425,7 +425,7 @@ public function testShouldCompareEqualsElement()
/**
* @covers ::findElement
*/
- public function testShouldThrowExceptionIfChildElementCannotBeFound()
+ public function testShouldThrowExceptionIfChildElementCannotBeFound(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$element = $this->driver->findElement(WebDriverBy::cssSelector('ul.list'));
@@ -434,7 +434,7 @@ public function testShouldThrowExceptionIfChildElementCannotBeFound()
$element->findElement(WebDriverBy::id('not_existing'));
}
- public function testShouldFindChildElementIfExistsOnAPage()
+ public function testShouldFindChildElementIfExistsOnAPage(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$element = $this->driver->findElement(WebDriverBy::cssSelector('ul.list'));
@@ -446,18 +446,18 @@ public function testShouldFindChildElementIfExistsOnAPage()
$this->assertSame('First', $childElement->getText());
}
- public function testShouldReturnEmptyArrayIfChildElementsCannotBeFound()
+ public function testShouldReturnEmptyArrayIfChildElementsCannotBeFound(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$element = $this->driver->findElement(WebDriverBy::cssSelector('ul.list'));
$childElements = $element->findElements(WebDriverBy::cssSelector('not_existing'));
- $this->assertTrue(is_array($childElements));
+ $this->assertIsArray($childElements);
$this->assertCount(0, $childElements);
}
- public function testShouldFindMultipleChildElements()
+ public function testShouldFindMultipleChildElements(): void
{
$this->driver->get($this->getTestPageUrl(TestPage::INDEX));
$element = $this->driver->findElement(WebDriverBy::cssSelector('ul.list'));
@@ -465,7 +465,7 @@ public function testShouldFindMultipleChildElements()
$allElements = $this->driver->findElements(WebDriverBy::cssSelector('li'));
$childElements = $element->findElements(WebDriverBy::cssSelector('li'));
- $this->assertTrue(is_array($childElements));
+ $this->assertIsArray($childElements);
$this->assertCount(5, $allElements); // there should be 5 elements on page
$this->assertCount(3, $childElements); // but we should find only subelements of one