Skip to content

[rb] generate the BiDi protocol layer from the shared binding-neutral schema#17731

Open
titusfortner wants to merge 13 commits into
trunkfrom
bidi-gen-rb
Open

[rb] generate the BiDi protocol layer from the shared binding-neutral schema#17731
titusfortner wants to merge 13 commits into
trunkfrom
bidi-gen-rb

Conversation

@titusfortner

@titusfortner titusfortner commented Jun 30, 2026

Copy link
Copy Markdown
Member

🔗 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::Domain superclass + a thin Transport) that makes it usable. With a BiDi-enabled driver, users can now reach the entire BiDi spec directly:

Selenium::WebDriver::BiDi::Protocol::BrowsingContext.new(driver).create(type: :tab)

Nothing in Selenium consumes it yet — current driver operations 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:

# hand-written: the result is a raw Hash, keyed by wire name
id = browser.create_user_context['userContext']
all_ids = browser.user_contexts['userContexts'].map { |c| c['userContext'] }

# generated: the result is a typed object with snake_case accessors
id = browser.create_user_context.user_context
all_ids = browser.get_user_contexts.user_contexts.map(&:user_context)
# hand-written: flat width/height, implicit context
browsing_context.set_viewport(width: 800, height: 600, device_pixel_ratio: 2.0)

# generated: mirrors browsingContext.setViewport — explicit context and a typed viewport object
browsing_context.set_viewport(
  context: window_handle,
  viewport: BrowsingContext::Viewport.new(width: 800, height: 600),
  device_pixel_ratio: 2.0
)

