[#205] Added selenium-less headless Chrome driver for JavaScript steps.#689
Conversation
Made the JavaScript-dependent traits driver-agnostic so '@javascript' scenarios run through 'dmore/chrome-mink-driver' over the Chrome DevTools Protocol as well as Selenium2. Added a 'chrome_headless' Behat profile and a 'chromedp/headless-shell' service, wired two CI matrix legs that run the suite through the new driver, and documented the setup. Claude-Session: https://claude.ai/code/session_012WhCKmn7GbhNCQgfXmWqXC
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis PR adds a headless Chrome driver path alongside Selenium2, updating CI, Docker, Behat configuration, docs, and helper traits to support CDP-based browser automation and related test flows. ChangesSelenium-less Chrome (CDP) driver support
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant GitHub Actions
participant Matrix
participant ahoy CLI
participant chrome_headless service
GitHub Actions->>Matrix: select matrix.driver = chrome_headless
Matrix->>ahoy CLI: run test-bdd -p chrome_headless
ahoy CLI->>chrome_headless service: connect via CDP api_url
chrome_headless service-->>ahoy CLI: BDD test results
ahoy CLI-->>GitHub Actions: report results
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
composer.json (1)
19-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider
require-dev/suggestinstead of hardrequirefor the CDP extension.Unlike
Selenium2Driver, the CDP-driver support added in this cohort detects the driver viamethod_exists($driver, 'getCookies')duck-typing rather than aninstanceofcheck on a class fromdmore/behat-chrome-extension/dmore/chrome-mink-driver. That means the package isn't needed at runtime by every consumer of this library — only by those opting into thechrome_headlessprofile. Making it a hardrequireforces all consumers to pull in this extra dependency tree, even projects that only use Selenium2 or BrowserKit.♻️ Proposed fix
"require": { "php": ">=8.2", "behat/behat": "^3.14", "behat/mink": ">=1.11", - "dmore/behat-chrome-extension": "^1.4", "drupal/drupal-extension": "^6.0", "lullabot/mink-selenium2-driver": "^1.7.4", "softcreatr/jsonpath": "^0.10 || ^1.0" }, "require-dev": { "alexskrypnyk/phpunit-helpers": "^0.15.0", + "dmore/behat-chrome-extension": "^1.4", "cweagans/composer-patches": "^2.0",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@composer.json` around lines 19 - 24, Move the CDP/chrome extension dependency out of the main package requirements in composer.json and make it optional for consumers who use the chrome_headless profile. Update the dependency declaration around dmore/behat-chrome-extension so it is treated as a dev/suggested integration instead of a hard require, while keeping the rest of the Behat and Drupal extension requirements intact. Ensure any code paths that rely on the CDP driver remain guarded by the existing duck-typing checks in the relevant driver/profile setup..github/workflows/test.yml (1)
219-227: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winCodecov upload runs for the
chrome_headlessleg with no coverage generated.The
chrome_headlessleg matching (php 8.3, drupal 11, normal) skips the coverage-producing BDD step (gated onmatrix.driver != 'chrome_headless'at Lines 184-187), but this Codecovifcondition doesn't excludematrix.driver == 'chrome_headless', so the step still runs and attempts an upload with no fresh coverage data.🛠️ Proposed fix
- if: matrix.php_version == '8.3' && matrix.drupal_version == '11' && matrix.deps == 'normal' && env.CODECOV_TOKEN != '' && !startsWith(github.head_ref, 'deps/') + if: matrix.php_version == '8.3' && matrix.drupal_version == '11' && matrix.deps == 'normal' && matrix.driver != 'chrome_headless' && env.CODECOV_TOKEN != '' && !startsWith(github.head_ref, 'deps/')🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/test.yml around lines 219 - 227, The Codecov upload step in the workflow is still running for the chrome_headless matrix leg even though that leg skips generating coverage, so update the Upload code coverage reports to Codecov condition to also exclude matrix.driver == 'chrome_headless'. Keep the existing gating on php_version, drupal_version, deps, CODECOV_TOKEN, and deps/ branches, but add the driver check so this step only runs when coverage data was actually produced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/test.yml:
- Around line 119-130: The artifact upload in the workflow uses a name that is
no longer unique across matrix jobs, causing collisions when `chrome_headless`
and non-driver legs share the same php_version, drupal_version, and deps values.
Update the upload logic in the test workflow to include matrix.driver in the
artifact name (or another unique matrix field) so each job path under the upload
step remains distinct and the `actions/upload-artifact@v4` step in the test job
can succeed without duplicate-name failures.
---
Outside diff comments:
In @.github/workflows/test.yml:
- Around line 219-227: The Codecov upload step in the workflow is still running
for the chrome_headless matrix leg even though that leg skips generating
coverage, so update the Upload code coverage reports to Codecov condition to
also exclude matrix.driver == 'chrome_headless'. Keep the existing gating on
php_version, drupal_version, deps, CODECOV_TOKEN, and deps/ branches, but add
the driver check so this step only runs when coverage data was actually
produced.
In `@composer.json`:
- Around line 19-24: Move the CDP/chrome extension dependency out of the main
package requirements in composer.json and make it optional for consumers who use
the chrome_headless profile. Update the dependency declaration around
dmore/behat-chrome-extension so it is treated as a dev/suggested integration
instead of a hard require, while keeping the rest of the Behat and Drupal
extension requirements intact. Ensure any code paths that rely on the CDP driver
remain guarded by the existing duck-typing checks in the relevant driver/profile
setup.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f06cf716-5181-4c3d-9fab-814690bcaac4
📒 Files selected for processing (13)
.github/workflows/test.ymlREADME.mdbehat.ymlcomposer.jsondocker-compose.ymlsrc/AccessibilityTrait.phpsrc/CookieTrait.phpsrc/DropzoneTrait.phpsrc/FileDownloadTrait.phpsrc/KeyboardTrait.phpsrc/ResponsiveTrait.phptests/behat/bootstrap/FeatureContextTrait.phptests/behat/features/keyboard.feature
💤 Files with no reviewable changes (1)
- src/ResponsiveTrait.php
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #689 +/- ##
==========================================
+ Coverage 97.27% 97.29% +0.02%
==========================================
Files 52 52
Lines 4404 4439 +35
==========================================
+ Hits 4284 4319 +35
Misses 120 120 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… selenium-less legs. Claude-Session: https://claude.ai/code/session_012WhCKmn7GbhNCQgfXmWqXC
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/test.yml (1)
219-227: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winExclude
chrome_headlessfrom the Codecov upload step.github/workflows/test.yml:219-227
This leg skips the coverage-producing steps, sodirectory: .logs/coveragewill be empty there. Add the samematrix.driver != 'chrome_headless'guard to avoid a no-files Codecov run.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/test.yml around lines 219 - 227, The Codecov upload step in the workflow is still eligible for the chrome_headless matrix leg even though that leg does not produce coverage files, so the upload runs on an empty directory. Update the existing conditional on the Upload code coverage reports to Codecov step to also exclude matrix.driver == 'chrome_headless', alongside the current php_version, drupal_version, deps, token, and branch checks, so the step only runs when .logs/coverage actually exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/test.yml:
- Around line 219-227: The Codecov upload step in the workflow is still eligible
for the chrome_headless matrix leg even though that leg does not produce
coverage files, so the upload runs on an empty directory. Update the existing
conditional on the Upload code coverage reports to Codecov step to also exclude
matrix.driver == 'chrome_headless', alongside the current php_version,
drupal_version, deps, token, and branch checks, so the step only runs when
.logs/coverage actually exists.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 252bd05e-089f-4739-ad18-dbd6b9cad6d7
📒 Files selected for processing (1)
.github/workflows/test.yml
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/test.yml:
- Around line 194-207: The chrome_headless BDD jobs can start before the CDP
endpoint is actually ready, since depends_on only waits for the container to
launch. Add a small readiness check with retries before the two chrome_headless
workflow steps so the jobs wait for http://chrome_headless:9222/json/version to
respond before running ahoy test-bdd-coverage or ahoy test-bdd. Keep the change
scoped to the chrome_headless matrix conditions in the workflow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: beceb0c4-2dbd-4c2f-82ec-527573c6421b
📒 Files selected for processing (1)
.github/workflows/test.yml
… its BDD steps. Claude-Session: https://claude.ai/code/session_012WhCKmn7GbhNCQgfXmWqXC
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/test.yml (1)
174-224: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRepeated condition clauses across steps are a maintainability risk.
The
matrix.driver != 'chrome_headless' && matrix.php_version == '8.3' && matrix.drupal_version == '11' && matrix.deps == 'normal'combination (and its negation) is duplicated across 4if:conditions (Lines 175, 180, 185, 190), plus a similar combination reappears at Line 246 for the Codecov step. A drift between any of these copies (e.g. updating one php/drupal/deps target but not the others) would silently break coverage selection without failing CI.Consider computing a single step output (e.g.
is_coverage_leg) earlier in the job and referencing it in each subsequentif:, reducing the risk of inconsistent copies.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/test.yml around lines 174 - 224, The workflow repeats the same coverage-leg condition in multiple if: clauses, which makes it easy for them to drift. Add a single computed step output or job env flag (for example, a coverage-leg boolean) before the test steps, then reference that shared value in Run Unit tests with coverage, Run Unit tests, Run BDD tests with coverage, Run BDD tests, and the Codecov step so the php_version/drupal_version/deps logic lives in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/test.yml:
- Around line 174-224: The workflow repeats the same coverage-leg condition in
multiple if: clauses, which makes it easy for them to drift. Add a single
computed step output or job env flag (for example, a coverage-leg boolean)
before the test steps, then reference that shared value in Run Unit tests with
coverage, Run Unit tests, Run BDD tests with coverage, Run BDD tests, and the
Codecov step so the php_version/drupal_version/deps logic lives in one place.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 28adc8cc-7662-494b-8eff-caa55d81f37c
📒 Files selected for processing (1)
.github/workflows/test.yml
…nse as ready. Claude-Session: https://claude.ai/code/session_012WhCKmn7GbhNCQgfXmWqXC
…instead of a CI step. Claude-Session: https://claude.ai/code/session_012WhCKmn7GbhNCQgfXmWqXC
Closes #205
Summary
JavaScript-dependent Behat steps previously assumed a Selenium2/WebDriver session, hard-checking
instanceof Selenium2Driverand reaching into WebDriver-specific APIs, so they broke under any other driver. This change makes six traits driver-agnostic and adds a selenium-lesschrome_headlessBehat profile that drives headless Chrome directly over the Chrome DevTools Protocol viadmore/chrome-mink-driver, with no Selenium server in the loop. CI now runs the full suite through both the existing Selenium2 profile and the newchrome_headlessprofile, proving step definitions behave identically on either driver.Changes
Driver-agnostic traits
CookieTrait: reads cookies viagetCookies()when the driver exposes it (CDP-based drivers), alongside the existing WebDriver and BrowserKit branches.FileDownloadTrait: replaced the hardSelenium2Driverinstance check with capability checks -getWebDriverSession()for WebDriver drivers,getCookies()for CDP-based drivers, and the BrowserKit branch as fallback.KeyboardTrait:keyboardTriggerKey()keeps the Syn-library/reflection path forSelenium2Driverand adds a native DevTools key-event path for other JavaScript drivers, mapping special keys (arrows, escape, enter, etc.) to keycodes sent viakeyDown()/keyUp(), and printable characters viakeyPress().keyboardPressKeyOnElementSingle()now only rejects non-JavaScript (BrowserKitDriver) sessions instead of anything that isn'tSelenium2Driver.ResponsiveTrait: removed theSelenium2Driverguards inresponsiveGetCurrentDimensions()andresponsiveResize()so viewport inspection and resizing run under any JavaScript driver.AccessibilityTrait: serializes the axe-core results to a JSON string inside the browser (JSON.stringify(...)) before decoding, since some CDP drivers cannot marshal the raw nested result object back throughevaluateScript().DropzoneTrait: sets the synthetic file input'snameattribute (previously onlyidwas set), which chrome-mink's file upload handling requires to locate the input.Test harness
behat.yml: newchrome_headlessprofile that inherits the default profile and swaps inDMore\ChromeExtensionwithjavascript_session: chromepointed athttp://chrome_headless:9222.docker-compose.yml: newchrome_headlessservice runningchromedp/headless-shell, exposing port 9222 for the DevTools Protocol.composer.json: added thedmore/behat-chrome-extensiondependency (pulls indmore/chrome-mink-driver).tests/behat/bootstrap/FeatureContextTrait.php: added matchinggetCookies()/setCookie()branches to the test harness's own cookie helpers so its@traitscenarios pass under both drivers.tests/behat/features/keyboard.feature: updated the negative-assertion scenario title and expected exception message to reflect the new "JavaScript drivers" wording rather than "Selenium2 driver".CI
.github/workflows/test.yml: added twochrome_headlessmatrix legs (PHP 8.3, Drupal 10 and 11, normal deps) that run the BDD suite through the new driver, with the existing unit/BDD steps guarded onmatrix.driver != 'chrome_headless'so the new legs run only the driver-specific step.chrome_headlessleg runs with coverage (ahoy test-bdd-coverage -- -p chrome_headless) so the selenium-less branches - reachable only through chrome-mink - are measured and merged into the Codecov report alongside the Selenium2 coverage. This keeps patch coverage at 100% (a line covered by either driver counts).matrix.driver, so the new legs stay distinct from the Selenium2 legs at the same PHP/Drupal/deps combination and do not collide on the immutable-artifact-name rule.wait_dependenciesservice now waits on thechrome_headless:9222port (added to itscommand, withchrome_headlessadded to itsdepends_on), so the CDP endpoint is reachable by the time the chrome legs run - reusing the repo's own dependency-wait mechanism rather than a bespoke CI step.Docs
README.md: new "JavaScript drivers" section documenting that@javascriptsteps work with both Selenium/WebDriver and selenium-less CDP drivers, with abehat.ymlsnippet for wiring upDMore\ChromeExtensionand a note on overridingapi_urlfor local visual debugging.Screenshots
N/A
Before / After
Added selenium-less headless Chrome support for JavaScript-dependent Behat steps, including a new
chrome_headlessBehat profile that drives headless Chrome directly via the Chrome DevTools Protocol. Updated Docker and CI to run the suite against both the existing Selenium/WebDriver-based profile and the new CDP-based headless Chrome profile (with dedicated BDD steps forchrome_headlesscoverage vs non-coverage legs), and documented the new "JavaScript drivers" configuration approach. Also added thedmore/behat-chrome-extensiondependency.Driver compatibility was broadened across six updated traits and related test helpers so steps work with Selenium/WebDriver sessions and CDP/Chrome-style drivers:
Updated the keyboard Behat feature to align expectations with the "keyboard interaction is only supported by JavaScript drivers (Selenium2 or Chrome)" constraint.