Cuprite support#157
Conversation
|
@seanpdoyle thanks for your feedback and apologies for dropping the ball! I'd still love to see Cuprite support. I'll see if I can address the various comments in the next week or so. |
58bb24e to
88d327c
Compare
There was a problem hiding this comment.
Pull request overview
Adds initial support for running this project’s Capybara accessible selectors against the Cuprite (Ferrum/CDP) driver by introducing Cuprite-based computed accessibility queries and wiring Cuprite into the test suite.
Changes:
- Add Cuprite driver registration in the RSpec spec helper and introduce driver-based skips for known-incompatible specs.
- Implement Cuprite-backed
accessible_name,accessible_description, androleresolution using a newAccessibilityComputedValuehelper. - Update rich text filling to avoid sending
nil/empty values, and add Cuprite to the bundle.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/spec_helper.rb | Optionally requires Cuprite and registers a Cuprite headless driver for specs. |
| spec/selectors/rich_text_spec.rb | Skips specific rich-text behavior tests for Cuprite. |
| spec/selectors/disclosure_spec.rb | Skips specific disclosure toggle/select tests for Cuprite. |
| spec/capybara/node/element/accessible_description_spec.rb | Skips specific accessible-description expectations for Cuprite. |
| lib/capybara_accessible_selectors/selectors/rich_text.rb | Avoids sending nil text and removes Selenium platform dependency for modifier key detection. |
| lib/capybara_accessible_selectors/rspec/matchers/have_validation_errors.rb | Adjusts outerHTML retrieval to support drivers without attribute('outerHTML'). |
| lib/capybara_accessible_selectors/node/role.rb | Adds Cuprite-specific role resolution. |
| lib/capybara_accessible_selectors/node/accessible_name.rb | Adds Cuprite-specific accessible name resolution. |
| lib/capybara_accessible_selectors/node/accessible_description.rb | Adds Cuprite-specific accessible description resolution. |
| lib/capybara_accessible_selectors/node.rb | Includes Cuprite node extensions when Cuprite is already loaded. |
| lib/capybara_accessible_selectors/cuprite/accessibility_computed_value.rb | New helper to query computed accessibility values via CDP. |
| Gemfile / Gemfile.lock | Adds Cuprite dependency and updates the lockfile. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
89ae084 to
810bde3
Compare
| # So we can't just use backspace | ||
| input.send_keys [command_modifier, "a"], :backspace if input.text != "" && clear | ||
| input.send_keys with | ||
| input.send_keys with if with |
There was a problem hiding this comment.
Note to self: would be good to fix upstream!
| begin | ||
| require "capybara/cuprite" | ||
| rescue LoadError | ||
| # Cuprite is optional | ||
| end |
There was a problem hiding this comment.
I'm not sure if this is necessary - it might be ok to make Cuprite (and all Capybara drivers) test dependencies.
810bde3 to
88f299b
Compare
| # The accname-1.2 description algorithm is driver-agnostic (it only uses | ||
| # the generic Capybara node API), so reuse it instead of Chrome's raw | ||
| # computed description, keeping results consistent with Selenium | ||
| Selenium::AccessibleDescription.resolve(self) || "" |
There was a problem hiding this comment.
N.B. we might consider renaming this module to Capybara::AccessibleDescription
88f299b to
0e200c8
Compare
Add Cuprite (Chrome DevTools Protocol) as an alternative browser driver. Uses Chrome's Accessibility.getPartialAXTree CDP command to resolve accessible name, description, and role values. - Add CupriteNodeExtensions with defined? guard for optional dependency - Add AccessibilityComputedValue resolver for CDP accessibility tree - Handle Chrome's non-standard PascalCase role identifiers - Guard Cuprite require/registration in spec_helper - Use RUBY_PLATFORM for platform detection (no hard Selenium dependency) - Use evaluate_script for outerHTML access across drivers - Add send_keys nil guards for rich text Fixes citizensadvice#156
Annotate specs with skip_driver: :cuprite_chrome where Cuprite's send_keys handling of modifier keys differs from Selenium, or where the <details> element interaction behaves differently. - 2 disclosure toggle/select specs (details element interaction) - 8 rich text fill/replace/within specs (modifier key handling)
details[:open] is a boolean attribute whose raw value differs per driver (Selenium returns "true"/nil, Cuprite returns true/false). The summary toggle compared it against the desired state as a string, so on Cuprite a re-toggle of an already-open disclosure misfired and closed it. Normalise the value before comparing, and un-skip the two disclosure specs that exercised re-toggling.
fill_in_rich_text cleared existing content with a select-all chord (mod+a) followed by backspace. Cuprite/Ferrum cannot reliably trigger a select-all chord inside a contenteditable, so the clear was a no-op and new text was appended instead of replacing. For Cuprite, move the caret to the end and backspace through the content instead (matching the approach used elsewhere for Ferrum); keep the chord for Selenium. Un-skip the eight rich text specs that exercised clearing or replacing content.
4be2e5b to
8442774
Compare
|
@seanpdoyle please give this a look when you get a chance! With the help of Claude I was able to improve robustness and work through the remaining skipped specs. All specs should now pass on Cuprite. |
Summary
Adds Cuprite (Ferrum/CDP-based headless Chrome) as a supported Capybara driver alongside Selenium and rack_test.
The accessibility methods (
accessible_name,accessible_description,role) and the:roleselector previously only worked under Selenium and rack_test. This wires up a Cuprite path for each, resolving computed values from Chrome's accessibility tree via CDP (Accessibility.getPartialAXTree), and fixes a couple of driver-behaviour differences that affected the disclosure and rich-text selectors.All existing specs run green on Cuprite — there are no Cuprite-skipped specs.
What's included
Driver wiring
Cuprite::AccessibilityComputedValueresolves a node's computed name/role/description from the CDP accessibility tree.accessible_name/roleread those computed values;accessible_descriptionreuses the existing accname-1.2 description algorithm (it only depends on the generic Capybara node API, so it is driver-agnostic) — this keeps description results consistent with Selenium rather than returning Chrome's raw value.Cross-driver behaviour fixes
details[:open]is a boolean attribute whose raw value differs per driver (Selenium"true"/nil, Cupritetrue/false). The summary toggle now normalises it before comparing, so re-toggling an already-open disclosure no longer misfires under Cuprite.contenteditable, sofill_in_rich_text's clear was a no-op and text was appended instead of replaced. Under Cuprite it now moves the caret to the end and backspaces through the content; Selenium keeps the faster chord.accessible_namecollapses whitespace to match the Selenium path.Tooling / docs
DRIVER=cuprite_chrome_headless) in addition to Chromium, Firefox, and rack_test.capybara_accessible_selectorsfor the extensions to be applied.Load-order requirement
Because Cuprite is optional, its extensions are only installed if
Capybara::Cuprite::Nodeis already defined at require time:This is documented in the README. A lazy/registration-based hook to remove the ordering constraint was considered but intentionally left out of scope.
Test plan
bundle exec rspec(default Selenium Chromium driver) — 0 failuresDRIVER=selenium_headless bundle exec rspec(Firefox) — 0 failuresDRIVER=cuprite_chrome_headless bundle exec rspec— 0 failures, 0 skipsDRIVER=rack_test bundle exec rspec— 0 failuresbundle exec rubocopUpstream Ferrum API (follow-up)
The Cuprite path currently reaches into raw CDP (
Accessibility.getPartialAXTree) because Ferrum exposes no public accessibility API. rubycdp/ferrum#595 adds a first-classpage.accessibilitydomain plus anode.axnodeconvenience returning anAXNodevalue object (name/role/description).Validated locally against that branch:
Cuprite::AccessibilityComputedValuecollapses tonode.axnode&.public_send(name), dropping the raw-CDP coupling, ignored-node filtering, and hash-digging. Full suite stays green (1813 examples, 0 failures).Once #595 ships in a Ferrum release, this PR's resolver can be simplified and the dependency constraint bumped as a follow-up.