🔧 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.

  • Generator and template code lives in bidi/support
  • Generated code lives in bidi/protocol and subclasses the hand-written Protocol::Domain
  • Serialization code is in bidi/serialization
    • Record: base class for every generated value type, an immutable Record.define value object (a ::Data subclass) that knows its field-to-wire-name mapping and round-trips to/from JSON, distinguishing omitted from explicit null via an UNSET sentinel. 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 resolved Record.
  • Protocol::Domain.new accepts a Driver (reusing the session's transport) or a Transport directly; a driver without BiDi enabled raises the standard "enable web_socket_url" error. Transport is socket-only and stays a thin pipe: send/receive over the connection plus generic error handling.
  • Generated rb files have corresponding generated rbs files with complete definitions
  • The generated layer is tagged @api private
  • Method and field names are a mechanical projection of the spec (browser.getUserContextsget_user_contexts, sameSitesame_site), so the layer reads as the spec does and stays comparable across bindings. Ruby-idiomatic naming (e.g. dropping get_) 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.

  • Outbound (commands): strict. Enum membership (symbols), required fields, no unknown keys — validated before the wire call, so a mistake is a local ArgumentError, not a round-trip.
  • Inbound (results and events): strict on values, lenient on keys. An unrecognized enum token or union variant raises; extra/unknown properties are accepted (the spec mandates it). So a browser that adds a property never breaks parsing, while one that sends a value outside our schema fails loud instead of passing an untyped value through.

🤖 AI assistance

  • AI assisted (complete below)
    • Tool(s): Claude Code
    • What was generated: the generator, serialization runtime, Domain superclass + Transport, the
      generated protocol modules + RBS, and the unit + integration specs
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

  • Consider removing files from version control and keeping them build-time only; (seems easier for now to do implementations with the code checked in and directly visible).
  • Next step is to implement the commands on Remote::BiDiBridge, WebDriver::Script, and BiDi::Network against the generated layer instead of the current BiDi classes.
  • Consider making inbound parsing more lenient; we will likely need to figure out how to make it more lenient conditionally as browsers are likely to have incompatible syntax, and we may not want to completely break whenever they do.

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

@selenium-ci selenium-ci added C-rb Ruby Bindings B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related B-support Issue or PR related to support classes labels Jun 30, 2026
@selenium-ci

Copy link
Copy Markdown
Member

Thank you, @titusfortner for this code suggestion.

The support packages contain example code that many users find helpful, but they do not necessarily represent
the best practices for using Selenium, and the Selenium team is not currently merging changes to them.

After reviewing the change, unless it is a critical fix or a feature that is needed for Selenium
to work, we will likely close the PR.

We actively encourage people to add the wrapper and helper code that makes sense for them to their own frameworks.
If you have any questions, please contact us

Comment thread rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb Dismissed
Comment thread rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb Dismissed
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

[rb] Generate Ruby BiDi protocol layer from shared schema

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add Ruby generator projecting shared BiDi schema into Ruby protocol modules + RBS.
• Introduce serialization runtime (Data/Union, UNSET) and a Transport execution seam.
• Check in generated protocol for 14 domains with unit coverage for round-trips/dispatch/validation.
Diagram

graph TD
  A[("Shared schema JSON")] --> B["Ruby generator"] --> D["Generated protocol + RBS"] --> F["Transport"] --> G["WebSocket connection"]
  C[/"ERB templates"/] --> B
  E["Serialization runtime"] --> D
  E --> F
  subgraph Legend
    direction LR
    _db[("Artifact")] ~~~ _proc["Code/runtime"] ~~~ _tpl[/"Template"/]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Build-time generation only (do not check in generated protocol)
  • ➕ Avoids large generated diffs and merge conflicts
  • ➕ Ensures protocol always matches schema at build time
  • ➕ Reduces repository churn
  • ➖ Harder local iteration/debugging without Bazel context
  • ➖ Tooling/packaging needs to ensure generated files are available for consumers
  • ➖ May complicate release workflows for RubyGems consumers
2. Use a shared cross-language generator (single implementation)
  • ➕ Reduces duplicated generator logic across bindings
  • ➕ Centralizes schema-to-language projection rules
  • ➖ Higher upfront coordination and build integration cost
  • ➖ Ruby-specific naming/UNSET/nullability semantics may still require custom hooks
3. Lean runtime + looser typing (hash-based protocol, minimal value types)
  • ➕ Smaller surface area and less generated code
  • ➕ Potentially faster to iterate early
  • ➖ Loses null-vs-omitted fidelity without a structured layer
  • ➖ Weaker RBS signal and fewer correctness checks (e.g., enum validation)
  • ➖ Harder to evolve into a robust API later

Recommendation: The current approach (checked-in generated protocol + small serialization runtime + Transport seam) is a good foundation because it preserves wire fidelity (UNSET vs nil), supports union dispatch, and provides RBS for editor/type tooling. Consider switching to build-time-only generation later once downstream integration stabilizes and release/packaging implications are resolved.

Files changed (44) +8652 / -2

Enhancement (38) +8246 / -0
protocol.rbAdd protocol entrypoint requiring serialization and all generated domains +36/-0

Add protocol entrypoint requiring serialization and all generated domains

• Defines the load order for the generated protocol by requiring the serialization runtime first, then requiring each generated domain module.

rb/lib/selenium/webdriver/bidi/protocol.rb

bluetooth.rbAdd generated Bluetooth domain protocol module +442/-0

Add generated Bluetooth domain protocol module

• Adds generated Bluetooth commands/events/types/enums using the shared BiDi schema and the Serialization runtime.

rb/lib/selenium/webdriver/bidi/protocol/bluetooth.rb

browser.rbAdd generated Browser domain protocol module +215/-0

Add generated Browser domain protocol module

• Adds generated Browser commands and associated structured types/enums, exposing typed result parsing hooks via Transport.

rb/lib/selenium/webdriver/bidi/protocol/browser.rb

browsing_context.rbAdd generated BrowsingContext domain protocol module +591/-0

Add generated BrowsingContext domain protocol module

• Adds generated BrowsingContext commands/events plus structured value objects and union dispatch tables for parsing/serialization.

rb/lib/selenium/webdriver/bidi/protocol/browsing_context.rb

emulation.rbAdd generated Emulation domain protocol module +334/-0

Add generated Emulation domain protocol module

• Adds generated Emulation commands/types and union-aware parameter construction for correct null-vs-omitted serialization.

rb/lib/selenium/webdriver/bidi/protocol/emulation.rb

input.rbAdd generated Input domain protocol module +271/-0

Add generated Input domain protocol module

• Adds generated Input commands/types including schema-driven unions that can dispatch on discriminators or structural presence rules.

rb/lib/selenium/webdriver/bidi/protocol/input.rb

log.rbAdd generated Log domain protocol module +108/-0

Add generated Log domain protocol module

• Adds generated Log events/types and enums, relying on trusted inbound parsing to remain forward-compatible with new browser values.

rb/lib/selenium/webdriver/bidi/protocol/log.rb

network.rbAdd generated Network domain protocol module +618/-0

Add generated Network domain protocol module

• Adds generated Network commands/events/types and enums, including outbound enum validation and typed result parsing support.

rb/lib/selenium/webdriver/bidi/protocol/network.rb

permissions.rbAdd generated Permissions domain protocol module +77/-0

Add generated Permissions domain protocol module

• Adds generated Permissions commands/types/enums derived from the shared schema.

rb/lib/selenium/webdriver/bidi/protocol/permissions.rb

script.rbAdd generated Script domain protocol module +758/-0

Add generated Script domain protocol module

• Adds generated Script commands/events/types, including recursive structured types and discriminator-based union dispatch.

rb/lib/selenium/webdriver/bidi/protocol/script.rb

session.rbAdd generated Session domain protocol module +225/-0

Add generated Session domain protocol module

• Adds generated Session commands/types and event maps for method-to-type dispatch.

rb/lib/selenium/webdriver/bidi/protocol/session.rb

speculation.rbAdd generated Speculation domain protocol module +60/-0

Add generated Speculation domain protocol module

• Adds generated Speculation commands/types/enums from the shared schema.

rb/lib/selenium/webdriver/bidi/protocol/speculation.rb

storage.rbAdd generated Storage domain protocol module +158/-0

Add generated Storage domain protocol module

• Adds generated Storage commands/types/enums derived from the shared schema.

rb/lib/selenium/webdriver/bidi/protocol/storage.rb

user_agent_client_hints.rbAdd generated UserAgentClientHints domain protocol module +79/-0

Add generated UserAgentClientHints domain protocol module

• Adds generated UserAgentClientHints commands/types/enums and schema-driven parameter structures.

rb/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb

web_extension.rbAdd generated WebExtension domain protocol module +88/-0

Add generated WebExtension domain protocol module

• Adds generated WebExtension commands/types/enums from the shared schema.

rb/lib/selenium/webdriver/bidi/protocol/web_extension.rb

serialization.rbIntroduce BiDi serialization runtime entrypoint (UNSET + validation) +55/-0

Introduce BiDi serialization runtime entrypoint (UNSET + validation)

• Defines the UNSET sentinel for omitted fields and outbound enum validation, then requires the Data and Union implementations.

rb/lib/selenium/webdriver/bidi/serialization.rb

data.rbAdd Data.define immutable value base with JSON round-trip semantics +153/-0

Add Data.define immutable value base with JSON round-trip semantics

• Implements the generated value-object base, including field metadata, extensible key capture, UNSET omission, and trusted inbound parsing. Adds outbound enum validation at construction time while leaving inbound 'from_json' lenient for forward compatibility.

rb/lib/selenium/webdriver/bidi/serialization/data.rb

union.rbAdd Union base for schema-driven discriminated/structural union dispatch +86/-0

Add Union base for schema-driven discriminated/structural union dispatch

• Implements union parsing and outbound variant selection via discriminator tables, presence rules, and optional fallback variants, returning raw scalars unchanged when unions include scalar arms.

rb/lib/selenium/webdriver/bidi/serialization/union.rb

bidi_generate.rbAdd Ruby code generator projecting shared BiDi schema into Ruby + RBS +866/-0

Add Ruby code generator projecting shared BiDi schema into Ruby + RBS

• Implements schema projection, naming safety (keywords/core method collisions), union/record IR building, enum constant emission, and ERB rendering with line-length aware formatting. Emits both Ruby protocol modules and matching RBS signatures, and includes guardrails for union dispatch keys and correlated unions.

rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb

module.rb.erbAdd ERB template for generated Ruby domain modules +113/-0

Add ERB template for generated Ruby domain modules

• Defines the structure of each generated domain: EVENTS map, enums, Data/Union types, EVENT_TYPES mapping, and command methods that call Transport with enum validation and parameter building.

rb/lib/selenium/webdriver/bidi/support/templates/module.rb.erb

module.rbs.erbAdd ERB template for generated RBS signatures +79/-0

Add ERB template for generated RBS signatures

• Generates RBS for domain modules, including typed attr_readers for Data subclasses and typed command method signatures aligned with the schema.

rb/lib/selenium/webdriver/bidi/support/templates/module.rbs.erb

transport.rbAdd Transport skeleton to execute commands and parse typed results +58/-0

Add Transport skeleton to execute commands and parse typed results

• Adds a Transport wrapper that serializes params (dropping nil/UNSET appropriately), calls the WebSocket connection, raises on error replies, and optionally parses results via 'from_json' into declared result types.

rb/lib/selenium/webdriver/bidi/transport.rb

bluetooth.rbsAdd generated RBS for Bluetooth domain +244/-0

Add generated RBS for Bluetooth domain

• Provides Steep/RBS type signatures for generated Bluetooth protocol classes, types, and command methods.

rb/sig/lib/selenium/webdriver/bidi/protocol/bluetooth.rbs

browser.rbsAdd generated RBS for Browser domain +116/-0

Add generated RBS for Browser domain

• Provides RBS signatures for Browser protocol commands and structured types.

rb/sig/lib/selenium/webdriver/bidi/protocol/browser.rbs

browsing_context.rbsAdd generated RBS for BrowsingContext domain +368/-0

Add generated RBS for BrowsingContext domain

• Provides RBS signatures for BrowsingContext protocol commands/events and value objects.

rb/sig/lib/selenium/webdriver/bidi/protocol/browsing_context.rbs

emulation.rbsAdd generated RBS for Emulation domain +167/-0

Add generated RBS for Emulation domain

• Provides RBS signatures for Emulation protocol commands and types, including union parameter builders.

rb/sig/lib/selenium/webdriver/bidi/protocol/emulation.rbs

input.rbsAdd generated RBS for Input domain +197/-0

Add generated RBS for Input domain

• Provides RBS signatures for Input protocol commands and structured types/unions.

rb/sig/lib/selenium/webdriver/bidi/protocol/input.rbs

log.rbsAdd generated RBS for Log domain +82/-0

Add generated RBS for Log domain

• Provides RBS signatures for Log protocol types and APIs.

rb/sig/lib/selenium/webdriver/bidi/protocol/log.rbs

network.rbsAdd generated RBS for Network domain +405/-0

Add generated RBS for Network domain

• Provides RBS signatures for Network protocol commands/events and associated value objects.

rb/sig/lib/selenium/webdriver/bidi/protocol/network.rbs

permissions.rbsAdd generated RBS for Permissions domain +49/-0

Add generated RBS for Permissions domain

• Provides RBS signatures for Permissions protocol commands and types.

rb/sig/lib/selenium/webdriver/bidi/protocol/permissions.rbs

script.rbsAdd generated RBS for Script domain +542/-0

Add generated RBS for Script domain

• Provides RBS signatures for Script protocol commands/events and nested/recursive value types.

rb/sig/lib/selenium/webdriver/bidi/protocol/script.rbs

session.rbsAdd generated RBS for Session domain +167/-0

Add generated RBS for Session domain

• Provides RBS signatures for Session protocol commands/types and event helpers.

rb/sig/lib/selenium/webdriver/bidi/protocol/session.rbs

speculation.rbsAdd generated RBS for Speculation domain +45/-0

Add generated RBS for Speculation domain

• Provides RBS signatures for Speculation protocol commands and types.

rb/sig/lib/selenium/webdriver/bidi/protocol/speculation.rbs

storage.rbsAdd generated RBS for Storage domain +120/-0

Add generated RBS for Storage domain

• Provides RBS signatures for Storage protocol commands and types.

rb/sig/lib/selenium/webdriver/bidi/protocol/storage.rbs

user_agent_client_hints.rbsAdd generated RBS for UserAgentClientHints domain +60/-0

Add generated RBS for UserAgentClientHints domain

• Provides RBS signatures for UserAgentClientHints protocol commands and types.

rb/sig/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rbs

web_extension.rbsAdd generated RBS for WebExtension domain +70/-0

Add generated RBS for WebExtension domain

• Provides RBS signatures for WebExtension protocol commands and types.

rb/sig/lib/selenium/webdriver/bidi/protocol/web_extension.rbs

serialization.rbsAdd RBS signatures for Serialization runtime (UNSET/Data/Union) +107/-0

Add RBS signatures for Serialization runtime (UNSET/Data/Union)

• Defines Steep signatures for the serialization entrypoint, Data and Union base classes, and key runtime APIs like 'validate!' and 'from_json'/'as_json'.

rb/sig/lib/selenium/webdriver/bidi/serialization.rbs

transport.rbsAdd RBS signatures for Transport +37/-0

Add RBS signatures for Transport

• Adds RBS for Transport initialization and the 'execute' method contract used by generated protocol modules.

rb/sig/lib/selenium/webdriver/bidi/transport.rbs

Tests (3) +374 / -0
protocol_types_spec.rbAdd unit tests for generated protocol types, unions, validation, and events +239/-0

Add unit tests for generated protocol types, unions, validation, and events

• Covers record round-trips, union dispatch (discriminator and structural), nested/recursive type parsing, extensible records, enum validation behavior (outbound-only), typed result parsing, and EVENT_TYPES event payload mapping.

rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb

bidi_generate_spec.rbAdd generator utility tests for naming and enum key normalization +47/-0

Add generator utility tests for naming and enum key normalization

• Validates key generator helpers such as camelCase-to-snake_case conversion and enum key normalization for edge cases (acronyms, punctuation, leading minus).

rb/spec/unit/selenium/webdriver/bidi/support/bidi_generate_spec.rb

transport_spec.rbAdd unit tests for Transport serialization, parsing, and error handling +88/-0

Add unit tests for Transport serialization, parsing, and error handling

• Verifies param serialization rules (empty payload, nil/UNSET dropping, explicit null), typed result parsing via 'from_json', and raising on error replies.

rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb

Other (3) +32 / -2
.rubocop.ymlExclude generator and generated protocol from RuboCop metrics checks +7/-0

Exclude generator and generated protocol from RuboCop metrics checks

• Adds exclusions for the generator and generated protocol tree to avoid class/module length and accessor naming violations typical of generated code.

rb/.rubocop.yml

SteepfileRefine Steep ignore list for BiDi while migration is in progress +9/-2

Refine Steep ignore list for BiDi while migration is in progress

• Stops ignoring all BiDi code and instead ignores the current hand-written BiDi implementation files plus the generator, keeping the door open for typing the new generated layer.

rb/Steepfile

BUILD.bazelAdd Bazel rb_binary target to run BiDi Ruby code generation +16/-0

Add Bazel rb_binary target to run BiDi Ruby code generation

• Introduces a 'bidi-generate' binary that consumes the JS-produced schema and writes generated Ruby protocol files into the repository output directory, bundling ERB templates as data.

rb/lib/selenium/webdriver/BUILD.bazel

@qodo-code-review

qodo-code-review Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 19 rules

Grey Divider


Action required

1. Inbound nulls accepted ✓ Resolved 🐞 Bug ≡ Correctness
Description
Serialization::Record#from_json can accept explicit null for non-nullable fields because read(field,
raw) returns nil without checking field.nullable, bypassing the outbound nil validation. This
creates invalid typed objects and can later silently omit the field during re-serialization.
Code

