4.5 KiB
The user's stated intent is to use Cuprite. To achieve this and eliminate the unstable Selenium/Firefox stack, the Rails configuration must be explicitly updated. It is insufficient to merely include the cuprite gem; it must be registered and set as the driver for the specific test type.
6.1 Step 1: Gem Dependency Management
Ensure the Gemfile includes Cuprite and excludes conflicting Selenium dependencies if they are no longer needed.
Ruby
group :test do
gem 'capybara'
gem 'cuprite'
# Optional: Keep these if you need Selenium for other specific tests,
# otherwise remove them to prevent accidental fallback.
# gem 'selenium-webdriver'
# gem 'webdrivers'
end
6.2 Step 2: Driver Registration
Create a robust driver registration file. This is best placed in spec/support/cuprite.rb. This configuration specifically addresses the resource constraints discussed in Section 5.2
Ruby
# spec/support/cuprite.rb
require 'capybara/cuprite'
Capybara.register_driver(:cuprite) do |app|
Capybara::Cuprite::Driver.new(
app,
window_size: ,
browser_options: {
# Critical for Docker/CI stability
'no-sandbox' => nil,
'disable-gpu' => nil,
'disable-dev-shm-usage' => nil, # fallback if /dev/shm is small
},
# Enable the inspector for debugging
inspector: true,
# Respect the HEADLESS environment variable
headless: ENV.fetch("HEADLESS", "true")!= "false"
)
end
# Set Cuprite as the default driver for JavaScript-enabled tests
Capybara.javascript_driver = :cuprite
6.3 Step 3: Configuring the System Test Base Class
This is the most common point of failure in configuration. Rails generators create a test/application_system_test_case.rb that defaults to Selenium. This must be overridden.
For Rails System Tests (test/application_system_test_case.rb):
Ruby
require "test_helper"
require "capybara/cuprite"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
# OLD (Delete this):
# driven_by :selenium, using: :headless_chrome, screen_size:
# NEW (Use this):
driven_by :cuprite, screen_size:
end
For RSpec Feature Specs (spec/rails_helper.rb):
If the failing file is a "Feature Spec" (spec/features/...), it relies on Capybara.javascript_driver. However, if it is a "System Spec" (spec/system/...), it relies on the driven_by configuration block.2
Ruby
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :cuprite
end
# For legacy feature specs, ensure js: true triggers Cuprite
config.before(:each, type: :feature, js: true) do
Capybara.current_driver = :cuprite
end
end
6.4 The "Wrapped" Misconception Resolved
By implementing the above, the architecture effectively becomes "Capybara wrapping Cuprite," as the user originally surmised. Capybara acts as the unified DSL (page.visit, page.click), while Cuprite handles the heavy lifting of browser automation via Ferrum and CDP. The "Marionette" error will vanish because geckodriver is no longer invoked.
7. Advanced Debugging with Cuprite
Once the migration is complete, the user will have access to superior debugging tools that can address the question: "What can I do to debug this? or try something different?"
7.1 Interactive Debugging (page.driver.debug)
Unlike Selenium, which often requires complex setups to attach a debugger to a headless session, Cuprite allows for pausing execution and spinning up a debug interface with a single command.8
Ruby
it "debugs the session" do
visit new_session_path
# Pauses the test.
# If configured, it will attempt to open Chrome Inspector.
# Otherwise, it allows inspection via the console.
page.driver.debug(binding)
end
When page.driver.debug(binding) is executed, it halts the Ruby process. The developer can then:
- Inspect the DOM using Capybara commands in the console.
- Open a browser window (if running locally) connected to the session.
- Check network traffic using page.driver.network.traffic to see if API requests failed.
7.2 Accessing Browser Logs
One of the most powerful features of CDP is the ability to access the browser's internal console logs easily, which is notoriously difficult in Selenium/Firefox (often requiring loggingPrefs capabilities).
Ruby
# Retrieve console logs from the Chrome instance
logs = page.driver.browser.logs.get(:browser)
logs.each do |log|
puts "[#{log.level}] #{log.message}"
end