diff --git a/rb/TESTING.md b/rb/TESTING.md
index 6fda6fc4d7c64..eb3d67e9e718a 100644
--- a/rb/TESTING.md
+++ b/rb/TESTING.md
@@ -1,13 +1,44 @@
# Ruby Testing Guide
-This guide helps contributors write tests in the Selenium Ruby codebase.
+This guide helps contributors write tests, maintain code style, and generate documentation for the Selenium Ruby bindings.
+
+## Local Development Setup
+
+Before running tests, navigate to the `rb/` directory and install the required dependencies.
+
+```shell
+cd rb
+bundle install
+```
+
+**Note:** Local development still requires running Bazel to generate the atoms and devtools code. You can generate these artifacts by running:
+
+```shell
+bundle exec rake update
+```
+
+Or from the parent `selenium` directory:
+
+```shell
+./go rb:update
+```
+
+### RubyMine IDE Setup
+
+If you want to use [RubyMine](https://www.jetbrains.com/ruby/) for development, you can configure it to use Bazel artifacts:
+
+1. Open `rb/` as a main project directory.
+2. Run `bundle exec rake update` as necessary to create up-to-date artifacts. If this does not work, run `./go rb:update` from the `selenium` (parent) directory.
+3. In Settings / Languages & Frameworks / Ruby SDK and Gems add new Interpreter pointing to `../bazel-selenium/external/rules_ruby_dist/dist/bin/ruby`.
+4. You should now be able to run and debug any spec. It uses Chrome by default, but you can alter it using environment variables specified in the [Environment Variables](#environment-variables) section.
## Test Framework
* Tests use RSpec.
-* Test HTML pages live in `common/src/web/`.
-* `url_for("page.html")` gets test page URLs.
-* Helper methods: `driver`, `wait`, `short_wait`, `long_wait`.
+* Test HTML files live in `common/src/web/`.
+* **Helper methods:** `driver`, `wait`, `short_wait`, `long_wait`, `url_for`.
+
+### Example Spec
```ruby
module Selenium
@@ -31,15 +62,27 @@ end
Bazel creates test targets for each browser and remote variants.
```shell
-bazel test //rb/spec/... # All tests
-bazel test //rb/spec/unit/... # Unit tests
+bazel test //rb/spec/... # All tests
+bazel test //rb/spec/unit/... # Unit tests
bazel test //rb/spec/integration/... --test_tag_filters=chrome # Chrome tests
-bazel test //rb/spec/integration/... --test_tag_filters=firefox # Firefox tests
-bazel test //rb/spec/integration/... --test_tag_filters=chrome-remote # Chrome on Grid
+bazel test //rb/spec/integration/... --test_tag_filters=firefox # Firefox tests
+bazel test //rb/spec/integration/... --test_tag_filters=chrome-remote # Chrome on Grid
+
+# Viewing Output
+bazel test //rb/... --test_output=all # See console output at the end
+bazel test //rb/... --test_output=streamed # See output in real-time (no parallel execution)
+
+```
+
+### 2. Using Rake
+
+The `rb/Rakefile` provides shortcuts for common tasks:
+
+```shell
+rake update # Setup everything to run tests in RubyMine
+rake unit # Run unit tests
+rake spec # Run all integration tests in Chrome
-# Additional Arguments
-bazel test //rb/... --test_output=all # See console output at the end
-bazel test //rb/... --test_output=streamed # See console output real-time (removes parallel execution)
```
## Guards (Test Skipping)
@@ -47,11 +90,26 @@ bazel test //rb/... --test_output=streamed # See console output real-time (remov
Guards control when tests run. Add them as metadata on `describe`, `context`, or `it` blocks.
| Guard | When to Use |
-|-------|-------------|
-| `except` | Test is pending if conditions ARE met |
-| `only` | Test is pending if conditions are NOT met |
-| `exclusive` | Test is skipped entirely if conditions not met |
-| `exclude` | Test is skipped (use for broken/unreliable tests) |
+| --- | --- |
+| `except` | Test is pending if conditions ARE met. |
+| `only` | Test is pending if conditions are NOT met. |
+| `exclusive` | Test is skipped entirely if conditions not met. |
+| `exclude` | Test is skipped (use for broken/unreliable tests). |
+
+### Guard Conditions
+
+Conditions are defined in [`spec/integration/selenium/webdriver/spec_helper.rb`](spec/integration/selenium/webdriver/spec_helper.rb).
+
+| Condition | Values |
+| --- | --- |
+| `browser` | `:chrome`, `:firefox`, `:edge`, `:safari`, `:safari_preview`, `:ie` |
+| `driver` | `:remote` |
+| `platform` | `:linux`, `:macos`, `:windows` |
+| `headless` | `true`, `false` |
+| `bidi` | `true`, `false` |
+| `ci` | `true`, `false` |
+
+### Guard Examples
```ruby
# Skip on Safari
@@ -69,56 +127,148 @@ end
# Multiple conditions
it 'something', exclude: [
{browser: :safari},
- {browser: :firefox, reason: 'https://github.com/SeleniumHQ/selenium/issues/123'}
+ {browser: :firefox, reason: '[https://github.com/SeleniumHQ/selenium/issues/123](https://github.com/SeleniumHQ/selenium/issues/123)'}
] do
end
+
```
-### Guard Conditions
+## Code Style & Linting
-| Condition | Values |
-|-----------|--------|
-| `browser` | `:chrome`, `:firefox`, `:edge`, `:safari`, `:safari_preview`, `:ie` |
-| `driver` | `:remote` |
-| `platform` | `:linux`, `:macos`, `:windows` |
-| `headless` | `true`, `false` |
-| `bidi` | `true`, `false` |
-| `ci` | `true`, `false` |
+Selenium enforces strict code style using **Rubocop**. CI will fail if linting errors are present.
-## Helpers
+Configuration is defined in [`.rubocop.yml`](.rubocop.yml). **Prefer updating the configuration file over using in-file rubocop guards** (like `# rubocop:disable`) to maintain consistency across the codebase.
-From `spec_support/helpers.rb`:
+```shell
+# Check code style
+bundle exec rubocop
-| Helper | Description |
-|--------|-------------|
-| `driver` | Current WebDriver instance |
-| `reset_driver!(...)` | Reset driver with new options |
-| `url_for(filename)` | Get test page URL |
-| `wait` / `short_wait` / `long_wait` | Wait instances (10s, 3s, 30s) |
-| `wait_for_element(locator)` | Wait for element to appear |
-| `wait_for_alert` | Wait for alert presence |
+# Auto-correct simple offenses
+bundle exec rubocop -A
-## Test Organization
+```
+
+## Type Signatures with Steep
+
+Selenium Ruby uses **Steep** for gradual type checking with RBS (Ruby Signature) files. When you create a new class or modify existing classes, you should add or update the corresponding type signatures.
+### Adding Type Signatures for New Classes
+
+When creating a new class in `lib/selenium/webdriver/`:
+
+1. Create a corresponding `.rbs` file in `sig/` with the same directory structure.
+2. Define the class signature with method signatures, parameter types, and return types.
+
+**Example:** For `lib/selenium/webdriver/my_class.rb`:
+
+```ruby
+# lib/selenium/webdriver/my_class.rb
+module Selenium
+ module WebDriver
+ class MyClass
+ def initialize(value)
+ @value = value
+ end
+
+ def process
+ @value.to_s.upcase
+ end
+ end
+ end
+end
```
-rb/spec/
-├── unit/ # Unit tests (no browser)
-│ └── selenium/webdriver/
-└── integration/ # Integration tests
- └── selenium/webdriver/
- ├── chrome/
- ├── firefox/
- ├── safari/
- ├── bidi/
- └── spec_support/ # Test helpers
+
+Create `sig/selenium/webdriver/my_class.rbs`:
+
+```rbs
+module Selenium
+ module WebDriver
+ class MyClass
+ @value: untyped
+
+ def initialize: (untyped value) -> void
+ def process: () -> String
+ end
+ end
+end
```
-Test files end in `_spec.rb` (e.g., `driver_spec.rb`).
+### Updating Type Signatures
-## Build Files
+When modifying method signatures, parameters, or return types:
-* Adding tests shouldn't require Bazel changes—`rb_integration_test` uses glob patterns.
-* Make sure `*_spec.rb` files are in a directory with a `BUILD.bazel` containing `rb_integration_test`.
+1. Update the corresponding `.rbs` file to match the implementation.
+2. Run Steep to check for type errors.
+
+### Running Steep
+
+```shell
+# Type check all files
+bundle exec steep check
+
+# Type check specific files
+bundle exec steep check lib/selenium/webdriver/my_class.rb
+
+# Show Steep statistics
+bundle exec steep stats
+
+```
+
+### Type Signature Best Practices
+
+* **Start simple:** Use `untyped` for complex types initially, then refine.
+* **Be specific:** Prefer concrete types (`String`, `Integer`) over `untyped` when possible.
+* **Document generics:** Use generic types for collections (e.g., `Array[String]`, `Hash[Symbol, String]`).
+* **Match reality:** Ensure signatures accurately reflect the actual implementation.
+
+### Common RBS Types
+
+| Type | Description | Example |
+| --- | --- | --- |
+| `String` | String values | `def name: () -> String` |
+| `Integer` | Integer numbers | `def count: () -> Integer` |
+| `bool` | Boolean (true/false) | `def valid?: () -> bool` |
+| `void` | No return value | `def initialize: () -> void` |
+| `untyped` | Any type (use sparingly) | `def raw: () -> untyped` |
+| `Array[T]` | Array of type T | `def tags: () -> Array[String]` |
+| `Hash[K, V]` | Hash with key/value types | `def opts: () -> Hash[Symbol, String]` |
+| `T \| nil` | Nullable type | `def find: () -> (Element \| nil)` |
+
+**Note:** CI runs Steep checks. Ensure your type signatures are correct before submitting a PR.
+
+## Documentation
+
+We use **YARD** for inline documentation. Ensure your changes are documented and generate valid HTML.
+
+```shell
+# Generate documentation
+bundle exec yard doc
+
+# Run a local documentation server (view at http://localhost:8808)
+bundle exec yard server --reload
+
+```
+
+## Helpers & Debugging
+
+From `spec_support/helpers.rb`:
+
+| Helper | Description |
+| --- | --- |
+| `driver` | Current WebDriver instance. |
+| `reset_driver!(...)` | Reset driver with new options. |
+| `url_for(filename)` | Get test page URL (from `common/src/web`). |
+| `wait` / `short_wait` / `long_wait` | Wait instances (10s, 3s, 30s). |
+| `wait_for_element(locator)` | Wait for element to appear. |
+| `wait_for_alert` | Wait for alert presence. |
+
+### Interactive REPL
+
+Instead of using `irb`, you can create an interactive REPL with all gems loaded using:
+
+```shell
+bazel run //rb:console
+```
## Environment Variables
@@ -134,47 +284,37 @@ WD_REMOTE_BROWSER=chrome BIDI=true bazel test //rb/spec/integration/...
# Run BiDi-specific tests
bazel test //rb/spec/integration/selenium/webdriver/bidi/...
+
```
-### Available Variables
+### Common Variables
| Variable | Purpose | Values | Example |
-|----------|---------|--------|---------|
+| --- | --- | --- | --- |
| `BIDI` | Enable BiDi protocol | `true`, `false` | `BIDI=true` |
| `WD_REMOTE_BROWSER` | Specify browser for remote tests | `chrome`, `firefox`, `edge`, `safari` | `WD_REMOTE_BROWSER=firefox` |
| `HEADLESS` | Run tests in headless mode | `true`, `false` | `HEADLESS=true` |
| `DEBUG` | Enable debug logging | `true`, `false` | `DEBUG=true` |
-### Examples
-
-```shell
-# Run Chrome tests with BiDi enabled
-BIDI=true bazel test //rb/spec/integration/... --test_tag_filters=chrome
-
-# Run headless Firefox tests
-HEADLESS=true bazel test //rb/spec/integration/... --test_tag_filters=firefox
+## Test Organization
-# Run remote tests on Edge with BiDi
-WD_REMOTE_BROWSER=edge BIDI=true bazel test //rb/spec/integration/... --test_tag_filters=remote
+```text
+rb/spec/
+├── unit/ # Unit tests (no browser)
+│ └── selenium/webdriver/
+└── integration/ # Integration tests
+ └── selenium/webdriver/
+ ├── chrome/
+ ├── firefox/
+ ├── safari/
+ ├── bidi`
+ └── spec_support/ # Test helpers
-# Combine multiple variables
-BIDI=true HEADLESS=true DEBUG=true bazel test //rb/spec/integration/selenium/webdriver/bidi/...
```
-### Testing Guard Behavior
-
-Environment variables interact with test guards. For example:
+Test files must end in `_spec.rb` (e.g., `driver_spec.rb`).
-```ruby
-# This test only runs when BiDi is enabled
-it 'uses BiDi feature', only: {bidi: true} do
- # Test code
-end
-
-# This test is excluded when BiDi is enabled
-it 'classic WebDriver only', exclusive: {bidi: false} do
- # Test code
-end
-```
+## Build Files
-Run with `BIDI=true` to see these guards in action.
+* Adding tests shouldn't require Bazel changes—`rb_integration_test` uses glob patterns.
+* Make sure `*_spec.rb` files are in a directory with a `BUILD.bazel` containing `rb_integration_test`.