rb/lib/selenium/webdriver/bidi/serialization/record.rb[R118-121]

+            def read(field, raw)
+              return raw if raw.nil?
+              return Serialization.to_symbol("#{name}##{field.name}", raw, enum_hash(field)) if field.enum
+              return raw if field.ref.nil?
Evidence
Outbound construction explicitly rejects nil for non-nullable fields, but inbound parsing returns
nil immediately and does not validate nullability, so non-nullable fields can become nil when
reading wire data.

rb/lib/selenium/webdriver/bidi/serialization/record.rb[92-105]
rb/lib/selenium/webdriver/bidi/serialization/record.rb[111-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Serialization::Record.from_json` currently allows `nil` to populate fields that are declared non-nullable, because `read(field, raw)` returns early on `raw.nil?` without validating `field.nullable`. This violates the “strict on values” inbound behavior and can create record instances that cannot be constructed outbound.

### Issue Context
Outbound construction (`new`) rejects `nil` for non-nullable fields via `validate_values`, but inbound construction (`from_json`) uses `construct` and relies on `read`/`wire_value`.

### Fix Focus Areas
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[92-105]
- rb/lib/selenium/webdriver/bidi/serialization/record.rb[118-125]

### Suggested fix
In `read(field, raw)`, when `raw.nil?`:
- return `nil` only if `field.nullable` is true
- otherwise raise `Selenium::WebDriver::Error::WebDriverError` (or a consistent parsing error) indicating a non-nullable field received null, including the field name for debugging.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Serialization module lacks tag ✓ Resolved 📘 Rule violation ✧ Quality
Description
The internal Serialization module is introduced without a YARD # @api private tag in the doc
block directly above the module definition. This can cause internal runtime APIs to appear
public/documented when they are intended to be private.
Code

rb/lib/selenium/webdriver/bidi/serialization.rb[R23-26]

+      # Wire round-trip runtime for the generated protocol layer: the value-type bases
+      # (Data, Union), the omit sentinel (UNSET), and outbound enum validation.
+      module Serialization
+        # Sentinel for an omitted optional: dropped from the payload entirely, vs nil which
Evidence
PR Compliance ID 389237 requires internal Ruby API elements to be marked with # @api private in
their YARD doc blocks. The new module Serialization has descriptive comments but no @api private
tag immediately above its definition.

Rule 389237: Mark internal Ruby APIs with @api private in YARD docs
rb/lib/selenium/webdriver/bidi/serialization.rb[23-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The YARD doc block immediately above `module Serialization` does not include `# @api private`.

## Issue Context
This module is part of the internal BiDi generated-protocol runtime and should be explicitly marked private per compliance requirements.

## Fix Focus Areas
- rb/lib/selenium/webdriver/bidi/serialization.rb[23-26]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. BiDiGenerate lacks @api private ✓ Resolved 📘 Rule violation ✧ Quality
Description
The internal generator module BiDiGenerate is introduced without a YARD # @api private tag
directly above the module definition. This violates the requirement to explicitly mark internal Ruby
APIs as private in YARD docs.
Code

rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[R24-36]

+# Generates Ruby WebDriver BiDi protocol modules from the shared, binding-neutral
+# BiDi schema produced by the JavaScript generator (see PR #17700):
+#   //javascript/selenium-webdriver:create-bidi-src_schema -> bidi-schema.json
+#
+# The schema is already normalized (inline enums hoisted, unions canonicalized,
+# group composition flattened, wire names and nullability preserved verbatim), so
+# this generator is a straight projection into Ruby with no CDDL interpretation.
+#
+# Invoked via `bazel run //rb/lib/selenium/webdriver:bidi-generate`. Bazel passes
+# the schema path (resolved through runfiles) plus the workspace-relative output
+# directory as ARGV. Can also be run directly:
+#   ruby bidi_generate.rb schema.json output/dir
+module BiDiGenerate
Evidence
PR Compliance ID 389237 requires internal Ruby classes/modules/methods to have # @api private in
the YARD doc block immediately above the definition. The new BiDiGenerate module has a YARD-style
comment but no @api private tag before the module declaration.

Rule 389237: Mark internal Ruby APIs with @api private in YARD docs
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[24-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`BiDiGenerate` is an internal code generator module but its YARD doc block is missing `# @api private` directly above `module BiDiGenerate`.

## Issue Context
Compliance requires internal Ruby APIs to be marked with `@api private` in YARD docs so they are not treated as supported public surface area.

## Fix Focus Areas
- rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[24-36]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. BiDi specs use RSpec mocks ✓ Resolved 📘 Rule violation ▣ Testability
Description
New unit specs use RSpec mocking (instance_double, allow(...).to receive) to simulate
WebDriver::WebSocketConnection. This violates the requirement to avoid mocks in tests unless using
real or contract-driven integrations, because hand-authored mocks can drift from the real
interface/behavior.
Code

rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb[R27-33]

+      describe Transport do
+        let(:connection) { instance_double(WebDriver::WebSocketConnection) }
+        let(:transport) { described_class.new(connection) }
+
+        def stub_result(result = {})
+          allow(connection).to receive(:send_cmd).and_return('result' => result)
+        end
Evidence
PR Compliance ID 389270 forbids use of mocking frameworks in tests unless backed by a
machine-checked contract; the new specs use RSpec mocks (instance_double, allow(...).to receive)
for WebDriver::WebSocketConnection behavior.

Rule 389270: Avoid mocks in tests; use real or contract-driven integrations
rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb[27-33]
rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb[164-190]
rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb[211-218]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
RSpec mocking (`instance_double`, `allow(...).to receive`) is used in new BiDi unit tests, violating the "avoid mocks" compliance requirement.

## Issue Context
The compliance rule allows simple in-memory fakes that implement the same interface without a mocking framework, or contract-driven stubs/integration tests.

## Fix Focus Areas
- rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb[27-33]
- rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb[164-193]
- rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb[211-218]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. BiDi load-order failure 🐞 Bug ☼ Reliability ⭐ New
Description
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.
Code

rb/lib/selenium/webdriver/remote/bidi_bridge.rb[33]

+          @transport = BiDi::Transport.new(@bidi.ws)
Evidence
BiDiBridge#create_session now depends on @bidi.ws to build a BiDi::Transport. However,
bidi/transport.rb defines/reopens class BiDi without defining initialize(url:) or ws, and
the intended BiDi implementation is only brought in via the autoload installed by
selenium/webdriver.rb. Without that autoload/require, the bridge can observe an
incompletely-defined BiDi constant and fail at session creation time.

rb/lib/selenium/webdriver/remote/bidi_bridge.rb[20-34]
rb/lib/selenium/webdriver/bidi/transport.rb[20-35]
rb/lib/selenium/webdriver/bidi.rb[34-43]
rb/lib/selenium/webdriver.rb[31-45]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### 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


6. transport missing YARD tags 📘 Rule violation ✧ Quality
Description
The new public transport method is added without a YARD doc block (@return/@raise), which
violates the requirement to document public API methods with YARD tags. This makes the API contract
unclear for users and documentation tooling.
Code

rb/lib/selenium/webdriver/remote/bridge.rb[R601-604]

+        def transport
+          msg = 'BiDi must be enabled by setting #web_socket_url to true in options class'
+          raise(WebDriver::Error::WebDriverError, msg)
+        end
Evidence
PR Compliance ID 389240 requires changed public API methods to include a YARD doc block with
description and relevant tags. The added def transport method has no preceding YARD documentation
and raises an exception as part of its contract.

Rule 389240: Document public API methods with YARD tags
rb/lib/selenium/webdriver/remote/bridge.rb[601-604]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new method `transport` is introduced without the required YARD documentation tags for public APIs.

## Issue Context
`transport` raises `WebDriver::Error::WebDriverError` when BiDi is not enabled; the method should document its behavior and return/raise contract using YARD tags.

## Fix Focus Areas
- rb/lib/selenium/webdriver/remote/bridge.rb[601-604]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Nil misroutes union dispatch 🐞 Bug ≡ Correctness
Description
Serialization::Union.outbound_variant treats a kwarg with value nil as “supplied” for presence-based
unions, so passing nil for a non-nullable dispatch key selects a variant even though that field will
be omitted during serialization. This can send an invalid payload instead of failing locally (e.g.,
emulation.setGeolocationOverride(error: nil) selects the Error variant but drops error on the wire).
Code

rb/lib/selenium/webdriver/bidi/serialization/union.rb[R82-85]

+            def outbound_variant(kwargs)
+              tag = @discriminator ? kwargs.fetch(@discriminator.to_sym, UNSET) : UNSET
+              variant_for(tag) { |k| kwargs.key?(k.to_sym) && !UNSET.equal?(kwargs[k.to_sym]) }
+            end
Evidence
Union presence-dispatch currently treats nil as present, but nil is documented/treated as
omission for optional params and is dropped for non-nullable fields during serialization; this can
select a variant and then omit the dispatching key on the wire.

rb/lib/selenium/webdriver/bidi/serialization/union.rb[81-85]
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[160-166]
rb/lib/selenium/webdriver/bidi/serialization/data.rb[136-145]
rb/lib/selenium/webdriver/bidi/protocol/emulation.rb[57-77]
rb/lib/selenium/webdriver/bidi/protocol/emulation.rb[205-216]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Serialization::Union.outbound_variant` uses key-presence to decide whether a presence-dispatched union arm matches, and it currently counts `nil` as “supplied”. However, the generated layer explicitly documents `nil` as equivalent to omitting an optional arg, and `Data#as_json` drops `nil` for non-nullable fields. This combination can select a union variant based on a `nil` value and then silently omit the dispatching key on the wire.

### Issue Context
Concrete example in the generated protocol:
- `Emulation::SetGeolocationOverrideParameters` presence-dispatches between `coordinates` (nullable) and `error` (non-nullable).
- The command signature makes both `coordinates:` and `error:` optional (default `Serialization::UNSET`), and the generator docs state `nil` is equivalent to omitting an optional param.
- With the current dispatch predicate, `error: nil` counts as supplied, so the union selects the `Error` variant even though `error` will be dropped from the serialized payload.

### Fix Focus Areas
- rb/lib/selenium/webdriver/bidi/serialization/union.rb[81-92]

Suggested approach:
- When evaluating presence rules, treat `nil` as “supplied” only if the corresponding field in that candidate variant is nullable; otherwise treat `nil` like “not supplied” (similar to `UNSET`).
 - This can be implemented by looking up the candidate variant class (`Protocol.const_get(path)`) and checking its `fields` metadata for each presence key.
 - Ensure this still allows cases like `coordinates: nil` where the field is nullable and explicit wire null is desired.
- Add/adjust a unit test demonstrating that `error: nil` does not match the `Error` variant (and should raise "no … variant matches …"), while `coordinates: nil` continues to work.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
8. Hash params lose explicit null ✓ Resolved 🐞 Bug ≡ Correctness
Description
Transport#serialize drops all nil values when params is a Hash, so callers using hash/passthrough
serialization cannot send explicit wire null even though the serialization layer defines nil as
“explicit null” and UNSET as “omit”. This silently changes request semantics for nullable fields
whenever the Hash path is used.
Code

rb/lib/selenium/webdriver/bidi/transport.rb[R42-48]

+        def serialize(params)
+          case params
+          when nil then {}
+          when ::Hash
+            params.reject { |_, value| value.nil? || Serialization::UNSET.equal?(value) }
+                  .transform_values { |value| Serialization::Data::Serializable.as_json(value) }
+          else params.as_json
Evidence
The serialization runtime explicitly defines UNSET for omission vs nil for wire null, but the Hash
serialization path drops nils, collapsing these semantics. Tests already demonstrate that explicit
wire null is meaningful and must be preserved (via typed params), highlighting the mismatch for
Hash/passthrough usage.

rb/lib/selenium/webdriver/bidi/transport.rb[42-49]
rb/lib/selenium/webdriver/bidi/serialization.rb[24-33]
rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb[61-66]
rb/spec/unit/selenium/webdriver/bidi/protocol_types_spec.rb[106-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Transport#serialize` treats `nil` the same as omitted for Hash params by rejecting nil values. This prevents sending explicit JSON nulls via the Hash path even though the runtime documents a semantic difference between `nil` (wire null) and `UNSET` (omit).

### Issue Context
Typed `Data` objects can correctly emit null-vs-omit, but Hash-based/passthrough calls currently cannot. This is especially risky for any commands/events that are modeled as passthrough hashes or for direct `Transport#execute` usage.

### Fix Focus Areas
- Change the Hash serialization filter to drop only `Serialization::UNSET`, not `nil`.
- Update any affected specs/callers to pass `Serialization::UNSET` when omission is intended.

- rb/lib/selenium/webdriver/bidi/transport.rb[42-49]
- rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb[61-66]
- rb/lib/selenium/webdriver/bidi/serialization.rb[24-33]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Generator emits unsafe literals 🐞 Bug ≡ Correctness
Description
BiDiGenerate.ruby_literal interpolates schema strings into single-quoted Ruby literals without
escaping, so discriminator/const values containing quotes or backslashes can generate syntactically
invalid Ruby (or unintended literals) in generated protocol files. This can break bidi-generate
and/or cause generated code to fail to load.
Code

rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[R72-77]

+  # Source literal for a discriminator/const value (string, boolean, or number).
+  def self.ruby_literal(value)
+    return 'nil' if value.nil?
+
+    value.is_a?(String) ? "'#{value}'" : value.to_s
+  end
Evidence
ruby_literal returns "'#{value}'" for strings, and its output is embedded into generated
Serialization::Data.define specs for baked discriminators; without escaping, certain schema values
can directly corrupt generated Ruby source.

rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[72-77]
rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[300-306]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`BiDiGenerate.ruby_literal` builds Ruby string literals via naive interpolation into single quotes (`'#{value}'`). Any schema value containing `'`, `\`, or newlines can produce invalid Ruby source or incorrect literal contents in the generated output.

### Issue Context
The generator uses `ruby_literal` for fixed discriminator members (`fixed:`), so these values are emitted directly into source code.

### Fix Focus Areas
- Use `value.dump` or `value.inspect` to emit correctly escaped Ruby literals.
- Ensure non-string values still serialize correctly (booleans/numbers/nil).

- rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[72-77]
- rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb[300-306]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Unknown union variant raises ✓ Resolved 🐞 Bug ☼ Reliability
Description
Serialization::Union#from_json raises ArgumentError when an inbound payload doesn’t match any
variant and the union has no fallback, which can break parsing when a browser adds new union arms.
This conflicts with the stated design goal in the runtime to keep inbound parsing forward-compatible
(i.e., avoid breaking on values newer than the schema).
Code

rb/lib/selenium/webdriver/bidi/serialization/union.rb[R71-75]

+            def variant_for(tag, payload:, &supplied)
+              return @variants[tag] if !UNSET.equal?(tag) && @variants&.key?(tag)
+
+              @presence&.each { |path, keys| return path if keys.all?(&supplied) }
+              @fallback || raise(::ArgumentError, "no #{name} variant matches #{payload.inspect}")
Evidence
Union#variant_for raises if no fallback is configured; several generated unions (e.g.,
Script::PrimitiveProtocolValue) define variants(...) without fallback, so a new discriminator
value from the browser would raise during from_json. This is at odds with the inbound-parsing
posture documented in Data#from_json (no validation so newer-than-schema values still parse).

rb/lib/selenium/webdriver/bidi/serialization/union.rb[43-76]
rb/lib/selenium/webdriver/bidi/protocol/script.rb[182-193]
rb/lib/selenium/webdriver/bidi/serialization/data.rb[78-86]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Serialization::Union#from_json` currently raises when no variant matches and the union class has no `fallback`, which makes inbound parsing brittle when a browser sends a new/unknown union discriminator (or a new presence-based arm).

### Issue Context
The `Data.from_json` path explicitly avoids inbound validation to remain forward-compatible; unions should follow the same resilience principle. Many generated unions do not define `fallback`, so an unknown discriminator currently crashes parsing.

### Fix Focus Areas
- Implement a non-raising inbound behavior for unmatched unions (e.g., return the raw `json_payload` hash for unknown cases, or introduce a generic `Unknown` wrapper type) while keeping `build` strict for outbound.
- Consider rescuing `ArgumentError`/`NameError` in `from_json` and returning `json_payload` when the union cannot be resolved.

- rb/lib/selenium/webdriver/bidi/serialization/union.rb[43-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

11. Bridge uses private ivar ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
Remote::BiDiBridge constructs BiDi::Transport by extracting BiDi’s internal @ws via
instance_variable_get, coupling the bridge to BiDi’s private state. This is brittle if BiDi’s
internal connection field/name/lifecycle changes.
Code

rb/lib/selenium/webdriver/remote/bidi_bridge.rb[R32-33]

+          # Share the BiDi object's socket until the bridge owns the connection directly.
+          @transport = BiDi::Transport.new(@bidi.instance_variable_get(:@ws))
Evidence
BiDiBridge explicitly uses instance_variable_get(:@ws), and BiDi stores its connection in @ws
without exposing it, demonstrating reliance on private implementation details.

rb/lib/selenium/webdriver/remote/bidi_bridge.rb[20-34]
rb/lib/selenium/webdriver/bidi.rb[34-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Remote::BiDiBridge` currently reaches into `Selenium::WebDriver::BiDi` using `instance_variable_get(:@ws)` to build a `BiDi::Transport`. This creates a hidden coupling to BiDi internals.

### Issue Context
`BiDi` owns the websocket connection in an instance variable and does not expose it as part of its API.

### Fix Focus Areas
- rb/lib/selenium/webdriver/remote/bidi_bridge.rb[20-34]
- rb/lib/selenium/webdriver/bidi.rb[34-64]

### Suggested fix
Introduce an explicit API seam instead of `instance_variable_get`, e.g.:
- Add a private `BiDi#connection` reader (or `BiDi#transport`) returning the underlying `WebSocketConnection` (or a `BiDi::Transport`) and use that from `BiDiBridge`.
- Alternatively, make `BiDi::Transport` accept a `BiDi` instance and adapt to its public `send_cmd` API.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Weak transport param assertion ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
In protocol_spec, the example “passes an allowed value through to the transport” only asserts that
send_cmd was called, so it will still pass even if the validated enum value (wait: 'complete') is
not forwarded/serialized into params.
Code

rb/spec/unit/selenium/webdriver/bidi/protocol_spec.rb[R51-58]

+            it 'passes an allowed value through to the transport' do
+              connection = instance_double(WebDriver::WebSocketConnection)
+              allow(connection).to receive(:send_cmd).and_return('result' => {'navigation' => 'n', 'url' => 'u'})
+
+              BrowsingContext.new(Transport.new(connection)).navigate(context: 'c', url: 'u', wait: 'complete')
+
+              expect(connection).to have_received(:send_cmd)
+            end
Evidence
The spec currently only checks that send_cmd was invoked, but Transport#execute forwards both
the command method and serialized params to send_cmd, so without asserting args the test does not
verify wait is actually transmitted.

rb/spec/unit/selenium/webdriver/bidi/protocol_spec.rb[51-58]
rb/lib/selenium/webdriver/bidi/transport.rb[32-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The spec claims it verifies that an allowed enum value is passed through to the transport, but it only checks that `send_cmd` was called (no argument assertion). This reduces coverage and can miss regressions where the `wait` parameter is dropped.

### Issue Context
`Transport#execute` forwards `method:` and serialized `params:` to `send_cmd`, so the spec should assert the outgoing params include the validated value.

### Fix Focus Areas
- rb/spec/unit/selenium/webdriver/bidi/protocol_spec.rb[51-58]

### Suggested change
Update the expectation to assert the call arguments, e.g.:

```ruby
expect(connection).to have_received(:send_cmd)
 .with(method: 'browsingContext.navigate', params: {'context' => 'c', 'url' => 'u', 'wait' => 'complete'})
```

(or `hash_including('wait' => 'complete')` if other params may be added).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb
Comment thread rb/lib/selenium/webdriver/bidi/serialization.rb
Comment thread rb/spec/unit/selenium/webdriver/bidi/transport_spec.rb

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 a Transport seam 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.

Comment thread rb/lib/selenium/webdriver/bidi/serialization/union.rb
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 45c6cd3

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit c155ac0

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 64e9f1a

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 2fdaa9c

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 44 changed files in this pull request and generated no new comments.

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d5372e8

Comment thread rb/spec/unit/selenium/webdriver/bidi/protocol_spec.rb
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 88ef296

Comment thread rb/lib/selenium/webdriver/remote/bridge.rb
Comment thread rb/lib/selenium/webdriver/bidi/serialization/record.rb
Comment thread rb/lib/selenium/webdriver/remote/bidi_bridge.rb Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 2adb4f7

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 53 out of 53 changed files in this pull request and generated 2 comments.

Comment thread rb/lib/selenium/webdriver/bidi/serialization/record.rb
Comment thread rb/lib/selenium/webdriver/remote/bidi_bridge.rb
# under the License.

# This file is automatically generated. DO NOT EDIT!
# Regenerate with: bazel run //rb/lib/selenium/webdriver:bidi-generate

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

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

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 1afbccd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related B-support Issue or PR related to support classes C-rb Ruby Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants