1
0
Fork 0
mirror of https://github.com/teamcapybara/capybara.git synced 2022-11-09 12:08:07 -05:00

Extract HTML xpaths into XPath gem

This commit is contained in:
Jonas Nicklas 2010-08-14 18:35:46 +02:00
parent 5f9483326c
commit d59d7bb3f7
10 changed files with 40 additions and 167 deletions

View file

@ -1,6 +1,6 @@
GIT GIT
remote: git://github.com/jnicklas/xpath.git remote: git://github.com/jnicklas/xpath.git
revision: 07cbb7a revision: 18be9a2
specs: specs:
xpath (0.0.1) xpath (0.0.1)

View file

@ -1,5 +1,6 @@
require 'timeout' require 'timeout'
require 'nokogiri' require 'nokogiri'
require 'xpath'
module Capybara module Capybara
class CapybaraError < StandardError; end class CapybaraError < StandardError; end
@ -49,7 +50,6 @@ module Capybara
autoload :Server, 'capybara/server' autoload :Server, 'capybara/server'
autoload :Session, 'capybara/session' autoload :Session, 'capybara/session'
autoload :XPath, 'capybara/xpath'
autoload :Node, 'capybara/node' autoload :Node, 'capybara/node'
autoload :Document, 'capybara/node' autoload :Document, 'capybara/node'
autoload :Element, 'capybara/node' autoload :Element, 'capybara/node'

View file

@ -36,7 +36,7 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
def set(value) def set(value)
if tag_name == 'input' and type == 'radio' if tag_name == 'input' and type == 'radio'
other_radios_xpath = ::XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
driver.html.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") } driver.html.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
native['checked'] = 'checked' native['checked'] = 'checked'
elsif tag_name == 'input' and type == 'checkbox' elsif tag_name == 'input' and type == 'checkbox'

View file

@ -11,7 +11,7 @@ module Capybara
# #
def click_link_or_button(locator) def click_link_or_button(locator)
msg = "no link or button '#{locator}' found" msg = "no link or button '#{locator}' found"
find(:xpath, Capybara::XPath.link_or_button(locator), :message => msg).click find(:xpath, XPath::HTML.link_or_button(locator), :message => msg).click
end end
## ##
@ -23,7 +23,7 @@ module Capybara
# #
def click_link(locator) def click_link(locator)
msg = "no link with title, id or text '#{locator}' found" msg = "no link with title, id or text '#{locator}' found"
find(:xpath, Capybara::XPath.link(locator), :message => msg).click find(:xpath, XPath::HTML.link(locator), :message => msg).click
end end
## ##
@ -34,7 +34,7 @@ module Capybara
# #
def click_button(locator) def click_button(locator)
msg = "no button with value or id or text '#{locator}' found" msg = "no button with value or id or text '#{locator}' found"
find(:xpath, Capybara::XPath.button(locator), :message => msg).click find(:xpath, XPath::HTML.button(locator), :message => msg).click
end end
## ##
@ -50,7 +50,7 @@ module Capybara
def fill_in(locator, options={}) def fill_in(locator, options={})
msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found" msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found"
raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with) raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
find(:xpath, Capybara::XPath.fillable_field(locator), :message => msg).set(options[:with]) find(:xpath, XPath::HTML.fillable_field(locator), :message => msg).set(options[:with])
end end
## ##
@ -64,7 +64,7 @@ module Capybara
# #
def choose(locator) def choose(locator)
msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found" msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found"
find(:xpath, Capybara::XPath.radio_button(locator), :message => msg).set(true) find(:xpath, XPath::HTML.radio_button(locator), :message => msg).set(true)
end end
## ##
@ -78,7 +78,7 @@ module Capybara
# #
def check(locator) def check(locator)
msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found" msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found"
find(:xpath, Capybara::XPath.checkbox(locator), :message => msg).set(true) find(:xpath, XPath::HTML.checkbox(locator), :message => msg).set(true)
end end
## ##
@ -92,7 +92,7 @@ module Capybara
# #
def uncheck(locator) def uncheck(locator)
msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found" msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found"
find(:xpath, Capybara::XPath.checkbox(locator), :message => msg).set(false) find(:xpath, XPath::HTML.checkbox(locator), :message => msg).set(false)
end end
## ##
@ -108,8 +108,8 @@ module Capybara
def select(value, options={}) def select(value, options={})
no_select_msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found" no_select_msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found"
no_option_msg = "cannot select option, no option with text '#{value}' in select box '#{options[:from]}'" no_option_msg = "cannot select option, no option with text '#{value}' in select box '#{options[:from]}'"
select = find(:xpath, Capybara::XPath.select(options[:from]), :message => no_select_msg) select = find(:xpath, XPath::HTML.select(options[:from]), :message => no_select_msg)
select.find(:xpath, Capybara::XPath.option(value), :message => no_option_msg).select_option select.find(:xpath, XPath::HTML.option(value), :message => no_option_msg).select_option
end end
## ##
@ -125,8 +125,8 @@ module Capybara
def unselect(value, options={}) def unselect(value, options={})
no_select_msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found" no_select_msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found"
no_option_msg = "cannot unselect option, no option with text '#{value}' in select box '#{options[:from]}'" no_option_msg = "cannot unselect option, no option with text '#{value}' in select box '#{options[:from]}'"
select = find(:xpath, Capybara::XPath.select(options[:from]), :message => no_select_msg) select = find(:xpath, XPath::HTML.select(options[:from]), :message => no_select_msg)
select.find(:xpath, Capybara::XPath.option(value), :message => no_option_msg).unselect_option select.find(:xpath, XPath::HTML.option(value), :message => no_option_msg).unselect_option
end end
## ##
@ -141,7 +141,7 @@ module Capybara
# #
def attach_file(locator, path) def attach_file(locator, path)
msg = "cannot attach file, no file field with id, name, or label '#{locator}' found" msg = "cannot attach file, no file field with id, name, or label '#{locator}' found"
find(:xpath, Capybara::XPath.file_field(locator), :message => msg).set(path) find(:xpath, XPath::HTML.file_field(locator), :message => msg).set(path)
end end
## ##

