Added fancy XPath generation class

This commit is contained in:
Jonas Nicklas 2009-12-09 19:03:55 +01:00
parent f22092a214
commit 8d44e8cb74
4 changed files with 254 additions and 26 deletions

View File

@ -1,17 +1,6 @@
module Capybara
class Session
FIELDS_PATHS = {
:text_field => proc { |id| "//input[@type='text'][@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:text_area => proc { |id| "//textarea[@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:password_field => proc { |id| "//input[@type='password'][@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:radio => proc { |id| "//input[@type='radio'][@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:hidden_field => proc { |id| "//input[@type='hidden'][@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:checkbox => proc { |id| "//input[@type='checkbox'][@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:select => proc { |id| "//select[@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" },
:file_field => proc { |id| "//input[@type='file'][@id='#{id}' or @id=//label[contains(.,'#{id}')]/@for]" }
}
attr_reader :mode, :app
def initialize(mode, app)
@ -49,37 +38,37 @@ module Capybara
end
def fill_in(locator, options={})
field = find_field(locator, :text_field, :text_area, :password_field)
field = find(XPath.fillable_field(locator).to_s)
raise Capybara::ElementNotFound, "cannot fill in, no text field, text area or password field with id or label '#{locator}' found" unless field
field.set(options[:with])
end
def choose(locator)
field = find_field(locator, :radio)
field = find(XPath.radio_button(locator).to_s)
raise Capybara::ElementNotFound, "cannot choose field, no radio button with id or label '#{locator}' found" unless field
field.set(true)
end
def check(locator)
field = find_field(locator, :checkbox)
field = find(XPath.checkbox(locator).to_s)
raise Capybara::ElementNotFound, "cannot check field, no checkbox with id or label '#{locator}' found" unless field
field.set(true)
end
def uncheck(locator)
field = find_field(locator, :checkbox)
field = find(XPath.checkbox(locator).to_s)
raise Capybara::ElementNotFound, "cannot uncheck field, no checkbox with id or label '#{locator}' found" unless field
field.set(false)
end
def select(value, options={})
field = find_field(options[:from], :select)
field = find(XPath.select(options[:from]).to_s)
raise Capybara::ElementNotFound, "cannot select option, no select box with id or label '#{options[:from]}' found" unless field
field.select(value)
end
def attach_file(locator, path)
field = find_field(locator, :file_field)
field = find(XPath.file_field(locator).to_s)
raise Capybara::ElementNotFound, "cannot attach file, no file field with id or label '#{locator}' found" unless field
field.set(path)
end
@ -143,9 +132,8 @@ module Capybara
all(locator).first
end
def find_field(locator, *kinds)
kinds = FIELDS_PATHS.keys if kinds.empty?
find(kinds.map { |kind| FIELDS_PATHS[kind].call(locator) }.join("|"))
def find_field(locator)
find(XPath.field(locator).to_s)
end
alias_method :field_labeled, :find_field

69
lib/capybara/xpath.rb Normal file
View File

@ -0,0 +1,69 @@
module Capybara
# this is a class for generating XPath queries, use it like this:
# Xpath.text_field('foo').link('blah').to_s
# this will generate an XPath that matches either a text field or a link
class XPath
class << self
def respond_to?(method)
new.respond_to?(method)
end
def method_missing(*args)
new.send(*args)
end
end
attr_reader :paths
def initialize(*paths)
@paths = paths
end
def field(locator)
fillable_field(locator).file_field(locator).checkbox(locator).radio_button(locator).select(locator)
end
def fillable_field(locator)
text_field(locator).password_field(locator).text_area(locator)
end
def text_field(locator)
append("//input[@type='text'][@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def password_field(locator)
append("//input[@type='password'][@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def text_area(locator)
append("//textarea[@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def radio_button(locator)
append("//input[@type='radio'][@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def checkbox(locator)
append("//input[@type='checkbox'][@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def select(locator)
append("//select[@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def file_field(locator)
append("//input[@type='file'][@id='#{locator}' or @id=//label[contains(.,'#{locator}')]/@for]")
end
def to_s
@paths.join(' | ')
end
private
def append(path)
XPath.new(*[@paths, path].flatten)
end
end
end

View File

@ -552,15 +552,10 @@ shared_examples_for "session" do
@session.find_field('Region')[:name].should == 'form[region]'
end
it "should raise an error if the field doesn't exist" do
it "should be nil if the field doesn't exist" do
@session.find_field('Does not exist').should be_nil
end
it "should find only given kind of field" do
@session.find_field('form_description', :text_field, :text_area).text.should == 'Descriptive text goes here'
@session.find_field('form_description', :password_field).should be_nil
end
it "should be aliased as 'field_labeled' for webrat compatibility" do
@session.field_labeled('Dog').value.should == 'dog'
@session.field_labeled('Does not exist').should be_nil

176
spec/xpath_spec.rb Normal file
View File

@ -0,0 +1,176 @@
require File.expand_path('spec_helper', File.dirname(__FILE__))
describe Capybara::XPath do
before do
@driver = Capybara::Driver::RackTest.new(TestApp)
@driver.visit('/form')
@xpath = Capybara::XPath.new
end
it "should proxy any class method calls to a new instance" do
@query = Capybara::XPath.fillable_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
end
it "should respond to instance methods at the class level" do
Capybara::XPath.should respond_to(:fillable_field)
end
describe '#field' do
it "should find any field by id or label" do
@query = @xpath.field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
@query = @xpath.field('Description').to_s
@driver.find(@query).first.text.should == 'Descriptive text goes here'
@query = @xpath.field('Document').to_s
@driver.find(@query).first[:name].should == 'form[document]'
@query = @xpath.field('Cat').to_s
@driver.find(@query).first.value.should == 'cat'
@query = @xpath.field('Male').to_s
@driver.find(@query).first.value.should == 'male'
@query = @xpath.field('Region').to_s
@driver.find(@query).first[:name].should == 'form[region]'
end
it "should be chainable" do
@query = @xpath.field('First Name').password_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.field('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#fillable_field' do
it "should find a text field, password field, or text area by id or label" do
@query = @xpath.fillable_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.fillable_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
@query = @xpath.fillable_field('Description').to_s
@driver.find(@query).first.text.should == 'Descriptive text goes here'
end
it "should be chainable" do
@query = @xpath.fillable_field('First Name').password_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.fillable_field('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#text_field' do
it "should find a text field by id or label" do
@query = @xpath.text_field('form_first_name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.text_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
end
it "should be chainable" do
@query = @xpath.text_field('First Name').password_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.text_field('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#password_field' do
it "should find a password field by id or label" do
@query = @xpath.password_field('form_password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
@query = @xpath.password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
it "should be chainable" do
@query = @xpath.password_field('First Name').text_field('First Name').to_s
@driver.find(@query).first.value.should == 'John'
@query = @xpath.password_field('Password').text_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#text_area' do
it "should find a text area by id or label" do
@query = @xpath.text_area('form_description').to_s
@driver.find(@query).first.text.should == 'Descriptive text goes here'
@query = @xpath.text_area('Description').to_s
@driver.find(@query).first.text.should == 'Descriptive text goes here'
end
it "should be chainable" do
@query = @xpath.text_area('Description').password_field('Description').to_s
@driver.find(@query).first.text.should == 'Descriptive text goes here'
@query = @xpath.text_area('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#radio_button' do
it "should find a radio button by id or label" do
@query = @xpath.radio_button('Male').to_s
@driver.find(@query).first.value.should == 'male'
@query = @xpath.radio_button('gender_male').to_s
@driver.find(@query).first.value.should == 'male'
end
it "should be chainable" do
@query = @xpath.radio_button('Male').password_field('Male').to_s
@driver.find(@query).first.value.should == 'male'
@query = @xpath.radio_button('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#checkbox' do
it "should find a checkbox by id or label" do
@query = @xpath.checkbox('Cat').to_s
@driver.find(@query).first.value.should == 'cat'
@query = @xpath.checkbox('form_pets_cat').to_s
@driver.find(@query).first.value.should == 'cat'
end
it "should be chainable" do
@query = @xpath.checkbox('Cat').password_field('Cat').to_s
@driver.find(@query).first.value.should == 'cat'
@query = @xpath.checkbox('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#select' do
it "should find a select by id or label" do
@query = @xpath.select('Region').to_s
@driver.find(@query).first[:name].should == 'form[region]'
@query = @xpath.select('form_region').to_s
@driver.find(@query).first[:name].should == 'form[region]'
end
it "should be chainable" do
@query = @xpath.select('Region').password_field('Region').to_s
@driver.find(@query).first[:name].should == 'form[region]'
@query = @xpath.select('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
describe '#file_field' do
it "should find a file field by id or label" do
@query = @xpath.file_field('Document').to_s
@driver.find(@query).first[:name].should == 'form[document]'
@query = @xpath.file_field('form_document').to_s
@driver.find(@query).first[:name].should == 'form[document]'
end
it "should be chainable" do
@query = @xpath.file_field('Document').password_field('Document').to_s
@driver.find(@query).first[:name].should == 'form[document]'
@query = @xpath.file_field('Password').password_field('Password').to_s
@driver.find(@query).first.value.should == 'seeekrit'
end
end
end