Capybara — Advanced Patterns & Playbook
Page Object with Site Prism
class LoginPage < SitePrism::Page
set_url "/login"
element :email_field, "#email"
element :password_field, "#password"
element :submit_button, "button[type='submit']"
element :error_message, ".alert-danger"
def login(email, password)
email_field.set(email)
password_field.set(password)
submit_button.click
end
end
class DashboardPage < SitePrism::Page
set_url "/dashboard"
section :nav, NavigationSection, ".navbar"
elements :cards, ".dashboard-card"
def card_titles
cards.map { |c| c.find("h3").text }
end
end
# Test
RSpec.describe "Login", type: :feature do
let(:login_page) { LoginPage.new }
it "logs in successfully" do
login_page.load
login_page.login("admin@test.com", "password")
expect(page).to have_current_path("/dashboard")
end
end
Advanced Matchers & Finders
# Scoped finders
within("#user-form") do
fill_in "Name", with: "Alice"
select "Admin", from: "Role"
check "Active"
click_button "Save"
end
# Custom matchers
expect(page).to have_css(".product", count: 5)
expect(page).to have_css(".product", minimum: 1, maximum: 10)
expect(page).to have_selector(:xpath, "//table//tr", count: 3)
expect(page).to have_text("Welcome", wait: 10)
# JavaScript evaluation
total = page.evaluate_script("document.querySelectorAll('.item').length")
expect(total).to eq(5)
# Accept/dismiss dialogs
accept_confirm("Are you sure?") { click_link "Delete" }
dismiss_prompt { click_link "Rename" }
Driver Configuration
# spec/support/capybara.rb
Capybara.configure do |config|
config.default_max_wait_time = 10
config.default_normalize_ws = true
config.save_path = "tmp/capybara"
config.automatic_label_click = true
end
Capybara.register_driver :headless_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
Capybara.javascript_driver = :headless_chrome
Anti-Patterns
- ❌
sleep(5) — use Capybara's built-in waiting: have_css, have_text
- ❌
find(".class").click without waiting — use click_link/click_button (auto-waits)
- ❌
page.html.include?("text") — use have_text matcher (handles async)
- ❌ XPath selectors when CSS suffices — CSS is more readable