View file

@ -47,7 +47,7 @@ module Capybara
# @return [Capybara::Element] The found element # @return [Capybara::Element] The found element
# #
def find_field(locator) def find_field(locator)
find(:xpath, Capybara::XPath.field(locator)) find(:xpath, XPath::HTML.field(locator))
end end
alias_method :field_labeled, :find_field alias_method :field_labeled, :find_field
@ -59,7 +59,7 @@ module Capybara
# @return [Capybara::Element] The found element # @return [Capybara::Element] The found element
# #
def find_link(locator) def find_link(locator)
find(:xpath, Capybara::XPath.link(locator)) find(:xpath, XPath::HTML.link(locator))
end end
## ##
@ -70,7 +70,7 @@ module Capybara
# @return [Capybara::Element] The found element # @return [Capybara::Element] The found element
# #
def find_button(locator) def find_button(locator)
find(:xpath, Capybara::XPath.button(locator)) find(:xpath, XPath::HTML.button(locator))
end end
## ##
@ -121,7 +121,7 @@ module Capybara
def all(*args) def all(*args)
options = if args.last.is_a?(Hash) then args.pop else {} end options = if args.last.is_a?(Hash) then args.pop else {} end
results = Capybara::XPath.wrap(normalize_locator(*args)).map do |path| results = XPath::HTML.wrap(normalize_locator(*args)).map do |path|
base.find(path) base.find(path)
end.flatten end.flatten
@ -142,7 +142,7 @@ module Capybara
def normalize_locator(kind, locator=nil) def normalize_locator(kind, locator=nil)
kind, locator = Capybara.default_selector, kind if locator.nil? kind, locator = Capybara.default_selector, kind if locator.nil?
locator = Capybara::XPath.from_css(locator) if kind == :css locator = XPath::HTML.from_css(locator) if kind == :css
locator locator
end end

View file

@ -30,67 +30,67 @@ module Capybara
end end
def has_css?(path, options={}) def has_css?(path, options={})
has_xpath?(Capybara::XPath.from_css(path), options) has_xpath?(XPath::HTML.from_css(path), options)
end end
def has_no_css?(path, options={}) def has_no_css?(path, options={})
has_no_xpath?(Capybara::XPath.from_css(path), options) has_no_xpath?(XPath::HTML.from_css(path), options)
end end
def has_content?(content) def has_content?(content)
has_xpath?(Capybara::XPath.content(content)) has_xpath?(XPath::HTML.content(content))
end end
def has_no_content?(content) def has_no_content?(content)
has_no_xpath?(Capybara::XPath.content(content)) has_no_xpath?(XPath::HTML.content(content))
end end
def has_link?(locator) def has_link?(locator)
has_xpath?(Capybara::XPath.link(locator)) has_xpath?(XPath::HTML.link(locator))
end end
def has_no_link?(locator) def has_no_link?(locator)
has_no_xpath?(Capybara::XPath.link(locator)) has_no_xpath?(XPath::HTML.link(locator))
end end
def has_button?(locator) def has_button?(locator)
has_xpath?(Capybara::XPath.button(locator)) has_xpath?(XPath::HTML.button(locator))
end end
def has_no_button?(locator) def has_no_button?(locator)
has_no_xpath?(Capybara::XPath.button(locator)) has_no_xpath?(XPath::HTML.button(locator))
end end
def has_field?(locator, options={}) def has_field?(locator, options={})
has_xpath?(Capybara::XPath.field(locator, options)) has_xpath?(XPath::HTML.field(locator, options))
end end
def has_no_field?(locator, options={}) def has_no_field?(locator, options={})
has_no_xpath?(Capybara::XPath.field(locator, options)) has_no_xpath?(XPath::HTML.field(locator, options))
end end
def has_checked_field?(locator) def has_checked_field?(locator)
has_xpath?(Capybara::XPath.field(locator, :checked => true)) has_xpath?(XPath::HTML.field(locator, :checked => true))
end end
def has_unchecked_field?(locator) def has_unchecked_field?(locator)
has_xpath?(Capybara::XPath.field(locator, :unchecked => true)) has_xpath?(XPath::HTML.field(locator, :unchecked => true))
end end
def has_select?(locator, options={}) def has_select?(locator, options={})
has_xpath?(Capybara::XPath.select(locator, options)) has_xpath?(XPath::HTML.select(locator, options))
end end
def has_no_select?(locator, options={}) def has_no_select?(locator, options={})
has_no_xpath?(Capybara::XPath.select(locator, options)) has_no_xpath?(XPath::HTML.select(locator, options))
end end
def has_table?(locator, options={}) def has_table?(locator, options={})
has_xpath?(Capybara::XPath.table(locator, options)) has_xpath?(XPath::HTML.table(locator, options))
end end
def has_no_table?(locator, options={}) def has_no_table?(locator, options={})
has_no_xpath?(Capybara::XPath.table(locator, options)) has_no_xpath?(XPath::HTML.table(locator, options))
end end
end end
end end

