cleanup!
This commit is contained in:
parent
bbc9dc73d2
commit
34a548efe8
|
@ -121,7 +121,7 @@ module Capybara
|
|||
def all(*args)
|
||||
options = if args.last.is_a?(Hash) then args.pop else {} end
|
||||
|
||||
results = Capybara::XPath.tempwrap(normalize_locator(*args)).map do |path|
|
||||
results = Capybara::XPath.wrap(normalize_locator(*args)).map do |path|
|
||||
base.find(path)
|
||||
end.flatten
|
||||
|
||||
|
|
|
@ -14,225 +14,144 @@ module Capybara
|
|||
#
|
||||
# This will generate an XPath that matches either a text field or a link.
|
||||
#
|
||||
class XPath
|
||||
module XPath
|
||||
|
||||
class << self
|
||||
include ::XPath
|
||||
include ::XPath
|
||||
extend self
|
||||
|
||||
def from_css(css)
|
||||
collection(*Nokogiri::CSS.xpath_for(css).map { |selector| ::XPath::Expression::Literal.new(:".#{selector}") }.flatten)
|
||||
end
|
||||
alias_method :for_css, :from_css
|
||||
def from_css(css)
|
||||
collection(*Nokogiri::CSS.xpath_for(css).map { |selector| ::XPath::Expression::Literal.new(:".#{selector}") }.flatten)
|
||||
end
|
||||
alias_method :for_css, :from_css
|
||||
|
||||
def collection(*args)
|
||||
::XPath::Collection.new(*args)
|
||||
end
|
||||
def collection(*args)
|
||||
::XPath::Collection.new(*args)
|
||||
end
|
||||
|
||||
def link(locator)
|
||||
link = descendant(:a)[attr(:href)]
|
||||
link[attr(:id).equals(locator) | text.is(locator) | attr(:title).is(locator) | descendant(:img)[attr(:alt).is(locator)]]
|
||||
end
|
||||
def link(locator)
|
||||
link = descendant(:a)[attr(:href)]
|
||||
link[attr(:id).equals(locator) | text.is(locator) | attr(:title).is(locator) | descendant(:img)[attr(:alt).is(locator)]]
|
||||
end
|
||||
|
||||
def content(locator)
|
||||
child(:"descendant-or-self::*")[current.n.contains(locator)]
|
||||
end
|
||||
def content(locator)
|
||||
child(:"descendant-or-self::*")[current.n.contains(locator)]
|
||||
end
|
||||
|
||||
def button(locator)
|
||||
collection(
|
||||
descendant(:input)[attr(:type).one_of('submit', 'image', 'button')][attr(:id).equals(locator) | attr(:value).is(locator)],
|
||||
descendant(:button)[attr(:id).equals(locator) | attr(:value).is(locator) | text.is(locator)],
|
||||
descendant(:input)[attr(:type).equals('image')][attr(:alt).is(locator)]
|
||||
)
|
||||
end
|
||||
def button(locator)
|
||||
collection(
|
||||
descendant(:input)[attr(:type).one_of('submit', 'image', 'button')][attr(:id).equals(locator) | attr(:value).is(locator)],
|
||||
descendant(:button)[attr(:id).equals(locator) | attr(:value).is(locator) | text.is(locator)],
|
||||
descendant(:input)[attr(:type).equals('image')][attr(:alt).is(locator)]
|
||||
)
|
||||
end
|
||||
|
||||
def link_or_button(locator)
|
||||
collection(link(locator), button(locator))
|
||||
end
|
||||
def link_or_button(locator)
|
||||
collection(link(locator), button(locator))
|
||||
end
|
||||
|
||||
def fieldset(locator)
|
||||
descendant(:fieldset)[attr(:id).equals(locator) | descendant(:legend)[text.is(locator)]]
|
||||
end
|
||||
def fieldset(locator)
|
||||
descendant(:fieldset)[attr(:id).equals(locator) | descendant(:legend)[text.is(locator)]]
|
||||
end
|
||||
|
||||
def field(locator, options={})
|
||||
if options[:with]
|
||||
fillable_field(locator, options)
|
||||
else
|
||||
xpath = descendant(:input, :textarea, :select)[attr(:type).one_of('submit', 'image', 'hidden').inverse]
|
||||
xpath = locate_field(xpath, locator)
|
||||
xpath = xpath[attr(:checked)] if options[:checked]
|
||||
xpath = xpath[attr(:checked).inverse] if options[:unchecked]
|
||||
xpath
|
||||
end
|
||||
end
|
||||
|
||||
def fillable_field(locator, options={})
|
||||
xpath = descendant(:input, :textarea)[attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file').inverse]
|
||||
def field(locator, options={})
|
||||
if options[:with]
|
||||
fillable_field(locator, options)
|
||||
else
|
||||
xpath = descendant(:input, :textarea, :select)[attr(:type).one_of('submit', 'image', 'hidden').inverse]
|
||||
xpath = locate_field(xpath, locator)
|
||||
xpath = xpath[field_value(options[:with])] if options.has_key?(:with)
|
||||
xpath = xpath[attr(:checked)] if options[:checked]
|
||||
xpath = xpath[attr(:checked).inverse] if options[:unchecked]
|
||||
xpath
|
||||
end
|
||||
|
||||
def select(locator, options={})
|
||||
xpath = locate_field(descendant(:select), locator)
|
||||
|
||||
options[:options].each do |option|
|
||||
xpath = xpath[descendant(:option).text.equals(option)]
|
||||
end if options[:options]
|
||||
|
||||
[options[:selected]].flatten.each do |option|
|
||||
xpath = xpath[descendant(:option)[attr(:selected)].text.equals(option)]
|
||||
end if options[:selected]
|
||||
|
||||
xpath
|
||||
end
|
||||
|
||||
def checkbox(locator, options={})
|
||||
xpath = locate_field(descendant(:input)[attr(:type).equals('checkbox')], locator)
|
||||
end
|
||||
|
||||
def radio_button(locator, options={})
|
||||
locate_field(descendant(:input)[attr(:type).equals('radio')], locator)
|
||||
end
|
||||
|
||||
def file_field(locator, options={})
|
||||
locate_field(descendant(:input)[attr(:type).equals('file')], locator)
|
||||
end
|
||||
|
||||
def field_value(value)
|
||||
(text.is(value) & name.equals('textarea')) | (attr(:value).equals(value) & name.equals('textarea').inverse)
|
||||
end
|
||||
|
||||
def locate_field(xpath, locator)
|
||||
collection(
|
||||
xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:id).equals(anywhere(:label)[text.is(locator)].attr(:for))],
|
||||
descendant(:label)[text.is(locator)].descendant(xpath)
|
||||
)
|
||||
end
|
||||
|
||||
def table(locator, options={})
|
||||
xpath = descendant(:table)[attr(:id).equals(locator) | descendant(:caption).contains(locator)]
|
||||
xpath = xpath[table_rows(options[:rows])] if options[:rows]
|
||||
xpath
|
||||
end
|
||||
|
||||
def table_rows(rows)
|
||||
row_conditions = descendant(:tr)[table_row(rows.first)]
|
||||
rows.drop(1).each do |row|
|
||||
row_conditions = row_conditions.next_sibling(:tr)[table_row(row)]
|
||||
end
|
||||
row_conditions
|
||||
end
|
||||
|
||||
def table_row(cells)
|
||||
cell_conditions = child(:td, :th)[text.equals(cells.first)]
|
||||
cells.drop(1).each do |cell|
|
||||
cell_conditions = cell_conditions.next_sibling(:td, :th)[text.equals(cell)]
|
||||
end
|
||||
cell_conditions
|
||||
end
|
||||
|
||||
def escape(string)
|
||||
if string.include?("'")
|
||||
string = string.split("'", -1).map do |substr|
|
||||
"'#{substr}'"
|
||||
end.join(%q{,"'",})
|
||||
"concat(#{string})"
|
||||
else
|
||||
"'#{string}'"
|
||||
end
|
||||
end
|
||||
|
||||
def wrap(path)
|
||||
if path.is_a?(self)
|
||||
path
|
||||
else
|
||||
new(path.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def tempwrap(path)
|
||||
if path.is_a?(::XPath::Expression) or path.is_a?(::XPath::Collection)
|
||||
paths = path.to_xpaths
|
||||
paths
|
||||
else
|
||||
wrap(path).paths
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
def fillable_field(locator, options={})
|
||||
xpath = descendant(:input, :textarea)[attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file').inverse]
|
||||
xpath = locate_field(xpath, locator)
|
||||
xpath = xpath[field_value(options[:with])] if options.has_key?(:with)
|
||||
xpath
|
||||
end
|
||||
|
||||
def to_s
|
||||
@paths.join(' | ')
|
||||
def select(locator, options={})
|
||||
xpath = locate_field(descendant(:select), locator)
|
||||
|
||||
options[:options].each do |option|
|
||||
xpath = xpath[descendant(:option).text.equals(option)]
|
||||
end if options[:options]
|
||||
|
||||
[options[:selected]].flatten.each do |option|
|
||||
xpath = xpath[descendant(:option)[attr(:selected)].text.equals(option)]
|
||||
end if options[:selected]
|
||||
|
||||
xpath
|
||||
end
|
||||
|
||||
def append(path)
|
||||
XPath.new(*[@paths, XPath.wrap(path).paths].flatten)
|
||||
def checkbox(locator, options={})
|
||||
xpath = locate_field(descendant(:input)[attr(:type).equals('checkbox')], locator)
|
||||
end
|
||||
|
||||
def prepend(path)
|
||||
XPath.new(*[XPath.wrap(path).paths, @paths].flatten)
|
||||
def radio_button(locator, options={})
|
||||
locate_field(descendant(:input)[attr(:type).equals('radio')], locator)
|
||||
end
|
||||
|
||||
def file_field(locator, options={})
|
||||
locate_field(descendant(:input)[attr(:type).equals('file')], locator)
|
||||
end
|
||||
|
||||
|
||||
|
||||
def field_value(value)
|
||||
(text.is(value) & name.equals('textarea')) | (attr(:value).equals(value) & name.equals('textarea').inverse)
|
||||
end
|
||||
|
||||
def option(name)
|
||||
append(".//option[normalize-space(text())=#{s(name)}]").append(".//option[contains(.,#{s(name)})]")
|
||||
descendant(:option)[text.n.is(name)]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def input_field(type, locator, options={})
|
||||
options = options.merge(:value => options[:with]) if options.has_key?(:with)
|
||||
add_field(locator, ".//input[@type='#{type}']", options)
|
||||
def locate_field(xpath, locator)
|
||||
collection(
|
||||
xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:id).equals(anywhere(:label)[text.is(locator)].attr(:for))],
|
||||
descendant(:label)[text.is(locator)].descendant(xpath)
|
||||
)
|
||||
end
|
||||
|
||||
# place this between to nodes to indicate that they should be siblings
|
||||
def sibling
|
||||
def table(locator, options={})
|
||||
xpath = descendant(:table)[attr(:id).equals(locator) | descendant(:caption).contains(locator)]
|
||||
xpath = xpath[table_rows(options[:rows])] if options[:rows]
|
||||
xpath
|
||||
end
|
||||
|
||||
def add_field(locator, field, options={})
|
||||
postfix = extract_postfix(options)
|
||||
xpath = append("#{field}[@id=#{s(locator)}]#{postfix}")
|
||||
xpath = xpath.append("#{field}[@name=#{s(locator)}]#{postfix}")
|
||||
xpath = xpath.append("#{field}[@id=//label[contains(.,#{s(locator)})]/@for]#{postfix}")
|
||||
# FIXME: Label should not be scoped to node, temporary workaround!!!
|
||||
xpath = xpath.append(".//label[contains(.,#{s(locator)})]/#{field}#{postfix}")
|
||||
xpath.prepend("#{field}[@id=//label[text()=#{s(locator)}]/@for]#{postfix}")
|
||||
def table_rows(rows)
|
||||
row_conditions = descendant(:tr)[table_row(rows.first)]
|
||||
rows.drop(1).each do |row|
|
||||
row_conditions = row_conditions.next_sibling(:tr)[table_row(row)]
|
||||
end
|
||||
row_conditions
|
||||
end
|
||||
|
||||
def extract_postfix(options)
|
||||
options.inject("") do |postfix, (key, value)|
|
||||
case key
|
||||
when :value then postfix += "[@value=#{s(value)}]"
|
||||
when :text then postfix += "[text()=#{s(value)}]"
|
||||
when :checked then postfix += "[@checked]"
|
||||
when :unchecked then postfix += "[not(@checked)]"
|
||||
when :options then postfix += value.map { |o| "[.//option/text()=#{s(o)}]" }.join
|
||||
when :selected then postfix += [value].flatten.map { |o| "[.//option[@selected]/text()=#{s(o)}]" }.join
|
||||
end
|
||||
postfix
|
||||
def table_row(cells)
|
||||
cell_conditions = child(:td, :th)[text.equals(cells.first)]
|
||||
cells.drop(1).each do |cell|
|
||||
cell_conditions = cell_conditions.next_sibling(:td, :th)[text.equals(cell)]
|
||||
end
|
||||
cell_conditions
|
||||
end
|
||||
|
||||
def escape(string)
|
||||
if string.include?("'")
|
||||
string = string.split("'", -1).map do |substr|
|
||||
"'#{substr}'"
|
||||
end.join(%q{,"'",})
|
||||
"concat(#{string})"
|
||||
else
|
||||
"'#{string}'"
|
||||
end
|
||||
end
|
||||
|
||||
# Sanitize a String for putting it into an xpath query
|
||||
def s(string)
|
||||
XPath.escape(string)
|
||||
|
||||
def wrap(path)
|
||||
if path.respond_to?(:to_xpaths)
|
||||
path.to_xpaths
|
||||
else
|
||||
[path.to_s].flatten
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
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 '.wrap' do
|
||||
it "should return an XPath unmodified" do
|
||||
Capybara::XPath.wrap(@xpath).should == @xpath
|
||||
end
|
||||
|
||||
it "should wrap a string in an xpath object" do
|
||||
@xpath = Capybara::XPath.wrap('//foo/bar')
|
||||
@xpath.should be_an_instance_of(Capybara::XPath)
|
||||
@xpath.paths.should == ['//foo/bar']
|
||||
end
|
||||
end
|
||||
|
||||
describe '#append' do
|
||||
it "should append an XPath's paths" do
|
||||
@xpath = Capybara::XPath.wrap('//test')
|
||||
@xpath = @xpath.append(Capybara::XPath.wrap('//foo/bar'))
|
||||
@xpath.paths.should == ['//test', '//foo/bar']
|
||||
end
|
||||
|
||||
it "should append an String as a new path" do
|
||||
@xpath = Capybara::XPath.wrap('//test')
|
||||
@xpath = @xpath.append('//foo/bar')
|
||||
@xpath.paths.should == ['//test', '//foo/bar']
|
||||
end
|
||||
end
|
||||
|
||||
describe '#prepend' do
|
||||
it "should prepend an XPath's paths" do
|
||||
@xpath = Capybara::XPath.wrap('//test')
|
||||
@xpath = @xpath.prepend(Capybara::XPath.wrap('//foo/bar'))
|
||||
@xpath.paths.should == ['//foo/bar', '//test']
|
||||
end
|
||||
|
||||
it "should prepend an String as a new path" do
|
||||
@xpath = Capybara::XPath.wrap('//test')
|
||||
@xpath = @xpath.prepend('//foo/bar')
|
||||
@xpath.paths.should == ['//foo/bar', '//test']
|
||||
end
|
||||
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').button('Click me!').to_s
|
||||
@driver.find(@query).first.value.should == 'John'
|
||||
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').button('Click me!').to_s
|
||||
@driver.find(@query).first.value.should == 'John'
|
||||
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').button('Click me!').to_s
|
||||
@driver.find(@query).first.text.should == 'Descriptive text goes here'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#button' do
|
||||
it "should find a button by id or content" do
|
||||
@query = @xpath.button('awe123').to_s
|
||||
@driver.find(@query).first.value.should == 'awesome'
|
||||
@query = @xpath.button('okay556').to_s
|
||||
@driver.find(@query).first.value.should == 'okay'
|
||||
@query = @xpath.button('click_me_123').to_s
|
||||
@driver.find(@query).first.value.should == 'click_me'
|
||||
@query = @xpath.button('Click me!').to_s
|
||||
@driver.find(@query).first.value.should == 'click_me'
|
||||
@query = @xpath.button('fresh_btn').to_s
|
||||
@driver.find(@query).first.value.should == 'i am fresh'
|
||||
@query = @xpath.button('i am fresh').to_s
|
||||
@driver.find(@query).first[:name].should == 'form[fresh]'
|
||||
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').button('Click me!').to_s
|
||||
@driver.find(@query).first.value.should == 'male'
|
||||
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').button('Click me!').to_s
|
||||
@driver.find(@query).first.value.should == 'cat'
|
||||
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').button('Click me!').to_s
|
||||
@driver.find(@query).first[:name].should == 'form[region]'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue