[rb] generate the BiDi protocol layer from the shared binding-neutral schema#17731
[rb] generate the BiDi protocol layer from the shared binding-neutral schema#17731titusfortner wants to merge 13 commits into
Conversation
|
Thank you, @titusfortner for this code suggestion. The support packages contain example code that many users find helpful, but they do not necessarily represent After reviewing the change, unless it is a critical fix or a feature that is needed for Selenium We actively encourage people to add the wrapper and helper code that makes sense for them to their own frameworks. |
PR Summary by Qodo[rb] Generate Ruby BiDi protocol layer from shared schema
AI Description
Diagram
High-Level Assessment
Files changed (44)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
19 rules 1.
|
…dation into build
There was a problem hiding this comment.
Pull request overview
Adds an auto-generated, binding-neutral Ruby BiDi protocol layer (plus runtime support) based on the shared BiDi schema so future Ruby BiDi work can build on a consistent, spec-aligned foundation without re-deriving types per domain.
Changes:
- Introduces a Bazel-driven Ruby generator + templates to emit Ruby protocol modules and matching RBS from the shared BiDi schema.
- Adds a small BiDi serialization runtime (
Data,Union,UNSET, outbound enum validation) and aTransportseam to send commands + parse typed results. - Adds unit specs validating round-trip JSON behavior, union dispatch, enum validation, and transport error handling; updates RuboCop/Steep config to accommodate generated/additive code.
Reviewed changes
Copilot reviewed 44 out of 44 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| rb/Steepfile | Narrows Steep ignores to only the remaining hand-written BiDi files while generated code lands. |
| rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb | Adds unit coverage for Transport serialization/result parsing/error raising. |
| rb/spec/unit/selenium/webdriver/bidi/support/bidi_generate_spec.rb | Adds unit coverage for generator naming helpers (camel_to_snake, enum_key). |
| rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb | Adds unit coverage for generated types: (de)serialization, unions, enums, extensible records, and command/event wiring. |
| rb/sig/lib/selenium/webdriver/bidi/transport.rbs | Adds RBS for the new BiDi::Transport API surface. |
| rb/sig/lib/selenium/webdriver/bidi/serialization.rbs | Adds RBS for the BiDi serialization runtime (UNSET, Data, Union, validation). |
| rb/sig/lib/selenium/webdriver/bidi/protocol/web_extension.rbs | Generated RBS for the webExtension domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rbs | Generated RBS for the userAgentClientHints domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/storage.rbs | Generated RBS for the storage domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/speculation.rbs | Generated RBS for the speculation domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/session.rbs | Generated RBS for the session domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/permissions.rbs | Generated RBS for the permissions domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/network.rbs | Generated RBS for the network domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/log.rbs | Generated RBS for the log domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/input.rbs | Generated RBS for the input domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/emulation.rbs | Generated RBS for the emulation domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/browsing_context.rbs | Generated RBS for the browsingContext domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/browser.rbs | Generated RBS for the browser domain protocol surface. |
| rb/sig/lib/selenium/webdriver/bidi/protocol/bluetooth.rbs | Generated RBS for the bluetooth domain protocol surface. |
| rb/lib/selenium/webdriver/BUILD.bazel | Adds a rb_binary target to run the generator via bazel run //rb/lib/selenium/webdriver:bidi-generate. |
| rb/lib/selenium/webdriver/bidi/transport.rb | Adds BiDi::Transport for serializing params, sending commands, and parsing typed results. |
| rb/lib/selenium/webdriver/bidi/support/templates/module.rbs.erb | Adds the generator template for emitting per-domain RBS. |
| rb/lib/selenium/webdriver/bidi/support/templates/module.rb.erb | Adds the generator template for emitting per-domain Ruby protocol code. |
| rb/lib/selenium/webdriver/bidi/serialization/union.rb | Adds Serialization::Union runtime for discriminator/presence-based union dispatch (+ forward-compatible fallback behavior). |
| rb/lib/selenium/webdriver/bidi/serialization/data.rb | Adds Serialization::Data runtime for immutable records with wire-name mapping and JSON round-tripping. |
| rb/lib/selenium/webdriver/bidi/serialization.rb | Adds UNSET sentinel + outbound enum validation and wires in the Data/Union runtime. |
| rb/lib/selenium/webdriver/bidi/protocol/web_extension.rb | Generated Ruby protocol code for the webExtension domain. |
| rb/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb | Generated Ruby protocol code for the userAgentClientHints domain. |
| rb/lib/selenium/webdriver/bidi/protocol/storage.rb | Generated Ruby protocol code for the storage domain. |
| rb/lib/selenium/webdriver/bidi/protocol/speculation.rb | Generated Ruby protocol code for the speculation domain. |
| rb/lib/selenium/webdriver/bidi/protocol/session.rb | Generated Ruby protocol code for the session domain. |
| rb/lib/selenium/webdriver/bidi/protocol/permissions.rb | Generated Ruby protocol code for the permissions domain. |
| rb/lib/selenium/webdriver/bidi/protocol/log.rb | Generated Ruby protocol code for the log domain. |
| rb/lib/selenium/webdriver/bidi/protocol/input.rb | Generated Ruby protocol code for the input domain. |
| rb/lib/selenium/webdriver/bidi/protocol/emulation.rb | Generated Ruby protocol code for the emulation domain. |
| rb/lib/selenium/webdriver/bidi/protocol/browsing_context.rb | Generated Ruby protocol code for the browsingContext domain. |
| rb/lib/selenium/webdriver/bidi/protocol/browser.rb | Generated Ruby protocol code for the browser domain. |
| rb/lib/selenium/webdriver/bidi/protocol/bluetooth.rb | Generated Ruby protocol code for the bluetooth domain. |
| rb/lib/selenium/webdriver/bidi/protocol.rb | Adds the domain require list to load the generated protocol surface. |
| rb/.rubocop.yml | Excludes generated BiDi protocol code (and generator) from select complexity/naming cops to keep CI signal focused. |
|
Code review by qodo was updated up to the latest commit 45c6cd3 |
|
Code review by qodo was updated up to the latest commit c155ac0 |
|
Code review by qodo was updated up to the latest commit 64e9f1a |
|
Code review by qodo was updated up to the latest commit 2fdaa9c |
|
Code review by qodo was updated up to the latest commit d5372e8 |
|
Code review by qodo was updated up to the latest commit 88ef296 |
|
Code review by qodo was updated up to the latest commit 2adb4f7 |
| # under the License. | ||
|
|
||
| # This file is automatically generated. DO NOT EDIT! | ||
| # Regenerate with: bazel run //rb/lib/selenium/webdriver:bidi-generate |
There was a problem hiding this comment.
Can we add a check on PRs in case someone edits a file they shouldn't to trigger and validates that the files are not edited?
| socket_url = @capabilities[:web_socket_url] | ||
| @bidi = Selenium::WebDriver::BiDi.new(url: socket_url) | ||
| # Share the BiDi object's socket until the bridge owns the connection directly. | ||
| @transport = BiDi::Transport.new(@bidi.ws) |
There was a problem hiding this comment.
1. Bidi load-order failure 🐞 Bug ☼ Reliability
Remote::BiDiBridge#create_session constructs BiDi::Transport from @bidi.ws, but bidi/transport.rb can define Selenium::WebDriver::BiDi without initialize(url:) or #ws when this bridge file is loaded without selenium/webdriver (which sets the BiDi autoload). In that require order, Selenium::WebDriver::BiDi.new(url: ...) raises and the session cannot be created.
Agent Prompt
### Issue description
`rb/lib/selenium/webdriver/remote/bidi_bridge.rb` now requires `selenium/webdriver/bidi/transport` and then calls `Selenium::WebDriver::BiDi.new(url: ...)` and `@bidi.ws`. If this file is required without first loading `selenium/webdriver` (which installs `autoload :BiDi, 'selenium/webdriver/bidi'`), then `bidi/transport.rb` may define `Selenium::WebDriver::BiDi` as an empty class (only adding `Transport`), so `BiDi.new(url: ...)` and/or `#ws` will be missing.
### Issue Context
The bridge should be robust to load order, especially when internal files are required directly (e.g., by tooling/tests or alternative boot paths).
### Fix Focus Areas
- rb/lib/selenium/webdriver/remote/bidi_bridge.rb[20-34]
### Suggested fix
Ensure `bidi.rb` is loaded before instantiating `Selenium::WebDriver::BiDi` / accessing `#ws`:
- Add `require 'selenium/webdriver/bidi'` (or `require 'selenium/webdriver'`) in `remote/bidi_bridge.rb` before using `Selenium::WebDriver::BiDi`.
Alternative (broader) fix:
- Make `rb/lib/selenium/webdriver/bidi/transport.rb` require `selenium/webdriver/bidi` so `BiDi` is fully defined regardless of load order.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 1afbccd |
🔗 Related Issues
💥 What does this PR do?
Generates the full typed Ruby BiDi protocol layer from the shared schema (all 14 domains, 77 commands, 27 events), plus the hand-written seam (
Protocol::Domainsuperclass + a thinTransport) that makes it usable. With a BiDi-enabled driver, users can now reach the entire BiDi spec directly:Nothing in Selenium consumes it yet — current
driveroperations still run the existing hand-written BiDi paths. To show the generated surface works, the hand-written integration specs have been re-implemented against this generated code for a side-by-side comparison. The generated API mirrors the spec's command names and shapes, exchanging typed objects instead of raw hashes:🔧 Implementation Notes
Generated code is updated with
bazel run //rb/lib/selenium/webdriver:bidi-generate, which consumes the shared schema produced by//javascript/selenium-webdriver:create-bidi-src_schema(#17700). The generator is a straight schema→code projection — the CDDL interpretation and its fidelity check live upstream in that projector, not here.bidi/supportbidi/protocoland subclasses the hand-writtenProtocol::Domainbidi/serializationRecord: base class for every generated value type, an immutableRecord.definevalue object (a::Datasubclass) that knows its field-to-wire-name mapping and round-trips to/from JSON, distinguishing omitted from explicit null via anUNSETsentinel. Enum-valued fields are idiomatic snake_case symbols (:before_request_sent), mapped to/from their wire tokens ('beforeRequestSent') on serialize/parse — symbol-in, symbol-out.Union: base class for a "one of N records" type, that selects the variant from the schema's selector; callers only ever hold the resolvedRecord.Protocol::Domain.newaccepts aDriver(reusing the session's transport) or aTransportdirectly; a driver without BiDi enabled raises the standard "enableweb_socket_url" error.Transportis socket-only and stays a thin pipe: send/receive over the connection plus generic error handling.@api privatebrowser.getUserContexts→get_user_contexts,sameSite→same_site), so the layer reads as the spec does and stays comparable across bindings. Ruby-idiomatic naming (e.g. droppingget_) is deliberately left to the hand-written public wrappers that will consume this layer, not baked into the generator.Validation. Two axes: we own what we send (outbound), the browser owns what it sends (inbound); and for inbound, strict on values but lenient on keys.
ArgumentError, not a round-trip.🤖 AI assistance
Domainsuperclass +Transport, thegenerated protocol modules + RBS, and the unit + integration specs
💡 Additional Considerations
Remote::BiDiBridge,WebDriver::Script, andBiDi::Networkagainst the generated layer instead of the current BiDi classes.🔄 Types of changes