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 - -
- -
-``` - -### 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(); + +
+ +
+ + 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 `' => ['#select'], @@ -70,7 +81,7 @@ public function provideSelectSelector() ]; } - public function testShouldDefaultSelectedOptionOfSimpleSelect() + public function testShouldDefaultSelectedOptionOfSimpleSelect(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -85,7 +96,7 @@ public function testShouldDefaultSelectedOptionOfSimpleSelect() $this->assertSame('First', $firstSelectedOption->getText()); } - public function testShouldReturnEmptyArrayIfNoOptionsOfMultipleSelectSelected() + public function testShouldReturnEmptyArrayIfNoOptionsOfMultipleSelectSelected(): void { $select = $this->getWebDriverSelectForMultipleSelect(); @@ -94,7 +105,7 @@ public function testShouldReturnEmptyArrayIfNoOptionsOfMultipleSelectSelected() $this->assertSame([], $selectedOptions); } - public function testShouldThrowExceptionIfThereIsNoFirstSelectedOptionOfMultipleSelect() + public function testShouldThrowExceptionIfThereIsNoFirstSelectedOptionOfMultipleSelect(): void { $select = $this->getWebDriverSelectForMultipleSelect(); @@ -103,7 +114,7 @@ public function testShouldThrowExceptionIfThereIsNoFirstSelectedOptionOfMultiple $select->getFirstSelectedOption(); } - public function testShouldSelectOptionOfSimpleSelectByIndex() + public function testShouldSelectOptionOfSimpleSelectByIndex(): void { $select = $this->getWebDriverSelectForSimpleSelect(); $this->assertSame('first', $select->getFirstSelectedOption()->getAttribute('value')); @@ -120,7 +131,7 @@ public function testShouldSelectOptionOfSimpleSelectByIndex() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldSelectOptionOfMultipleSelectByIndex() + public function testShouldSelectOptionOfMultipleSelectByIndex(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $this->assertSame([], $select->getAllSelectedOptions()); @@ -138,7 +149,7 @@ public function testShouldSelectOptionOfMultipleSelectByIndex() $this->assertContainsOptionsWithValues(['second', 'fourth', 'fifth'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfThereIsNoOptionIndexToSelect() + public function testShouldThrowExceptionIfThereIsNoOptionIndexToSelect(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -147,7 +158,7 @@ public function testShouldThrowExceptionIfThereIsNoOptionIndexToSelect() $select->selectByIndex(1337); } - public function testShouldSelectOptionOfSimpleSelectByValue() + public function testShouldSelectOptionOfSimpleSelectByValue(): void { $select = $this->getWebDriverSelectForSimpleSelect(); $this->assertSame('first', $select->getFirstSelectedOption()->getAttribute('value')); @@ -164,7 +175,7 @@ public function testShouldSelectOptionOfSimpleSelectByValue() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldSelectOptionOfMultipleSelectByValue() + public function testShouldSelectOptionOfMultipleSelectByValue(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $this->assertSame([], $select->getAllSelectedOptions()); @@ -182,7 +193,7 @@ public function testShouldSelectOptionOfMultipleSelectByValue() $this->assertContainsOptionsWithValues(['second', 'fourth', 'fifth'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfThereIsNoOptionValueToSelect() + public function testShouldThrowExceptionIfThereIsNoOptionValueToSelect(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -191,7 +202,7 @@ public function testShouldThrowExceptionIfThereIsNoOptionValueToSelect() $select->selectByValue(1337); } - public function testShouldSelectOptionOfSimpleSelectByVisibleText() + public function testShouldSelectOptionOfSimpleSelectByVisibleText(): void { $select = $this->getWebDriverSelectForSimpleSelect(); $this->assertSame('first', $select->getFirstSelectedOption()->getAttribute('value')); @@ -208,7 +219,7 @@ public function testShouldSelectOptionOfSimpleSelectByVisibleText() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldSelectOptionOfMultipleSelectByVisibleText() + public function testShouldSelectOptionOfMultipleSelectByVisibleText(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $this->assertSame([], $select->getAllSelectedOptions()); @@ -226,7 +237,7 @@ public function testShouldSelectOptionOfMultipleSelectByVisibleText() $this->assertContainsOptionsWithValues(['second', 'fourth', 'fifth'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfThereIsNoOptionVisibleTextToSelect() + public function testShouldThrowExceptionIfThereIsNoOptionVisibleTextToSelect(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -235,7 +246,7 @@ public function testShouldThrowExceptionIfThereIsNoOptionVisibleTextToSelect() $select->selectByVisibleText('second'); // the option is "This is second option" } - public function testShouldSelectOptionOfSimpleSelectByVisiblePartialText() + public function testShouldSelectOptionOfSimpleSelectByVisiblePartialText(): void { $select = $this->getWebDriverSelectForSimpleSelect(); $this->assertSame('first', $select->getFirstSelectedOption()->getAttribute('value')); @@ -252,7 +263,7 @@ public function testShouldSelectOptionOfSimpleSelectByVisiblePartialText() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldSelectOptionOfMultipleSelectByVisiblePartialText() + public function testShouldSelectOptionOfMultipleSelectByVisiblePartialText(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $this->assertSame([], $select->getAllSelectedOptions()); @@ -273,7 +284,7 @@ public function testShouldSelectOptionOfMultipleSelectByVisiblePartialText() ); } - public function testShouldThrowExceptionIfThereIsNoOptionVisiblePartialTextToSelect() + public function testShouldThrowExceptionIfThereIsNoOptionVisiblePartialTextToSelect(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -282,7 +293,7 @@ public function testShouldThrowExceptionIfThereIsNoOptionVisiblePartialTextToSel $select->selectByVisiblePartialText('Not existing option'); } - public function testShouldThrowExceptionWhenDeselectingOnSimpleSelect() + public function testShouldThrowExceptionWhenDeselectingOnSimpleSelect(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -295,7 +306,7 @@ public function testShouldThrowExceptionWhenDeselectingOnSimpleSelect() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldDeselectAllOptionsOnMultipleSelect() + public function testShouldDeselectAllOptionsOnMultipleSelect(): void { $select = $this->getWebDriverSelectForMultipleSelect(); @@ -313,7 +324,7 @@ public function testShouldDeselectAllOptionsOnMultipleSelect() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldDeselectOptionOnMultipleSelectByIndex() + public function testShouldDeselectOptionOnMultipleSelectByIndex(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $select->selectByValue('fourth'); // index 3 @@ -326,7 +337,7 @@ public function testShouldDeselectOptionOnMultipleSelectByIndex() $this->assertContainsOptionsWithValues(['second'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfDeselectingSimpleSelectByIndex() + public function testShouldThrowExceptionIfDeselectingSimpleSelectByIndex(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -339,7 +350,7 @@ public function testShouldThrowExceptionIfDeselectingSimpleSelectByIndex() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldDeselectOptionOnMultipleSelectByValue() + public function testShouldDeselectOptionOnMultipleSelectByValue(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $select->selectByValue('third'); @@ -352,7 +363,7 @@ public function testShouldDeselectOptionOnMultipleSelectByValue() $this->assertContainsOptionsWithValues(['first'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfDeselectingSimpleSelectByValue() + public function testShouldThrowExceptionIfDeselectingSimpleSelectByValue(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -365,7 +376,7 @@ public function testShouldThrowExceptionIfDeselectingSimpleSelectByValue() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldDeselectOptionOnMultipleSelectByVisibleText() + public function testShouldDeselectOptionOnMultipleSelectByVisibleText(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $select->selectByValue('fourth'); // text 'Fourth with spaces inside' @@ -380,7 +391,7 @@ public function testShouldDeselectOptionOnMultipleSelectByVisibleText() $this->assertContainsOptionsWithValues(['second'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfDeselectingSimpleSelectByVisibleText() + public function testShouldThrowExceptionIfDeselectingSimpleSelectByVisibleText(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -393,7 +404,7 @@ public function testShouldThrowExceptionIfDeselectingSimpleSelectByVisibleText() * @group exclude-edge * https://connect.microsoft.com/IE/feedback/details/2020772/-microsoft-edge-webdriver-cannot-select-multiple-on-select-html-tag */ - public function testShouldDeselectOptionOnMultipleSelectByVisiblePartialText() + public function testShouldDeselectOptionOnMultipleSelectByVisiblePartialText(): void { $select = $this->getWebDriverSelectForMultipleSelect(); $select->selectByValue('fourth'); // text 'Fourth with spaces inside' @@ -414,7 +425,7 @@ public function testShouldDeselectOptionOnMultipleSelectByVisiblePartialText() $this->assertContainsOptionsWithValues(['first'], $select->getAllSelectedOptions()); } - public function testShouldThrowExceptionIfDeselectingSimpleSelectByVisiblePartialText() + public function testShouldThrowExceptionIfDeselectingSimpleSelectByVisiblePartialText(): void { $select = $this->getWebDriverSelectForSimpleSelect(); @@ -423,20 +434,14 @@ public function testShouldThrowExceptionIfDeselectingSimpleSelectByVisiblePartia $select->deselectByVisiblePartialText('First'); } - /** - * @return WebDriverSelect - */ - protected function getWebDriverSelectForSimpleSelect() + protected function getWebDriverSelectForSimpleSelect(): WebDriverSelect { $originalElement = $this->driver->findElement(WebDriverBy::cssSelector('#select')); return new WebDriverSelect($originalElement); } - /** - * @return WebDriverSelect - */ - protected function getWebDriverSelectForMultipleSelect() + protected function getWebDriverSelectForMultipleSelect(): WebDriverSelect { $originalElement = $this->driver->findElement(WebDriverBy::cssSelector('#select-multiple')); @@ -445,9 +450,8 @@ protected function getWebDriverSelectForMultipleSelect() /** * @param string[] $expectedValues - * @param array $options */ - private function assertContainsOptionsWithValues(array $expectedValues, array $options) + private function assertContainsOptionsWithValues(array $expectedValues, array $options): void { $expectedCount = count($expectedValues); $this->assertContainsOnlyInstancesOf(WebDriverElement::class, $options); diff --git a/tests/functional/WebDriverTestCase.php b/tests/functional/WebDriverTestCase.php index dbd6606ac..db0fd8b0f 100644 --- a/tests/functional/WebDriverTestCase.php +++ b/tests/functional/WebDriverTestCase.php @@ -1,4 +1,4 @@ -setUpSauceLabs(); } else { $browserName = getenv('BROWSER_NAME'); + $disableHeadless = filter_var(getenv('DISABLE_HEADLESS') ?: '', FILTER_VALIDATE_BOOLEAN); if ($browserName === '' || $browserName === false) { $this->markTestSkipped( 'To execute functional tests browser name must be provided in BROWSER_NAME environment variable' @@ -46,29 +47,31 @@ protected function setUp(): void if ($browserName === WebDriverBrowserType::CHROME) { $chromeOptions = new ChromeOptions(); - // --no-sandbox is a workaround for Chrome crashing: https://github.com/SeleniumHQ/selenium/issues/4961 + $chromeOptions->addArguments([ - '--headless', - '--window-size=1024,768', - '--no-sandbox', + '--screen-info={1280x720}', + '--no-sandbox', // workaround for https://github.com/SeleniumHQ/selenium/issues/4961 '--force-color-profile=srgb', + '--disable-search-engine-choice-screen', ]); + if (!$disableHeadless) { + $chromeOptions->addArguments(['--headless']); + } + if (!static::isW3cProtocolBuild()) { $chromeOptions->setExperimentalOption('w3c', false); } $this->desiredCapabilities->setCapability(ChromeOptions::CAPABILITY, $chromeOptions); } elseif ($browserName === WebDriverBrowserType::FIREFOX) { - if (getenv('GECKODRIVER') === '1') { - $this->serverUrl = 'http://localhost:4444/'; + $firefoxOptions = new FirefoxOptions(); + + if (!$disableHeadless) { + $firefoxOptions->addArguments(['-headless']); } - $firefoxOptions = new FirefoxOptions(); - $firefoxOptions->addArguments(['-headless']); $this->desiredCapabilities->setCapability(FirefoxOptions::CAPABILITY, $firefoxOptions); - } elseif ($browserName === WebDriverBrowserType::SAFARI) { - $this->serverUrl = 'http://localhost:4444/'; } $this->desiredCapabilities->setBrowserName($browserName); @@ -89,35 +92,34 @@ protected function tearDown(): void if (getenv('BROWSER_NAME') === 'safari') { // The Safari instance is already paired with another WebDriver session - usleep(2e5); // 200ms + usleep(200000); // 200ms } } } - /** - * @return bool - */ - public static function isSauceLabsBuild() + public static function isSauceLabsBuild(): bool { return getenv('SAUCELABS') ? true : false; } - /** - * @return bool - */ - public static function isW3cProtocolBuild() + public static function isW3cProtocolBuild(): bool { return getenv('DISABLE_W3C_PROTOCOL') !== '1'; } - public static function skipForW3cProtocol($message = 'Not supported by W3C specification') + public static function isSeleniumServerUsed(): bool + { + return getenv('SELENIUM_SERVER') === '1'; + } + + public static function skipForW3cProtocol($message = 'Not supported by W3C specification'): void { if (static::isW3cProtocolBuild()) { static::markTestSkipped($message); } } - public static function skipForJsonWireProtocol($message = 'Not supported by JsonWire protocol') + public static function skipForJsonWireProtocol($message = 'Not supported by JsonWire protocol'): void { if (!static::isW3cProtocolBuild()) { static::markTestSkipped($message); @@ -128,9 +130,8 @@ public static function skipForJsonWireProtocol($message = 'Not supported by Json * Mark a test as skipped if the current browser is not in the list of browsers. * * @param string[] $browsers List of browsers for this test - * @param string|null $message */ - public static function skipForUnmatchedBrowsers($browsers = [], $message = null) + public static function skipForUnmatchedBrowsers(array $browsers = [], ?string $message = null): void { $browserName = (string) getenv('BROWSER_NAME'); if (!in_array($browserName, $browsers, true)) { @@ -169,11 +170,8 @@ public function runBare(): void /** * Get the URL of given test HTML on running webserver. - * - * @param string $path - * @return string */ - protected function getTestPageUrl($path) + protected function getTestPageUrl(string $path): string { $host = 'http://localhost:8000/'; if ($alternateHost = getenv('FIXTURES_HOST')) { @@ -183,7 +181,7 @@ protected function getTestPageUrl($path) return $host . '/' . $path; } - protected function setUpSauceLabs() + protected function setUpSauceLabs(): void { $this->serverUrl = sprintf( 'http://%s:%s@ondemand.saucelabs.com/wd/hub', @@ -200,14 +198,14 @@ protected function setUpSauceLabs() if ($ciDetector->isCiDetected()) { $ci = $ciDetector->detect(); if (!empty($ci->getBuildNumber())) { - // SAUCE_TUNNEL_IDENTIFIER appended as a workaround for GH actions not having environment value + // SAUCE_TUNNEL_NAME appended as a workaround for GH actions not having environment value // to distinguish runs of the matrix - $build = $ci->getBuildNumber() . '.' . getenv('SAUCE_TUNNEL_IDENTIFIER'); + $build = $ci->getBuildNumber() . '.' . getenv('SAUCE_TUNNEL_NAME'); } } - if (getenv('SAUCE_TUNNEL_IDENTIFIER')) { - $tunnelIdentifier = getenv('SAUCE_TUNNEL_IDENTIFIER'); + if (getenv('SAUCE_TUNNEL_NAME')) { + $tunnelName = getenv('SAUCE_TUNNEL_NAME'); } if (static::isW3cProtocolBuild()) { @@ -218,16 +216,16 @@ protected function setUpSauceLabs() if (isset($build)) { $sauceOptions['build'] = $build; } - if (isset($tunnelIdentifier)) { - $sauceOptions['tunnelIdentifier'] = $tunnelIdentifier; + if (isset($tunnelName)) { + $sauceOptions['tunnelName'] = $tunnelName; } $this->desiredCapabilities->setCapability('sauce:options', (object) $sauceOptions); } else { $this->desiredCapabilities->setCapability('name', $name); $this->desiredCapabilities->setCapability('tags', $tags); - if (isset($tunnelIdentifier)) { - $this->desiredCapabilities->setCapability('tunnel-identifier', $tunnelIdentifier); + if (isset($tunnelName)) { + $this->desiredCapabilities->setCapability('tunnel-identifier', $tunnelName); } if (isset($build)) { $this->desiredCapabilities->setCapability('build', $build); @@ -235,23 +233,7 @@ protected function setUpSauceLabs() } } - /** - * Uses assertStringContainsString when it is available or uses assertContains for old phpunit versions - * @param string $needle - * @param string $haystack - * @param string $message - */ - protected function compatAssertStringContainsString($needle, $haystack, $message = '') - { - if (method_exists($this, 'assertStringContainsString')) { - parent::assertStringContainsString($needle, $haystack, $message); - - return; - } - parent::assertContains($needle, $haystack, $message); - } - - protected function createWebDriver() + protected function createWebDriver(): void { $this->driver = RemoteWebDriver::create( $this->serverUrl, diff --git a/tests/functional/WebDriverTimeoutsTest.php b/tests/functional/WebDriverTimeoutsTest.php index 66f61f51a..52534ae37 100644 --- a/tests/functional/WebDriverTimeoutsTest.php +++ b/tests/functional/WebDriverTimeoutsTest.php @@ -1,4 +1,4 @@ -driver->get($this->getTestPageUrl(TestPage::DELAYED_ELEMENT)); @@ -27,7 +27,7 @@ public function testShouldFailGettingDelayedElementWithoutWait() * @covers ::__construct * @covers ::implicitlyWait */ - public function testShouldGetDelayedElementWithImplicitWait() + public function testShouldGetDelayedElementWithImplicitWait(): void { $this->driver->get($this->getTestPageUrl(TestPage::DELAYED_ELEMENT)); @@ -42,7 +42,7 @@ public function testShouldGetDelayedElementWithImplicitWait() * @covers ::__construct * @covers ::pageLoadTimeout */ - public function testShouldFailIfPageIsLoadingLongerThanPageLoadTimeout() + public function testShouldFailIfPageIsLoadingLongerThanPageLoadTimeout(): void { $this->driver->manage()->timeouts()->pageLoadTimeout(1); diff --git a/tests/functional/WebDriverWindowTest.php b/tests/functional/WebDriverWindowTest.php index 3cd91e587..219126a7e 100644 --- a/tests/functional/WebDriverWindowTest.php +++ b/tests/functional/WebDriverWindowTest.php @@ -1,4 +1,4 @@ -driver->manage() ->window() @@ -20,7 +20,7 @@ public function testShouldGetPosition() $this->assertGreaterThanOrEqual(0, $position->getY()); } - public function testShouldGetSize() + public function testShouldGetSize(): void { $size = $this->driver->manage() ->window() @@ -30,7 +30,7 @@ public function testShouldGetSize() $this->assertGreaterThan(0, $size->getHeight()); } - public function testShouldMaximizeWindow() + public function testShouldMaximizeWindow(): void { $sizeBefore = $this->driver->manage() ->window() @@ -50,9 +50,10 @@ public function testShouldMaximizeWindow() /** * @group exclude-edge + * @group exclude-safari * @group exclude-saucelabs */ - public function testShouldFullscreenWindow() + public function testShouldFullscreenWindow(): void { self::skipForJsonWireProtocol('"fullscreen" window is not supported in JsonWire protocol'); @@ -79,7 +80,7 @@ public function testShouldFullscreenWindow() * @group exclude-safari * @group exclude-saucelabs */ - public function testShouldMinimizeWindow() + public function testShouldMinimizeWindow(): void { self::skipForJsonWireProtocol('"minimize" window is not supported in JsonWire protocol'); @@ -95,7 +96,7 @@ public function testShouldMinimizeWindow() /** * @group exclude-saucelabs */ - public function testShouldSetSize() + public function testShouldSetSize(): void { $sizeBefore = $this->driver->manage() ->window() @@ -118,7 +119,7 @@ public function testShouldSetSize() /** * @todo Skip when running headless mode */ - public function testShouldSetWindowPosition() + public function testShouldSetWindowPosition(): void { $this->driver->manage() ->window() diff --git a/tests/functional/web/form.html b/tests/functional/web/form.html index 7463750a6..597baca2e 100644 --- a/tests/functional/web/form.html +++ b/tests/functional/web/form.html @@ -48,6 +48,22 @@ + + + +
diff --git a/tests/functional/web/slow_pixel.png.php b/tests/functional/web/slow_pixel.png.php index a834089ad..06885539a 100644 --- a/tests/functional/web/slow_pixel.png.php +++ b/tests/functional/web/slow_pixel.png.php @@ -1,4 +1,4 @@ - diff --git a/tests/functional/web/upload.php b/tests/functional/web/upload.php index 71143d46c..92b52b57a 100644 --- a/tests/functional/web/upload.php +++ b/tests/functional/web/upload.php @@ -1,4 +1,4 @@ - diff --git a/tests/functional/web/web_components.html b/tests/functional/web/web_components.html index c76101356..65e458c30 100644 --- a/tests/functional/web/web_components.html +++ b/tests/functional/web/web_components.html @@ -16,7 +16,7 @@

