Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 133 additions & 80 deletions rb/TESTING.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
# 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

```

## 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`.
* **Runner:** Tests use RSpec.
* **Test Pages:** HTML files live in `common/src/web/`.
* **Helpers:** `driver`, `wait`, `short_wait`, `long_wait`, `url_for`.

### Example Spec

```ruby
module Selenium
Expand All @@ -24,34 +35,81 @@ module Selenium
end
end
end

```

## Running Tests

Bazel creates test targets for each browser and remote variants.
There are two ways to run tests: **Bazel** (used in CI) and **RSpec** (preferred for local development).

### 1. Using Bazel (CI Workflow)

Bazel creates isolated 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 RSpec (Local Workflow)

For rapid "edit-run" cycles, use RSpec directly. This bypasses the Bazel sandbox and is faster for local debugging. **Selenium Manager** automatically handles driver setup (chromedriver, geckodriver), so no manual path configuration is required.

```shell
# Run a specific spec file
bundle exec rspec spec/integration/selenium/webdriver/chrome/driver_spec.rb

# Run all unit tests
bundle exec rspec spec/unit

# Run with specific environment variables
driver=firefox bundle exec rspec spec/integration/selenium/webdriver/firefox/driver_spec.rb

```

### 3. Using Rake

The `Rakefile` provides shortcuts for common tasks:

```shell
rake spec:unit # Run unit tests
rake spec:integration # Run integration tests (Chrome default)
rake docs # Generate docs
rake clean # Clean generated artifacts
Copy link
Member

Choose a reason for hiding this comment

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

Haven't looked at the code in a while. Does clean require we re-run the bazel commands.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rake clean doesn't apply here, and I will actually remove it because the clean task is on the rakefile at the selenium level and not under rb/rakefile


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

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 (not pending). |
| `exclude` | Test is skipped (use for broken/unreliable tests). |

### Guard Conditions

| 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
Expand All @@ -69,56 +127,57 @@ 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
```shell
# Check code style
bundle exec rubocop

From `spec_support/helpers.rb`:
# Auto-correct simple offenses
bundle exec rubocop -A

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

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

```
rb/spec/
├── unit/ # Unit tests (no browser)
│ └── selenium/webdriver/
└── integration/ # Integration tests
└── selenium/webdriver/
├── chrome/
├── firefox/
├── safari/
├── bidi/
└── spec_support/ # Test helpers
```

Test files end in `_spec.rb` (e.g., `driver_spec.rb`).
## Helpers & Debugging

## Build Files
From `spec_support/helpers.rb`:

* 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`.
| 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. |

### Debugging

To debug tests locally (outside of Bazel), insert a breakpoint:

1. Add `require 'pry'; binding.pry` in your code.
2. Run the test using `bundle exec rspec`.

## Environment Variables

Expand All @@ -134,47 +193,41 @@ 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
Test files must end in `_spec.rb` (e.g., `driver_spec.rb`).

Environment variables interact with test guards. For example:
## Build Files

```ruby
# This test only runs when BiDi is enabled
it 'uses BiDi feature', only: {bidi: true} do
# Test code
end
* 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`.

# This test is excluded when BiDi is enabled
it 'classic WebDriver only', exclusive: {bidi: false} do
# Test code
end
```

Run with `BIDI=true` to see these guards in action.
```