diff --git a/.coveralls.yml b/.coveralls.yml
new file mode 100644
index 000000000..5ae55a93b
--- /dev/null
+++ b/.coveralls.yml
@@ -0,0 +1,2 @@
+coverage_clover: ./logs/clover.xml
+json_path: ./logs/coveralls-upload.json
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..2fdc4ff88
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,15 @@
+* text=auto
+
+/scripts export-ignore
+/tests export-ignore
+/tools export-ignore
+/.gitattributes export-ignore
+/.gitignore export-ignore
+/example.php export-ignore
+/phpunit.xml.dist export-ignore
+/.github export-ignore
+/.php-cs-fixer.dist.php export-ignore
+/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
new file mode 100644
index 000000000..a79b02b1f
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,94 @@
+# Contributing to php-webdriver
+
+We love to have your help to make php-webdriver better!
+
+Feel free to open an [issue](https://github.com/php-webdriver/php-webdriver/issues) if you run into any problem, or
+send a pull request (see bellow) with your contribution.
+
+## Before you contribute
+
+Do not hesitate to ask for a guidance before you implement notable change, or a new feature - use the associated [issue](https://github.com/php-webdriver/php-webdriver/issues) or use [Discussions](https://github.com/php-webdriver/php-webdriver/discussions).
+Because any new code means increased effort in library maintenance (which is being done by volunteers in their free time),
+please understand not every pull request is automatically accepted. This is why we recommend using the mentioned channels to discuss bigger changes in the source code first.
+
+When you are going to contribute, also please keep in mind that this webdriver client aims to be similar to clients in languages Java/Ruby/Python/C#.
+Here is the [official documentation](https://www.selenium.dev/documentation/en/) and overview of [the official Java API](http://seleniumhq.github.io/selenium/docs/api/java/)
+
+## Workflow when contributing a patch
+
+1. Fork the project on GitHub
+2. Implement your code changes into separate branch
+3. Make sure all PHPUnit tests passes and code-style matches PSR-2 (see below). We also have CI builds which will automatically run tests on your pull request. Make sure to fix any reported issues reported by these automated tests.
+4. When implementing a notable change, fix or a new feature, add record to the Unreleased section of [CHANGELOG.md](../CHANGELOG.md)
+5. Submit your [pull request](https://github.com/php-webdriver/php-webdriver/pulls) against `main` branch
+
+### Run automated code checks
+
+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 all
+```
+
+To run functional tests locally there is some additional setup needed - see below. Without this setup, functional tests will be skipped.
+
+
+For easier development there are also few other prepared commands:
+- `composer fix` - to auto-fix the codestyle and composer.json
+- `composer analyze` - to run only code analysis (without tests)
+- `composer test` - to run all tests
+
+### Unit tests
+
+There are two test-suites: one with **unit tests** only (`unit`), and second with **functional tests** (`functional`),
+which requires running Selenium server and local PHP server.
+
+To execute **all tests** in both suites run:
+
+```sh
+composer test
+```
+
+If you want to execute **just the unit tests**, run:
+
+```sh
+composer test -- --testsuite unit
+```
+
+**Functional tests** are run against a real browser. It means they take a bit longer and also require an additional setup:
+you must first [download](https://www.selenium.dev/downloads/) and start the Selenium standalone server,
+then start the local PHP server which will serve the test pages and then run the `functional` test suite:
+
+```sh
+export BROWSER_NAME="htmlunit" # see below for other browsers
+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
+# Or this to run only functional tests:
+composer test -- --testsuite functional
+```
+
+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
+...
+export BROWSER_NAME="chrome"
+composer all
+```
+
+To test with Firefox/Geckodriver, you must also set `GECKODRIVER` environment variable:
+
+```sh
+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.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/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..e9a259980
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: ❓ Questions and Help
+ url: https://github.com/php-webdriver/php-webdriver/discussions
+ about: Please ask and answer questions here
+ - name: 💡 Ideas and feature requests
+ url: https://github.com/php-webdriver/php-webdriver/discussions
+ about: Suggest an idea for php-webdriver
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
new file mode 100644
index 000000000..d6044861d
--- /dev/null
+++ b/.github/workflows/coveralls-workaround.yaml
@@ -0,0 +1,48 @@
+name: Coveralls coverage
+# Must be run in separate workflow to have access to repository secrets even for PR from forks.
+# See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
+
+permissions:
+ contents: read
+
+on:
+ workflow_run:
+ workflows: [ "Tests" ]
+ types:
+ - completed
+
+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: |
+ 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
new file mode 100644
index 000000000..42baa0000
--- /dev/null
+++ b/.github/workflows/docs-lint.yml
@@ -0,0 +1,26 @@
+name: Lint PHP documentation
+
+permissions:
+ contents: read
+
+on:
+ push:
+ pull_request:
+ branches:
+ - 'main'
+
+jobs:
+ lint-docs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Lint PHP documentation
+ uses: sudo-bot/action-doctum@v5
+ with:
+ config-file: scripts/doctum.php
+ method: 'parse'
+ cli-args: '--output-format=github --no-ansi --no-progress -v --ignore-parse-errors'
diff --git a/.github/workflows/docs-publish.yml b/.github/workflows/docs-publish.yml
new file mode 100644
index 000000000..d0da12f8b
--- /dev/null
+++ b/.github/workflows/docs-publish.yml
@@ -0,0 +1,38 @@
+name: Publish API documentation
+
+permissions:
+ contents: read
+
+on:
+ repository_dispatch:
+ types: [ run-build-api-docs ]
+ workflow_dispatch:
+ schedule:
+ - cron: "00 12 * * *"
+
+jobs:
+ publish-pages:
+ environment:
+ name: API documentation
+ url: https://php-webdriver.github.io/php-webdriver/
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ ssh-key: ${{ secrets.SSH_KEY_DEPLOY }}
+
+ - name: Build PHP documentation
+ uses: sudo-bot/action-doctum@v5
+ with:
+ config-file: scripts/doctum.php
+ method: 'update'
+ cli-args: '--output-format=github --no-ansi --no-progress -v --ignore-parse-errors'
+
+ - name: Set commit author
+ run: |
+ git config user.name "Automated"
+ git config user.email "actions@users.noreply.github.com"
+ - name: Push the changes
+ run: ./scripts/update-built-docs.sh
diff --git a/.github/workflows/no-response.yaml b/.github/workflows/no-response.yaml
new file mode 100644
index 000000000..7147e3792
--- /dev/null
+++ b/.github/workflows/no-response.yaml
@@ -0,0 +1,29 @@
+name: No Response
+
+permissions:
+ issues: write
+
+# Both `issue_comment` and `scheduled` event types are required for this Action to work properly.
+on:
+ issue_comment:
+ types: [created]
+ schedule:
+ - cron: '* */8 * * *' # every hour at :33
+
+jobs:
+ noResponse:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: lee-dohm/no-response@v0.5.0
+ with:
+ token: ${{ github.token }}
+ daysUntilClose: 14
+ responseRequiredLabel: 'waiting for reaction'
+ closeComment: >
+ This issue has been automatically closed because there has been no response
+ to our request for more information from the original author. With only the
+ information that is currently in the issue, we don't have enough information
+ to take action.
+
+ If the original issue author adds comment with more information,
+ this issue will be automatically reopened and we can investigate further.
diff --git a/.github/workflows/sauce-labs.yaml b/.github/workflows/sauce-labs.yaml
new file mode 100644
index 000000000..812fbfe25
--- /dev/null
+++ b/.github/workflows/sauce-labs.yaml
@@ -0,0 +1,74 @@
+name: Sauce Labs
+
+permissions:
+ contents: read
+
+on:
+ push:
+ schedule:
+ - cron: '0 3 * * *'
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ # Source: https://github.community/t/do-not-run-cron-workflows-in-forks/17636/2
+ if: (github.event_name == 'schedule' && github.repository == 'php-webdriver/php-webdriver') || (github.event_name != 'schedule')
+ env:
+ SAUCELABS: 1
+ SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
+ SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
+ strategy:
+ fail-fast: false
+ matrix:
+ 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 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-name }})
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ extensions: mbstring, intl, zip
+ coverage: none
+
+ - name: Install PHP dependencies
+ run: composer update --no-interaction
+
+ - name: Start local PHP server
+ run: |
+ php -S 127.0.0.1:8000 -t tests/functional/web/ &>>./logs/php-server.log &
+
+ - name: Start Sauce Connect
+ uses: saucelabs/sauce-connect-action@v3
+ with:
+ username: ${{ secrets.SAUCE_USERNAME }}
+ accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
+ tunnelName: ${{ matrix.tunnel-name }}
+ proxyLocalhost: allow
+ region: 'us-west-1'
+
+ - name: Run tests
+ env:
+ BROWSER_NAME: ${{ matrix.BROWSER_NAME }}
+ VERSION: ${{ matrix.VERSION }}
+ PLATFORM: ${{ matrix.PLATFORM }}
+ DISABLE_W3C_PROTOCOL: "${{ matrix.w3c && '0' || '1' }}"
+ 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
+ if [ "$BROWSER_NAME" = "firefox" ]; then EXCLUDE_GROUP+="exclude-firefox,"; fi
+ if [ "$BROWSER_NAME" = "chrome" ]; then EXCLUDE_GROUP+="exclude-chrome,"; fi
+ if [ -n "$EXCLUDE_GROUP" ]; then EXTRA_PARAMS+=" --exclude-group $EXCLUDE_GROUP"; fi
+ ./vendor/bin/phpunit --testsuite functional $EXTRA_PARAMS
+
+ - name: Print logs
+ if: ${{ always() }}
+ run: |
+ if [ -f ./logs/php-server.log ]; then cat ./logs/php-server.log; fi
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
new file mode 100644
index 000000000..d215e92ce
--- /dev/null
+++ b/.github/workflows/tests.yaml
@@ -0,0 +1,214 @@
+name: Tests
+
+permissions:
+ contents: read
+
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '0 3 * * *'
+
+jobs:
+ analyze:
+ name: "Code style and static analysis"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ extensions: mbstring, intl, zip
+
+ - name: Install PHP dependencies
+ run: composer update --no-interaction
+
+ - name: Lint
+ run: composer lint
+
+ - name: Run analysis
+ run: composer analyze
+
+ markdown-link-check:
+ name: "Markdown link check"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+ - uses: tcort/github-action-markdown-link-check@v1
+ with:
+ use-verbose-mode: 'yes'
+
+ unit-tests:
+ name: "Unit tests"
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ php-version: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
+ dependencies: ['']
+ include:
+ - { php-version: '7.3', dependencies: '--prefer-lowest' }
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+ extensions: mbstring, intl, zip
+ coverage: xdebug
+ ini-values: ${{ matrix.xdebug-ini-values }}
+
+ - name: Install PHP dependencies
+ run: composer update --no-interaction ${{ matrix.dependencies }}
+
+ - name: Run tests
+ run: vendor/bin/phpunit --testsuite unit --colors=always --coverage-clover ./logs/clover.xml
+
+ - name: Submit coverage to Coveralls
+ # We use php-coveralls library for this, as the official Coveralls GitHub Action lacks support for clover reports:
+ # https://github.com/coverallsapp/github-action/issues/15
+ env:
+ COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ COVERALLS_PARALLEL: true
+ COVERALLS_FLAG_NAME: ${{ github.job }}-PHP-${{ matrix.php-version }} ${{ matrix.dependencies }}
+ run: |
+ composer global require php-coveralls/php-coveralls
+ ~/.composer/vendor/bin/php-coveralls -v
+
+ functional-tests:
+ runs-on: ${{ matrix.os }}
+ env:
+ 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
+ matrix:
+ os: ['ubuntu-latest']
+ browser: ['chrome', 'firefox']
+ selenium-server: [true, false] # Whether to run via Selenium server or directly via browser driver
+ w3c: [true] # Although all builds negotiate protocol by default, it implies W3C protocol for both Chromedriver and Geckodriver
+ include:
+ - { browser: 'safari', os: 'macos-latest', selenium-server: false, w3c: true }
+ # Force OSS (JsonWire) protocol on ChromeDriver - to make sure we keep compatibility:
+ - { browser: 'chrome', os: 'ubuntu-latest', selenium-server: false, w3c: false }
+
+ name: "Functional tests (${{ matrix.browser }}, Selenium server: ${{ matrix.selenium-server }}, W3C: ${{ matrix.w3c }})"
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3'
+ extensions: mbstring, intl, zip
+ coverage: xdebug
+
+ - name: Install PHP dependencies
+ run: composer update --no-interaction
+
+ - name: Start Selenium standalone server
+ # If you want to run your Selenium WebDriver tests on GitHub actions, we recommend using service containers
+ # with eg. selenium/standalone-chrome image. See https://docs.github.com/en/actions/guides/about-service-containers
+ # But for the purpose of testing this library itself, we need more control, so we set everything up manually.
+ if: ${{ matrix.selenium-server }}
+ run: |
+ mkdir -p build logs
+ wget -q -t 3 -O build/selenium-server.jar $SELENIUM_SERVER_DOWNLOAD_URL
+ java -jar build/selenium-server.jar standalone --version
+ xvfb-run --server-args="-screen 0, 1280x720x24" --auto-servernum java -jar build/selenium-server.jar standalone --log logs/selenium-server.log &
+
+ - name: Start ChromeDriver
+ if: ${{ !matrix.selenium-server && matrix.browser == 'chrome' }}
+ run: |
+ google-chrome --version
+ xvfb-run --server-args="-screen 0, 1280x720x24" --auto-servernum \
+ chromedriver --port=4444 &> ./logs/chromedriver.log &
+
+ - name: Start GeckoDriver
+ if: ${{ !matrix.selenium-server && matrix.browser == 'firefox' }}
+ run: |
+ firefox --version
+ geckodriver --version
+ xvfb-run --server-args="-screen 0, 1280x720x24" --auto-servernum \
+ geckodriver &> ./logs/geckodriver.log &
+
+ - name: Start SafariDriver
+ if: ${{ !matrix.selenium-server && matrix.browser == 'safari' }}
+ run: |
+ defaults read /Applications/Safari.app/Contents/Info CFBundleShortVersionString
+ /usr/bin/safaridriver -p 4444 --diagnose &
+
+ - name: Start local PHP server
+ run: |
+ 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/.gitignore b/.gitignore
index 7e4c563c5..a9384110d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,18 @@
composer.phar
composer.lock
vendor
+tools/php-cs-fixer/vendor
+.php_cs.cache
+.php-cs-fixer.cache
+.phpunit.result.cache
+phpunit.xml
+logs/
+build/
# generic files to ignore
*.lock
*.DS_Store
*~
*.swp
+.idea
+.vscode
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 000000000..f754fb8b7
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,139 @@
+notPath('Firefox/FirefoxProfile.php') // need to use str_* instead of mb_str_* methods
+ ->in([__DIR__ . '/lib', __DIR__ . '/tests']);
+
+return (new PhpCsFixer\Config())
+ ->setRules([
+ '@PSR2' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'binary_operator_spaces' => true,
+ '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', '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,
+ '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,
+ 'no_alias_functions' => true,
+ 'no_blank_lines_after_class_opening' => true,
+ 'no_blank_lines_after_phpdoc' => true,
+ 'no_empty_comment' => true,
+ 'no_empty_phpdoc' => true,
+ 'no_empty_statement' => true,
+ 'normalize_index_brace' => true,
+ 'no_extra_blank_lines' => [
+ 'tokens' => [
+ 'break',
+ 'case',
+ 'continue',
+ 'curly_brace_block',
+ 'default',
+ 'extra',
+ 'parenthesis_brace_block',
+ 'return',
+ 'square_brace_block',
+ 'switch',
+ 'throw',
+ 'use',
+ ],
+ ],
+ 'no_leading_import_slash' => true,
+ 'no_leading_namespace_whitespace' => true,
+ 'no_singleline_whitespace_before_semicolons' => 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' => 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' => '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'],
+ 'phpdoc_add_missing_param_annotation' => true,
+ 'phpdoc_indent' => true,
+ 'phpdoc_no_access' => true,
+ // 'phpdoc_no_empty_return' => true, // disabled to allow forward compatibility with PHP 8.1
+ 'phpdoc_no_package' => true,
+ 'phpdoc_order_by_value' => ['annotations' => ['covers', 'group', 'throws']],
+ 'phpdoc_order' => true,
+ 'phpdoc_return_self_reference' => true,
+ '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,
+ 'single_blank_line_before_namespace' => true,
+ 'single_quote' => true,
+ 'single_space_after_construct' => true,
+ 'single_trait_insert_per_statement' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'strict_param' => true,
+ 'switch_continue_to_break' => true,
+ 'ternary_operator_spaces' => true,
+ 'ternary_to_elvis_operator' => true,
+ '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', '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],
+ ])
+ ->setRiskyAllowed(true)
+ ->setFinder($finder);
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..a468c26f1
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,266 @@
+# Changelog
+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 `