WebComponents and Shadow DOM tests

-
+

Element out of Shadow DOM

diff --git a/tests/unit/CookieTest.php b/tests/unit/CookieTest.php index d58419a9a..444569499 100644 --- a/tests/unit/CookieTest.php +++ b/tests/unit/CookieTest.php @@ -1,15 +1,13 @@ -setPath('/bar'); @@ -33,9 +31,8 @@ public function testShouldSetAllProperties() /** * @depends testShouldSetAllProperties - * @param Cookie $cookie */ - public function testShouldBeConvertibleToArray(Cookie $cookie) + public function testShouldBeConvertibleToArray(Cookie $cookie): void { $this->assertSame( [ @@ -61,7 +58,7 @@ public function testShouldBeConvertibleToArray(Cookie $cookie) * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol * https://w3c.github.io/webdriver/#add-cookie */ - public function testShouldNotContainNullValues() + public function testShouldNotContainNullValues(): void { $cookie = new Cookie('cookieName', 'someValue'); @@ -77,9 +74,8 @@ public function testShouldNotContainNullValues() /** * @depends testShouldSetAllProperties - * @param Cookie $cookie */ - public function testShouldProvideArrayAccessToProperties(Cookie $cookie) + public function testShouldProvideArrayAccessToProperties(Cookie $cookie): void { $this->assertSame('cookieName', $cookie['name']); $this->assertSame('someValue', $cookie['value']); @@ -96,7 +92,7 @@ public function testShouldProvideArrayAccessToProperties(Cookie $cookie) $this->assertArrayNotHasKey('domain', $cookie); } - public function testShouldBeCreatableFromAnArrayWithBasicValues() + public function testShouldBeCreatableFromAnArrayWithBasicValues(): void { $sourceArray = [ 'name' => 'cookieName', @@ -133,7 +129,7 @@ public function testShouldBeCreatableFromAnArrayWithBasicValues() $this->assertNull($cookie->getSameSite()); } - public function testShouldBeCreatableFromAnArrayWithAllValues() + public function testShouldBeCreatableFromAnArrayWithAllValues(): void { $sourceArray = [ 'name' => 'cookieName', @@ -160,15 +156,15 @@ public function testShouldBeCreatableFromAnArrayWithAllValues() /** * @dataProvider provideInvalidCookie - * @param string $name - * @param string $value - * @param string $domain - * @param string $expectedMessage */ - public function testShouldValidateCookieOnConstruction($name, $value, $domain, $expectedMessage) - { + public function testShouldValidateCookieOnConstruction( + ?string $name, + ?string $value, + ?string $domain, + ?string $expectedMessage + ): void { if ($expectedMessage) { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage($expectedMessage); } @@ -183,7 +179,7 @@ public function testShouldValidateCookieOnConstruction($name, $value, $domain, $ /** * @return array[] */ - public function provideInvalidCookie() + public function provideInvalidCookie(): array { return [ // $name, $value, $domain, $expectedMessage diff --git a/tests/unit/Exception/DriverServerDiedExceptionTest.php b/tests/unit/Exception/Internal/DriverServerDiedExceptionTest.php similarity index 52% rename from tests/unit/Exception/DriverServerDiedExceptionTest.php rename to tests/unit/Exception/Internal/DriverServerDiedExceptionTest.php index 3c98e6e00..2e2fc5617 100644 --- a/tests/unit/Exception/DriverServerDiedExceptionTest.php +++ b/tests/unit/Exception/Internal/DriverServerDiedExceptionTest.php @@ -1,14 +1,14 @@ -assertSame('Error message ("/file/path.txt")', $exception->getMessage()); + } +} diff --git a/tests/unit/Exception/Internal/LogicExceptionTest.php b/tests/unit/Exception/Internal/LogicExceptionTest.php new file mode 100644 index 000000000..ba09d58d8 --- /dev/null +++ b/tests/unit/Exception/Internal/LogicExceptionTest.php @@ -0,0 +1,26 @@ +assertSame('Error message', $exception->getMessage()); + } + + public function testShouldCreateExceptionForInvalidHttpMethod(): void + { + $exception = LogicException::forInvalidHttpMethod('http://foo.bar/', 'FOO', ['key' => 'val']); + + $this->assertSame( + 'The http method called for "http://foo.bar/" is "FOO", but it has to be POST if you want to pass' + . ' the JSON params {"key":"val"}', + $exception->getMessage() + ); + } +} diff --git a/tests/unit/Exception/Internal/RuntimeExceptionTest.php b/tests/unit/Exception/Internal/RuntimeExceptionTest.php new file mode 100644 index 000000000..97adf87b3 --- /dev/null +++ b/tests/unit/Exception/Internal/RuntimeExceptionTest.php @@ -0,0 +1,34 @@ +assertSame('Error message', $exception->getMessage()); + } + + public function testShouldCreateExceptionForDriverError(): void + { + $processMock = $this->createConfiguredMock( + Process::class, + [ + 'getCommandLine' => '/bin/true --force', + 'getErrorOutput' => 'may the force be with you', + ] + ); + + $exception = RuntimeException::forDriverError($processMock); + + $this->assertSame( + 'Error starting driver executable "/bin/true --force": may the force be with you', + $exception->getMessage() + ); + } +} diff --git a/tests/unit/Exception/Internal/UnexpectedResponseExceptionTest.php b/tests/unit/Exception/Internal/UnexpectedResponseExceptionTest.php new file mode 100644 index 000000000..80f4b4a0d --- /dev/null +++ b/tests/unit/Exception/Internal/UnexpectedResponseExceptionTest.php @@ -0,0 +1,30 @@ +assertSame('Error message', $exception->getMessage()); + } + + public function testShouldCreateExceptionForJsonDecodingError(): void + { + $exception = UnexpectedResponseException::forJsonDecodingError(JSON_ERROR_SYNTAX, 'foo'); + + $this->assertSame( + <<getMessage() + ); + } +} diff --git a/tests/unit/Exception/Internal/WebDriverCurlExceptionTest.php b/tests/unit/Exception/Internal/WebDriverCurlExceptionTest.php new file mode 100644 index 000000000..acc94cb88 --- /dev/null +++ b/tests/unit/Exception/Internal/WebDriverCurlExceptionTest.php @@ -0,0 +1,34 @@ +assertSame( + <<getMessage() + ); + } + + public function provideParams(): array + { + return [ + 'null params' => [null, ''], + 'empty params' => [[], ''], + 'array of params' => [['bar' => 'foo', 'baz' => 'bat'], ' with params: {"bar":"foo","baz":"bat"}'], + ]; + } +} diff --git a/tests/unit/Exception/WebDriverExceptionTest.php b/tests/unit/Exception/WebDriverExceptionTest.php index 5013265f1..c770533d2 100644 --- a/tests/unit/Exception/WebDriverExceptionTest.php +++ b/tests/unit/Exception/WebDriverExceptionTest.php @@ -1,4 +1,4 @@ - */ - const EXPECTED_DEFAULT_PREFS = [ + public const EXPECTED_DEFAULT_PREFS = [ FirefoxPreferences::READER_PARSE_ON_LOAD_ENABLED => false, FirefoxPreferences::DEVTOOLS_JSONVIEW => false, ]; - public function testShouldBeConstructedWithDefaultOptions() + public function testShouldBeConstructedWithDefaultOptions(): void { $options = new FirefoxOptions(); @@ -24,7 +25,7 @@ public function testShouldBeConstructedWithDefaultOptions() ); } - public function testShouldAddCustomOptions() + public function testShouldAddCustomOptions(): void { $options = new FirefoxOptions(); @@ -39,7 +40,7 @@ public function testShouldAddCustomOptions() ); } - public function testShouldOverwriteDefaultOptionsWhenSpecified() + public function testShouldOverwriteDefaultOptionsWhenSpecified(): void { $options = new FirefoxOptions(); @@ -56,7 +57,7 @@ public function testShouldOverwriteDefaultOptionsWhenSpecified() ); } - public function testShouldSetCustomPreference() + public function testShouldSetCustomPreference(): void { $options = new FirefoxOptions(); @@ -74,7 +75,7 @@ public function testShouldSetCustomPreference() ); } - public function testShouldAddArguments() + public function testShouldAddArguments(): void { $options = new FirefoxOptions(); @@ -89,7 +90,7 @@ public function testShouldAddArguments() ); } - public function testShouldJsonSerializeToArrayObject() + public function testShouldJsonSerializeToArrayObject(): void { $options = new FirefoxOptions(); $options->setOption('binary', '/usr/local/firefox/bin/firefox'); @@ -100,20 +101,20 @@ public function testShouldJsonSerializeToArrayObject() $this->assertSame('/usr/local/firefox/bin/firefox', $jsonSerialized['binary']); } - public function testShouldNotAllowToSetArgumentsOptionDirectly() + public function testShouldNotAllowToSetArgumentsOptionDirectly(): void { $options = new FirefoxOptions(); - $this->expectException(\InvalidArgumentException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('Use addArguments() method to add Firefox arguments'); $options->setOption('args', []); } - public function testShouldNotAllowToSetPreferencesOptionDirectly() + public function testShouldNotAllowToSetPreferencesOptionDirectly(): void { $options = new FirefoxOptions(); - $this->expectException(\InvalidArgumentException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('Use setPreference() method to set Firefox preferences'); $options->setOption('prefs', []); } diff --git a/tests/unit/Interactions/Internal/WebDriverButtonReleaseActionTest.php b/tests/unit/Interactions/Internal/WebDriverButtonReleaseActionTest.php index 22aed07ef..f14bad0c2 100644 --- a/tests/unit/Interactions/Internal/WebDriverButtonReleaseActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverButtonReleaseActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverClickActionTest.php b/tests/unit/Interactions/Internal/WebDriverClickActionTest.php index 7ec720f5d..de12aab26 100644 --- a/tests/unit/Interactions/Internal/WebDriverClickActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverClickActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverClickAndHoldActionTest.php b/tests/unit/Interactions/Internal/WebDriverClickAndHoldActionTest.php index 749215583..da5a18dee 100644 --- a/tests/unit/Interactions/Internal/WebDriverClickAndHoldActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverClickAndHoldActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverContextClickActionTest.php b/tests/unit/Interactions/Internal/WebDriverContextClickActionTest.php index 5bc3282e7..f3e66f6ee 100644 --- a/tests/unit/Interactions/Internal/WebDriverContextClickActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverContextClickActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverCoordinatesTest.php b/tests/unit/Interactions/Internal/WebDriverCoordinatesTest.php index b14ad2654..f8faee8d8 100644 --- a/tests/unit/Interactions/Internal/WebDriverCoordinatesTest.php +++ b/tests/unit/Interactions/Internal/WebDriverCoordinatesTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverKeyDownActionTest.php b/tests/unit/Interactions/Internal/WebDriverKeyDownActionTest.php index edf522b25..0fd8aa9dd 100644 --- a/tests/unit/Interactions/Internal/WebDriverKeyDownActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverKeyDownActionTest.php @@ -1,4 +1,4 @@ -createMock(WebDriverCoordinates::class); $this->webDriverMouse->expects($this->once())->method('click')->with($coords); diff --git a/tests/unit/Interactions/Internal/WebDriverKeyUpActionTest.php b/tests/unit/Interactions/Internal/WebDriverKeyUpActionTest.php index 275a4b968..f10d53b09 100644 --- a/tests/unit/Interactions/Internal/WebDriverKeyUpActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverKeyUpActionTest.php @@ -1,4 +1,4 @@ -createMock(WebDriverCoordinates::class); $this->webDriverMouse->expects($this->once())->method('click')->with($coords); diff --git a/tests/unit/Interactions/Internal/WebDriverMouseMoveActionTest.php b/tests/unit/Interactions/Internal/WebDriverMouseMoveActionTest.php index 47c8aa191..f23594852 100644 --- a/tests/unit/Interactions/Internal/WebDriverMouseMoveActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverMouseMoveActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverMouseToOffsetActionTest.php b/tests/unit/Interactions/Internal/WebDriverMouseToOffsetActionTest.php index af2c9be09..966b88325 100644 --- a/tests/unit/Interactions/Internal/WebDriverMouseToOffsetActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverMouseToOffsetActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverSendKeysActionTest.php b/tests/unit/Interactions/Internal/WebDriverSendKeysActionTest.php index d38816479..b610d083f 100644 --- a/tests/unit/Interactions/Internal/WebDriverSendKeysActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverSendKeysActionTest.php @@ -1,4 +1,4 @@ -getMockBuilder(WebDriverCoordinates::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/unit/Interactions/Internal/WebDriverSingleKeyActionTest.php b/tests/unit/Interactions/Internal/WebDriverSingleKeyActionTest.php index ddded93bb..9a3e61160 100644 --- a/tests/unit/Interactions/Internal/WebDriverSingleKeyActionTest.php +++ b/tests/unit/Interactions/Internal/WebDriverSingleKeyActionTest.php @@ -1,7 +1,8 @@ -expectException(\InvalidArgumentException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage( 'keyDown / keyUp actions can only be used for modifier keys, but "foo" was given' ); diff --git a/tests/unit/Remote/CustomWebDriverCommandTest.php b/tests/unit/Remote/CustomWebDriverCommandTest.php index b56bc4aea..8e829c615 100644 --- a/tests/unit/Remote/CustomWebDriverCommandTest.php +++ b/tests/unit/Remote/CustomWebDriverCommandTest.php @@ -1,13 +1,13 @@ -assertSame('POST', $command->getCustomMethod()); } - public function testCustomCommandInvalidUrlExceptionInit() + public function testCustomCommandInvalidUrlExceptionInit(): void { - $this->expectException(WebDriverException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('URL of custom command has to start with / but is "url-without-leading-slash"'); new CustomWebDriverCommand('session-id-123', 'url-without-leading-slash', 'POST', []); } - public function testCustomCommandInvalidMethodExceptionInit() + public function testCustomCommandInvalidMethodExceptionInit(): void { - $this->expectException(WebDriverException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('Invalid custom method "invalid-method", must be one of [GET, POST]'); new CustomWebDriverCommand('session-id-123', '/some-url', 'invalid-method', []); diff --git a/tests/unit/Remote/DesiredCapabilitiesTest.php b/tests/unit/Remote/DesiredCapabilitiesTest.php index 0d96831aa..e57b2a4d3 100644 --- a/tests/unit/Remote/DesiredCapabilitiesTest.php +++ b/tests/unit/Remote/DesiredCapabilitiesTest.php @@ -1,4 +1,4 @@ - 'fooVal', WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY] @@ -27,7 +27,7 @@ public function testShouldInstantiateWithCapabilitiesGivenInConstructor() ); } - public function testShouldInstantiateEmptyInstance() + public function testShouldInstantiateEmptyInstance(): void { $capabilities = new DesiredCapabilities(); @@ -35,7 +35,7 @@ public function testShouldInstantiateEmptyInstance() $this->assertSame([], $capabilities->toArray()); } - public function testShouldProvideAccessToCapabilitiesUsingSettersAndGetters() + public function testShouldProvideAccessToCapabilitiesUsingSettersAndGetters(): void { $capabilities = new DesiredCapabilities(); // generic capability setter @@ -51,7 +51,7 @@ public function testShouldProvideAccessToCapabilitiesUsingSettersAndGetters() $this->assertSame(333, $capabilities->getVersion()); } - public function testShouldAccessCapabilitiesIsser() + public function testShouldAccessCapabilitiesIsser(): void { $capabilities = new DesiredCapabilities(); @@ -66,7 +66,7 @@ public function testShouldAccessCapabilitiesIsser() $this->assertFalse($capabilities->is('customNull')); } - public function testShouldNotAllowToDisableJavascriptForNonHtmlUnitBrowser() + public function testShouldNotAllowToDisableJavascriptForNonHtmlUnitBrowser(): void { $this->expectException(\Exception::class); $this->expectExceptionMessage('isJavascriptEnabled() is a htmlunit-only option'); @@ -76,7 +76,7 @@ public function testShouldNotAllowToDisableJavascriptForNonHtmlUnitBrowser() $capabilities->setJavascriptEnabled(false); } - public function testShouldAllowToDisableJavascriptForHtmlUnitBrowser() + public function testShouldAllowToDisableJavascriptForHtmlUnitBrowser(): void { $capabilities = new DesiredCapabilities(); $capabilities->setBrowserName(WebDriverBrowserType::HTMLUNIT); @@ -87,15 +87,12 @@ public function testShouldAllowToDisableJavascriptForHtmlUnitBrowser() /** * @dataProvider provideBrowserCapabilities - * @param string $setupMethod - * @param string $expectedBrowser - * @param string $expectedPlatform */ public function testShouldProvideShortcutSetupForCapabilitiesOfEachBrowser( - $setupMethod, - $expectedBrowser, - $expectedPlatform - ) { + string $setupMethod, + string $expectedBrowser, + string $expectedPlatform + ): void { /** @var DesiredCapabilities $capabilities */ $capabilities = call_user_func([DesiredCapabilities::class, $setupMethod]); @@ -106,7 +103,7 @@ public function testShouldProvideShortcutSetupForCapabilitiesOfEachBrowser( /** * @return array[] */ - public function provideBrowserCapabilities() + public function provideBrowserCapabilities(): array { return [ ['android', WebDriverBrowserType::ANDROID, WebDriverPlatform::ANDROID], @@ -124,7 +121,7 @@ public function provideBrowserCapabilities() ]; } - public function testShouldSetupFirefoxWithDefaultOptions() + public function testShouldSetupFirefoxWithDefaultOptions(): void { $capabilitiesArray = DesiredCapabilities::firefox()->toArray(); @@ -137,7 +134,7 @@ public function testShouldSetupFirefoxWithDefaultOptions() ); } - public function testShouldSetupFirefoxWithCustomOptions() + public function testShouldSetupFirefoxWithCustomOptions(): void { $firefoxOptions = new FirefoxOptions(); $firefoxOptions->addArguments(['-headless']); @@ -159,7 +156,7 @@ public function testShouldSetupFirefoxWithCustomOptions() ); } - public function testShouldNotOverwriteDefaultFirefoxOptionsWhenAddingFirefoxOptionAsArray() + public function testShouldNotOverwriteDefaultFirefoxOptionsWhenAddingFirefoxOptionAsArray(): void { $capabilities = DesiredCapabilities::firefox(); $capabilities->setCapability('moz:firefoxOptions', ['args' => ['-headless']]); @@ -175,23 +172,21 @@ public function testShouldNotOverwriteDefaultFirefoxOptionsWhenAddingFirefoxOpti /** * @dataProvider provideW3cCapabilities - * @param DesiredCapabilities $inputJsonWireCapabilities - * @param array $expectedW3cCapabilities */ public function testShouldConvertCapabilitiesToW3cCompatible( DesiredCapabilities $inputJsonWireCapabilities, array $expectedW3cCapabilities - ) { + ): void { $this->assertJsonStringEqualsJsonString( - json_encode($expectedW3cCapabilities), - json_encode($inputJsonWireCapabilities->toW3cCompatibleArray()) + json_encode($expectedW3cCapabilities, JSON_THROW_ON_ERROR), + json_encode($inputJsonWireCapabilities->toW3cCompatibleArray(), JSON_THROW_ON_ERROR) ); } /** * @return array[] */ - public function provideW3cCapabilities() + public function provideW3cCapabilities(): array { $chromeOptions = new ChromeOptions(); $chromeOptions->addArguments(['--headless']); @@ -285,23 +280,6 @@ public function provideW3cCapabilities() ), ], ], - 'chromeOptions should be merged if already defined' => [ - new DesiredCapabilities([ - ChromeOptions::CAPABILITY => $chromeOptions, - ChromeOptions::CAPABILITY_W3C => [ - 'debuggerAddress' => '127.0.0.1:38947', - 'args' => ['window-size=1024,768'], - ], - ]), - [ - 'goog:chromeOptions' => new \ArrayObject( - [ - 'args' => ['--headless', 'window-size=1024,768'], - 'debuggerAddress' => '127.0.0.1:38947', - ] - ), - ], - ], 'firefox_profile should be converted' => [ new DesiredCapabilities([ FirefoxDriver::PROFILE => $firefoxProfileEncoded, diff --git a/tests/unit/Remote/HttpCommandExecutorTest.php b/tests/unit/Remote/HttpCommandExecutorTest.php index 30e117536..b29d788b1 100644 --- a/tests/unit/Remote/HttpCommandExecutorTest.php +++ b/tests/unit/Remote/HttpCommandExecutorTest.php @@ -1,4 +1,4 @@ -getFunctionMock(__NAMESPACE__, 'curl_setopt'); - $curlSetoptMock->expects($this->at(0)) - ->with($this->anything(), CURLOPT_URL, $expectedUrl); + bool $shouldResetExpectHeader, + string $expectedUrl, + ?string $expectedPostData + ): void { + $expectedCurlSetOptCalls = [ + [$this->anything(), CURLOPT_URL, $expectedUrl], + [$this->anything()], + ]; if ($shouldResetExpectHeader) { - $curlSetoptMock->expects($this->at(2)) - ->with( - $this->anything(), - CURLOPT_HTTPHEADER, - ['Content-Type: application/json;charset=UTF-8', 'Accept: application/json', 'Expect:'] - ); - $curlSetoptMock->expects($this->at(3)) - ->with($this->anything(), CURLOPT_POSTFIELDS, $expectedPostData); + $expectedCurlSetOptCalls[] = [ + $this->anything(), + CURLOPT_HTTPHEADER, + ['Content-Type: application/json;charset=UTF-8', 'Accept: application/json', 'Expect:'], + ]; } else { - $curlSetoptMock->expects($this->at(2)) - ->with( - $this->anything(), - CURLOPT_HTTPHEADER, - ['Content-Type: application/json;charset=UTF-8', 'Accept: application/json'] - ); - $curlSetoptMock->expects($this->at(3)) - ->with($this->anything(), CURLOPT_POSTFIELDS, $expectedPostData); + $expectedCurlSetOptCalls[] = [ + $this->anything(), + CURLOPT_HTTPHEADER, + ['Content-Type: application/json;charset=UTF-8', 'Accept: application/json'], + ]; } + $expectedCurlSetOptCalls[] = [$this->anything(), CURLOPT_POSTFIELDS, $expectedPostData]; + + $curlSetoptMock = $this->getFunctionMock(__NAMESPACE__, 'curl_setopt'); + $curlSetoptMock->expects($this->exactly(4)) + ->withConsecutive(...$expectedCurlSetOptCalls); + $curlExecMock = $this->getFunctionMock(__NAMESPACE__, 'curl_exec'); $curlExecMock->expects($this->once()) ->willReturn('{}'); @@ -64,7 +61,7 @@ public function testShouldSendRequestToAssembledUrl( /** * @return array[] */ - public function provideCommand() + public function provideCommand(): array { return [ 'POST command having :id placeholder in url' => [ diff --git a/tests/unit/Remote/LocalFileDetectorTest.php b/tests/unit/Remote/LocalFileDetectorTest.php index b58413d8b..dc6112636 100644 --- a/tests/unit/Remote/LocalFileDetectorTest.php +++ b/tests/unit/Remote/LocalFileDetectorTest.php @@ -1,4 +1,4 @@ -assertSame(__FILE__, $file); } - public function testShouldReturnNullIfFileNotDetected() + public function testShouldReturnNullIfFileNotDetected(): void { $detector = new LocalFileDetector(); diff --git a/tests/unit/Remote/RemoteWebDriverTest.php b/tests/unit/Remote/RemoteWebDriverTest.php index 198fd3997..666ac9e5c 100644 --- a/tests/unit/Remote/RemoteWebDriverTest.php +++ b/tests/unit/Remote/RemoteWebDriverTest.php @@ -1,8 +1,8 @@ -driver = RemoteWebDriver::createBySessionID('session-id', 'http://foo.bar:4444/'); + // `createBySessionID()` is used because it is the simplest way to instantiate real RemoteWebDriver + $this->driver = RemoteWebDriver::createBySessionID( + 'session-id', + 'http://foo.bar:4444/', + null, + null, + true, + new DesiredCapabilities([]) + ); } /** * @covers ::manage */ - public function testShouldCreateWebDriverOptionsInstance() + public function testShouldCreateWebDriverOptionsInstance(): void { $wait = $this->driver->manage(); @@ -38,7 +46,7 @@ public function testShouldCreateWebDriverOptionsInstance() /** * @covers ::navigate */ - public function testShouldCreateWebDriverNavigationInstance() + public function testShouldCreateWebDriverNavigationInstance(): void { $wait = $this->driver->navigate(); @@ -48,7 +56,7 @@ public function testShouldCreateWebDriverNavigationInstance() /** * @covers ::switchTo */ - public function testShouldCreateRemoteTargetLocatorInstance() + public function testShouldCreateRemoteTargetLocatorInstance(): void { $wait = $this->driver->switchTo(); @@ -58,7 +66,7 @@ public function testShouldCreateRemoteTargetLocatorInstance() /** * @covers ::getMouse */ - public function testShouldCreateRemoteMouseInstance() + public function testShouldCreateRemoteMouseInstance(): void { $wait = $this->driver->getMouse(); @@ -68,7 +76,7 @@ public function testShouldCreateRemoteMouseInstance() /** * @covers ::getKeyboard */ - public function testShouldCreateRemoteKeyboardInstance() + public function testShouldCreateRemoteKeyboardInstance(): void { $wait = $this->driver->getKeyboard(); @@ -78,7 +86,7 @@ public function testShouldCreateRemoteKeyboardInstance() /** * @covers ::getTouch */ - public function testShouldCreateRemoteTouchScreenInstance() + public function testShouldCreateRemoteTouchScreenInstance(): void { $wait = $this->driver->getTouch(); @@ -88,7 +96,7 @@ public function testShouldCreateRemoteTouchScreenInstance() /** * @covers ::action */ - public function testShouldCreateWebDriverActionsInstance() + public function testShouldCreateWebDriverActionsInstance(): void { $wait = $this->driver->action(); @@ -98,7 +106,7 @@ public function testShouldCreateWebDriverActionsInstance() /** * @covers ::wait */ - public function testShouldCreateWebDriverWaitInstance() + public function testShouldCreateWebDriverWaitInstance(): void { $wait = $this->driver->wait(15, 1337); @@ -106,11 +114,10 @@ public function testShouldCreateWebDriverWaitInstance() } /** - * @param string $method - * @param string $expectedExceptionMessage - * @dataProvider provideMethods + * @covers ::findElements + * @covers \Facebook\WebDriver\Exception\Internal\UnexpectedResponseException */ - public function testShouldThrowExceptionOnUnexpectedNullValueFromRemoteEnd($method, $expectedExceptionMessage) + public function testShouldThrowExceptionOnUnexpectedValueFromRemoteEndWhenFindingElements(): void { $executorMock = $this->createMock(HttpCommandExecutor::class); $executorMock->expects($this->once()) @@ -120,16 +127,8 @@ public function testShouldThrowExceptionOnUnexpectedNullValueFromRemoteEnd($meth $this->driver->setCommandExecutor($executorMock); - $this->expectException(UnknownErrorException::class); - $this->expectExceptionMessage($expectedExceptionMessage); - call_user_func([$this->driver, $method], $this->createMock(WebDriverBy::class)); - } - - public function provideMethods() - { - return [ - ['findElement', 'Unexpected server response to findElement command'], - ['findElements', 'Unexpected server response to findElements command'], - ]; + $this->expectException(UnexpectedResponseException::class); + $this->expectExceptionMessage('Server response to findElements command is not an array'); + $this->driver->findElements($this->createMock(WebDriverBy::class)); } } diff --git a/tests/unit/Remote/RemoteWebElementTest.php b/tests/unit/Remote/RemoteWebElementTest.php index 7ca0eddb6..a7ec76d8a 100644 --- a/tests/unit/Remote/RemoteWebElementTest.php +++ b/tests/unit/Remote/RemoteWebElementTest.php @@ -1,4 +1,4 @@ -createMock(RemoteExecuteMethod::class); $element = new RemoteWebElement($executeMethod, 333); diff --git a/tests/unit/Remote/WebDriverCommandTest.php b/tests/unit/Remote/WebDriverCommandTest.php index 8ba7abf33..182b72d94 100644 --- a/tests/unit/Remote/WebDriverCommandTest.php +++ b/tests/unit/Remote/WebDriverCommandTest.php @@ -1,4 +1,4 @@ - 'bar']); @@ -15,7 +15,7 @@ public function testShouldSetOptionsUsingConstructor() $this->assertSame(['foo' => 'bar'], $command->getParameters()); } - public function testShouldCreateNewSessionCommand() + public function testShouldCreateNewSessionCommand(): void { $command = WebDriverCommand::newSession(['bar' => 'baz']); diff --git a/tests/unit/Support/ScreenshotHelperTest.php b/tests/unit/Support/ScreenshotHelperTest.php index edc3f661f..5f7ff0ca6 100644 --- a/tests/unit/Support/ScreenshotHelperTest.php +++ b/tests/unit/Support/ScreenshotHelperTest.php @@ -1,21 +1,22 @@ -assertDirectoryNotExists($directoryPath); + $this->assertDirectoryDoesNotExist($directoryPath); $executorMock = $this->createMock(RemoteExecuteMethod::class); $executorMock->expects($this->once()) @@ -35,7 +36,7 @@ public function testShouldSavePageScreenshotToSubdirectoryIfNotExists() rmdir($directoryPath); } - public function testShouldOnlyReturnBase64IfDirectoryNotProvided() + public function testShouldOnlyReturnBase64IfDirectoryNotProvided(): void { $executorMock = $this->createMock(RemoteExecuteMethod::class); $executorMock->expects($this->once()) @@ -49,11 +50,11 @@ public function testShouldOnlyReturnBase64IfDirectoryNotProvided() $this->assertSame(base64_decode(self::BLACK_PIXEL, true), $output); } - public function testShouldSaveElementScreenshotToSubdirectoryIfNotExists() + public function testShouldSaveElementScreenshotToSubdirectoryIfNotExists(): void { $fullFilePath = sys_get_temp_dir() . '/' . uniqid('php-webdriver-', true) . '/screenshot.png'; $directoryPath = dirname($fullFilePath); - $this->assertDirectoryNotExists($directoryPath); + $this->assertDirectoryDoesNotExist($directoryPath); $elementId = 'foo-id'; $executorMock = $this->createMock(RemoteExecuteMethod::class); @@ -76,9 +77,8 @@ public function testShouldSaveElementScreenshotToSubdirectoryIfNotExists() /** * @dataProvider provideInvalidData * @param mixed $data - * @param string $expectedExceptionMessage */ - public function testShouldThrowExceptionWhenInvalidDataReceived($data, $expectedExceptionMessage) + public function testShouldThrowExceptionWhenInvalidDataReceived($data, string $expectedExceptionMessage): void { $executorMock = $this->createMock(RemoteExecuteMethod::class); $executorMock->expects($this->once()) @@ -88,12 +88,12 @@ public function testShouldThrowExceptionWhenInvalidDataReceived($data, $expected $helper = new ScreenshotHelper($executorMock); - $this->expectException(WebDriverException::class); + $this->expectException(UnexpectedResponseException::class); $this->expectExceptionMessage($expectedExceptionMessage); $helper->takePageScreenshot(); } - public function provideInvalidData() + public function provideInvalidData(): array { return [ 'empty response' => [null, 'Error taking screenshot, no data received from the remote end'], diff --git a/tests/unit/Support/XPathEscaperTest.php b/tests/unit/Support/XPathEscaperTest.php index e5a373f98..9804d93e1 100644 --- a/tests/unit/Support/XPathEscaperTest.php +++ b/tests/unit/Support/XPathEscaperTest.php @@ -1,4 +1,4 @@ - ['', "''"], diff --git a/tests/unit/WebDriverExpectedConditionTest.php b/tests/unit/WebDriverExpectedConditionTest.php index d401a8886..4ac75336b 100644 --- a/tests/unit/WebDriverExpectedConditionTest.php +++ b/tests/unit/WebDriverExpectedConditionTest.php @@ -1,4 +1,4 @@ -wait = new WebDriverWait($this->driverMock, 1, 1); } - public function testShouldDetectTitleIsCondition() + public function testShouldDetectTitleIsCondition(): void { $this->driverMock->expects($this->any()) ->method('getTitle') @@ -38,7 +38,7 @@ public function testShouldDetectTitleIsCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectTitleContainsCondition() + public function testShouldDetectTitleContainsCondition(): void { $this->driverMock->expects($this->any()) ->method('getTitle') @@ -51,7 +51,7 @@ public function testShouldDetectTitleContainsCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectTitleMatchesCondition() + public function testShouldDetectTitleMatchesCondition(): void { $this->driverMock->expects($this->any()) ->method('getTitle') @@ -64,7 +64,7 @@ public function testShouldDetectTitleMatchesCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectUrlIsCondition() + public function testShouldDetectUrlIsCondition(): void { $this->driverMock->expects($this->any()) ->method('getCurrentURL') @@ -77,7 +77,7 @@ public function testShouldDetectUrlIsCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectUrlContainsCondition() + public function testShouldDetectUrlContainsCondition(): void { $this->driverMock->expects($this->any()) ->method('getCurrentURL') @@ -90,7 +90,7 @@ public function testShouldDetectUrlContainsCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectUrlMatchesCondition() + public function testShouldDetectUrlMatchesCondition(): void { $this->driverMock->expects($this->any()) ->method('getCurrentURL') @@ -103,7 +103,7 @@ public function testShouldDetectUrlMatchesCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectPresenceOfElementLocatedCondition() + public function testShouldDetectPresenceOfElementLocatedCondition(): void { $element = new RemoteWebElement(new RemoteExecuteMethod($this->driverMock), 'id'); @@ -120,7 +120,7 @@ public function testShouldDetectPresenceOfElementLocatedCondition() $this->assertSame($element, $this->wait->until($condition)); } - public function testShouldDetectNotPresenceOfElementLocatedCondition() + public function testShouldDetectNotPresenceOfElementLocatedCondition(): void { $element = new RemoteWebElement(new RemoteExecuteMethod($this->driverMock), 'id'); @@ -140,7 +140,7 @@ public function testShouldDetectNotPresenceOfElementLocatedCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - public function testShouldDetectPresenceOfAllElementsLocatedByCondition() + public function testShouldDetectPresenceOfAllElementsLocatedByCondition(): void { $element = $this->createMock(RemoteWebElement::class); @@ -157,7 +157,7 @@ public function testShouldDetectPresenceOfAllElementsLocatedByCondition() $this->assertSame([$element], $this->wait->until($condition)); } - public function testShouldDetectVisibilityOfElementLocatedCondition() + public function testShouldDetectVisibilityOfElementLocatedCondition(): void { // Set-up the consecutive calls to apply() as follows: // Call #1: throws NoSuchElementException @@ -181,7 +181,7 @@ public function testShouldDetectVisibilityOfElementLocatedCondition() $this->assertSame($element, $this->wait->until($condition)); } - public function testShouldDetectVisibilityOfAnyElementLocated() + public function testShouldDetectVisibilityOfAnyElementLocated(): void { $elementList = [ $this->createMock(RemoteWebElement::class), @@ -211,7 +211,7 @@ public function testShouldDetectVisibilityOfAnyElementLocated() $this->assertSame([$elementList[1], $elementList[2]], $this->wait->until($condition)); } - public function testShouldDetectInvisibilityOfElementLocatedConditionOnNoSuchElementException() + public function testShouldDetectInvisibilityOfElementLocatedConditionOnNoSuchElementException(): void { $element = $this->createMock(RemoteWebElement::class); @@ -232,7 +232,7 @@ public function testShouldDetectInvisibilityOfElementLocatedConditionOnNoSuchEle $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectInvisibilityOfElementLocatedConditionOnStaleElementReferenceException() + public function testShouldDetectInvisibilityOfElementLocatedConditionOnStaleElementReferenceException(): void { $element = $this->createMock(RemoteWebElement::class); @@ -253,7 +253,7 @@ public function testShouldDetectInvisibilityOfElementLocatedConditionOnStaleElem $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectInvisibilityOfElementLocatedConditionWhenElementBecamesInvisible() + public function testShouldDetectInvisibilityOfElementLocatedConditionWhenElementBecamesInvisible(): void { $element = $this->createMock(RemoteWebElement::class); @@ -274,7 +274,7 @@ public function testShouldDetectInvisibilityOfElementLocatedConditionWhenElement $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectVisibilityOfCondition() + public function testShouldDetectVisibilityOfCondition(): void { $element = $this->createMock(RemoteWebElement::class); $element->expects($this->exactly(2)) @@ -289,7 +289,7 @@ public function testShouldDetectVisibilityOfCondition() $this->assertSame($element, $this->wait->until($condition)); } - public function testShouldDetectElementTextContainsCondition() + public function testShouldDetectElementTextContainsCondition(): void { // Set-up the consecutive calls to apply() as follows: // Call #1: throws NoSuchElementException @@ -313,7 +313,7 @@ public function testShouldDetectElementTextContainsCondition() $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectElementTextIsCondition() + public function testShouldDetectElementTextIsCondition(): void { // Set-up the consecutive calls to apply() as follows: // Call #1: throws NoSuchElementException @@ -340,7 +340,7 @@ public function testShouldDetectElementTextIsCondition() $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectElementTextMatchesCondition() + public function testShouldDetectElementTextMatchesCondition(): void { // Set-up the consecutive calls to apply() as follows: // Call #1: throws NoSuchElementException @@ -368,7 +368,7 @@ public function testShouldDetectElementTextMatchesCondition() $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectElementValueContainsCondition() + public function testShouldDetectElementValueContainsCondition(): void { // Set-up the consecutive calls to apply() as follows: // Call #1: throws NoSuchElementException @@ -397,7 +397,7 @@ public function testShouldDetectElementValueContainsCondition() $this->assertTrue($this->wait->until($condition)); } - public function testShouldDetectNumberOfWindowsToBeCondition() + public function testShouldDetectNumberOfWindowsToBeCondition(): void { $this->driverMock->expects($this->any()) ->method('getWindowHandles') @@ -410,12 +410,10 @@ public function testShouldDetectNumberOfWindowsToBeCondition() $this->assertTrue(call_user_func($condition->getApply(), $this->driverMock)); } - /** - * @param RemoteWebElement $element - * @param int $expectedNumberOfFindElementCalls - */ - private function setupDriverToReturnElementAfterAnException($element, $expectedNumberOfFindElementCalls) - { + private function setupDriverToReturnElementAfterAnException( + RemoteWebElement $element, + int $expectedNumberOfFindElementCalls + ): void { $consecutiveReturn = [ $this->throwException(new NoSuchElementException('')), ]; diff --git a/tests/unit/WebDriverKeysTest.php b/tests/unit/WebDriverKeysTest.php index 757b7ff99..cfa07308f 100644 --- a/tests/unit/WebDriverKeysTest.php +++ b/tests/unit/WebDriverKeysTest.php @@ -1,4 +1,4 @@ -assertSame($expectedOssOutput, WebDriverKeys::encode($keys)); $this->assertSame($expectedW3cOutput, WebDriverKeys::encode($keys, true)); } @@ -24,7 +25,7 @@ public function testShouldEncodeKeysToFormatOfEachProtocol($keys, $expectedOssOu /** * @return array[] */ - public function provideKeys() + public function provideKeys(): array { return [ 'empty string' => ['', [''], ''], diff --git a/tests/unit/WebDriverOptionsTest.php b/tests/unit/WebDriverOptionsTest.php index 7b4b8f45e..4f51d12b6 100644 --- a/tests/unit/WebDriverOptionsTest.php +++ b/tests/unit/WebDriverOptionsTest.php @@ -1,7 +1,8 @@ -getMock(); } - public function testShouldAddCookieFromArray() + public function testShouldAddCookieFromArray(): void { $cookieInArray = [ 'name' => 'cookieName', @@ -42,7 +43,7 @@ public function testShouldAddCookieFromArray() $options->addCookie($cookieInArray); } - public function testShouldAddCookieFromCookieObject() + public function testShouldAddCookieFromCookieObject(): void { $cookieObject = new Cookie('cookieName', 'someValue'); $cookieObject->setPath('/bar'); @@ -70,18 +71,18 @@ public function testShouldAddCookieFromCookieObject() $options->addCookie($cookieObject); } - public function testShouldNotAllowToCreateCookieFromDifferentObjectThanCookie() + public function testShouldNotAllowToCreateCookieFromDifferentObjectThanCookie(): void { $notCookie = new \stdClass(); $options = new WebDriverOptions($this->executor); - $this->expectException(\InvalidArgumentException::class); + $this->expectException(LogicException::class); $this->expectExceptionMessage('Cookie must be set from instance of Cookie class or from array.'); $options->addCookie($notCookie); } - public function testShouldGetAllCookies() + public function testShouldGetAllCookies(): void { $this->executor->expects($this->once()) ->method('execute') @@ -117,7 +118,7 @@ public function testShouldGetAllCookies() $this->assertSame('secondCookie', $cookies[1]->getName()); } - public function testShouldGetCookieByName() + public function testShouldGetCookieByName(): void { $this->executor->expects($this->once()) ->method('execute') @@ -156,7 +157,7 @@ public function testShouldGetCookieByName() $this->assertTrue($cookie->isSecure()); } - public function testShouldReturnNullIfCookieWithNameNotFound() + public function testShouldReturnNullIfCookieWithNameNotFound(): void { $this->executor->expects($this->once()) ->method('execute') @@ -179,7 +180,7 @@ public function testShouldReturnNullIfCookieWithNameNotFound() $this->assertNull($options->getCookieNamed('notExistingCookie')); } - public function testShouldReturnTimeoutsInstance() + public function testShouldReturnTimeoutsInstance(): void { $options = new WebDriverOptions($this->executor); @@ -187,7 +188,7 @@ public function testShouldReturnTimeoutsInstance() $this->assertInstanceOf(WebDriverTimeouts::class, $timeouts); } - public function testShouldReturnWindowInstance() + public function testShouldReturnWindowInstance(): void { $options = new WebDriverOptions($this->executor); diff --git a/tools/phpstan/composer.json b/tools/phpstan/composer.json new file mode 100644 index 000000000..0ac8efe45 --- /dev/null +++ b/tools/phpstan/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpstan/phpstan": "^1.8" + } +}