View file

@ -170,7 +170,7 @@ module Capybara
# @param [String] locator Id or legend of the fieldset # @param [String] locator Id or legend of the fieldset
# #
def within_fieldset(locator) def within_fieldset(locator)
within :xpath, Capybara::XPath.fieldset(locator) do within :xpath, XPath::HTML.fieldset(locator) do
yield yield
end end
end end
@ -182,7 +182,7 @@ module Capybara
# @param [String] locator Id or caption of the table # @param [String] locator Id or caption of the table
# #
def within_table(locator) def within_table(locator)
within :xpath, Capybara::XPath.table(locator) do within :xpath, XPath::HTML.table(locator) do
yield yield
end end
end end

View file

@ -16,7 +16,7 @@ shared_examples_for "all" do
it "should accept an XPath instance" do it "should accept an XPath instance" do
@session.visit('/form') @session.visit('/form')
@xpath = Capybara::XPath.fillable_field('Name') @xpath = XPath::HTML.fillable_field('Name')
@result = @session.all(@xpath).map { |r| r.value } @result = @session.all(@xpath).map { |r| r.value }
@result.should include('Smith', 'John', 'John Smith') @result.should include('Smith', 'John', 'John Smith')
end end

View file

@ -72,7 +72,7 @@ shared_examples_for "find" do
it "should accept an XPath instance and respect the order of paths" do it "should accept an XPath instance and respect the order of paths" do
@session.visit('/form') @session.visit('/form')
@xpath = Capybara::XPath.fillable_field('Name') @xpath = XPath::HTML.fillable_field('Name')
@session.find(@xpath).value.should == 'John Smith' @session.find(@xpath).value.should == 'John Smith'
end end

View file

@ -1,127 +0,0 @@
require 'xpath'
module Capybara
module XPath
include ::XPath
extend self
def from_css(css)
XPath::Union.new(*Nokogiri::CSS.xpath_for(css).map { |selector| ::XPath::Expression::Literal.new(:".#{selector}") }.flatten)
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 button(locator)
button = descendant(:input)[attr(:type).one_of('submit', 'image', 'button')][attr(:id).equals(locator) | attr(:value).is(locator)]
button += descendant(:button)[attr(:id).equals(locator) | attr(:value).is(locator) | text.is(locator)]
button += descendant(:input)[attr(:type).equals('image')][attr(:alt).is(locator)]
end
def link_or_button(locator)
link(locator) + button(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')]
xpath = locate_field(xpath, locator)
xpath = xpath[attr(:checked)] if options[:checked]
xpath = xpath[~attr(:checked)] 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')]
xpath = locate_field(xpath, locator)
xpath = xpath[field_value(options[:with])] if options.has_key?(:with)
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 option(name)
descendant(:option)[text.n.is(name)]
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 wrap(path)
if path.respond_to?(:to_xpaths)
path.to_xpaths
else
[path.to_s].flatten
end
end
protected
def locate_field(xpath, locator)
locate_field = xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:id).equals(anywhere(:label)[text.is(locator)].attr(:for))]
locate_field += descendant(:label)[text.is(locator)].descendant(xpath)
end
def field_value(value)
(text.is(value) & name.equals('textarea')) | (attr(:value).equals(value) & ~name.equals('textarea'))
end
end
end