mirror of
https://github.com/teamcapybara/capybara.git
synced 2022-11-09 12:08:07 -05:00
Merge branch 'master' of github.com:jnicklas/capybara
This commit is contained in:
commit
282ffa48d4
30 changed files with 499 additions and 70 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "xpath"]
|
||||
path = xpath
|
||||
url = git://github.com/jnicklas/xpath.git
|
4
Gemfile
4
Gemfile
|
@ -1,5 +1,7 @@
|
|||
source :rubygems
|
||||
|
||||
gem 'bundler', '~> 1.0'
|
||||
gem 'xpath', :path => '../xpath' if ENV['XPATH_LOCAL']
|
||||
gemspec
|
||||
|
||||
@dependencies.delete_if {|d| d.name == "xpath" }
|
||||
gem 'xpath', :path => 'xpath'
|
||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
capybara (0.4.0)
|
||||
capybara (0.4.1.rc)
|
||||
celerity (>= 0.7.9)
|
||||
culerity (>= 0.2.4)
|
||||
mime-types (>= 1.16)
|
||||
|
@ -9,7 +9,13 @@ PATH
|
|||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (>= 0.0.27)
|
||||
xpath (~> 0.1.2)
|
||||
xpath (~> 0.1.3)
|
||||
|
||||
PATH
|
||||
remote: xpath
|
||||
specs:
|
||||
xpath (0.1.3)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
|
@ -58,8 +64,6 @@ GEM
|
|||
sinatra (1.0)
|
||||
rack (>= 1.0)
|
||||
weakling (0.0.4-java)
|
||||
xpath (0.1.2)
|
||||
nokogiri (~> 1.3)
|
||||
yard (0.6.1)
|
||||
|
||||
PLATFORMS
|
||||
|
@ -80,5 +84,5 @@ DEPENDENCIES
|
|||
rspec (>= 2.0.0)
|
||||
selenium-webdriver (>= 0.0.27)
|
||||
sinatra (>= 0.9.4)
|
||||
xpath (~> 0.1.2)
|
||||
xpath!
|
||||
yard (>= 0.5.8)
|
||||
|
|
29
History.txt
29
History.txt
|
@ -4,7 +4,34 @@ Release date:
|
|||
|
||||
### Added
|
||||
|
||||
* New click_on alias for click_link_or_button, shorter yet unambiguous.
|
||||
* New click_on alias for click_link_or_button, shorter yet unambiguous. [Jonas Nicklas]
|
||||
* Finders now accept :visible => false which will find all elements regardless of Capybara.ignore_hidden_elements [Jonas Nicklas]
|
||||
* Configure how the server is started via Capybara.server { |app, port| ... }. [John Firebough]
|
||||
* Added :between, :maximum and :minimum options to has_selector and friends [James B. Byrne]
|
||||
* New Capybara.string util function which allows matchers on arbitrary strings, mostly for helper and view specs [David Chelimsky and Jonas Nicklas]
|
||||
* Server boot timeout is now configurable, via Capybara.server_boot_timeout [Adam Cigánek]
|
||||
* Built in support for RSpec [Jonas Nicklas]
|
||||
* Capybara.using_driver to switch to a different driver temporarily [Jeff Kreeftmeijer]
|
||||
* Added Session#first which is somewhat speedier than Session#all, use it internally for speed boost [John Firebaugh]
|
||||
|
||||
### Changed
|
||||
|
||||
* Session#within now accepts the same arguments as other finders, like Session#all and Session#find [Jonas Nicklas]
|
||||
|
||||
### Removed
|
||||
|
||||
* All deprecations from 0.4.0 have been removed. [Jonas Nicklas]
|
||||
|
||||
### Fixed
|
||||
|
||||
* Don't mangle URLs in save_and_open_page when using self-closing tags [Adam Spiers]
|
||||
* Catch correct error when server boot times out [Jonas Nicklas]
|
||||
* Celerity driver now properly passes through options, making it configurable [Jonas Nicklas]
|
||||
* Better implementation of attributes in C[ue]lerity, should fix issues with attributes with strange names [Jonas Nicklas]
|
||||
* Session#find no longer swallows errors [Jonas Nicklas]
|
||||
* Fix problems with multiple file inputs [Philip Arndt]
|
||||
* Submit multipart forms as multipart under rack-test even if they contain no files [Ryan Kinderman]
|
||||
* Matchers like has_select? and has_checked_field? now work with dynamically changed values [John Firebaugh]
|
||||
|
||||
# Version 0.4.0
|
||||
|
||||
|
|
20
README.rdoc
20
README.rdoc
|
@ -90,15 +90,20 @@ If you prefer RSpec to using Cucumber, you can use the built in RSpec support:
|
|||
|
||||
You can now use it in your examples:
|
||||
|
||||
it "signs me in" do
|
||||
within("#session") do
|
||||
fill_in 'Login', :with => 'user@example.com'
|
||||
fill_in 'Password', :with => 'password'
|
||||
describe "the signup process", :type => :acceptance do
|
||||
it "signs me in" do
|
||||
within("#session") do
|
||||
fill_in 'Login', :with => 'user@example.com'
|
||||
fill_in 'Password', :with => 'password'
|
||||
end
|
||||
click_link 'Sign in'
|
||||
end
|
||||
click_link 'Sign in'
|
||||
end
|
||||
|
||||
RSpec's metadata feature can be used to switch to a different driver. Use the
|
||||
Capybara is only included for examples which have the type
|
||||
<tt>:acceptance</tt>.
|
||||
|
||||
RSpec's metadata feature can be used to switch to a different driver. Use
|
||||
<tt>:js => true</tt> to switch to the javascript driver, or provide a
|
||||
<tt>:driver</tt> option to switch to one specific driver. For example:
|
||||
|
||||
|
@ -483,6 +488,9 @@ Whatever is returned from the block should conform to the API described by
|
|||
Capybara::Driver::Base, it does not however have to inherit from this class.
|
||||
Gems can use this API to add their own drivers to Capybara.
|
||||
|
||||
The {Selenium wiki}[http://code.google.com/p/selenium/wiki/RubyBindings] has
|
||||
additional info about how the underlying driver can be configured.
|
||||
|
||||
== Gotchas:
|
||||
|
||||
* Access to session and request is not possible from the test, Access to
|
||||
|
|
|
@ -29,7 +29,7 @@ Gem::Specification.new do |s|
|
|||
s.add_runtime_dependency("selenium-webdriver", [">= 0.0.27"])
|
||||
s.add_runtime_dependency("rack", [">= 1.0.0"])
|
||||
s.add_runtime_dependency("rack-test", [">= 0.5.4"])
|
||||
s.add_runtime_dependency("xpath", ["~> 0.1.2"]) unless ENV['XPATH_LOCAL']
|
||||
s.add_runtime_dependency("xpath", ["~> 0.1.3"])
|
||||
|
||||
s.add_development_dependency("sinatra", [">= 0.9.4"])
|
||||
s.add_development_dependency("rspec", [">= 2.0.0"])
|
||||
|
|
|
@ -11,7 +11,7 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
|
|||
|
||||
def value
|
||||
if tag_name == "select" and native.multiple?
|
||||
find(".//option[@selected]").map { |n| n.value || n.text }
|
||||
find(".//option[@selected]").map { |n| if n.has_value? then n.value else n.text end }
|
||||
else
|
||||
native.value
|
||||
end
|
||||
|
@ -56,6 +56,18 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
|
|||
native.visible?
|
||||
end
|
||||
|
||||
def checked?
|
||||
native.checked?
|
||||
rescue # https://github.com/langalex/culerity/issues/issue/33
|
||||
false
|
||||
end
|
||||
|
||||
def selected?
|
||||
native.selected?
|
||||
rescue # https://github.com/langalex/culerity/issues/issue/33
|
||||
false
|
||||
end
|
||||
|
||||
def path
|
||||
native.xpath
|
||||
end
|
||||
|
@ -77,7 +89,9 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
|
|||
find('./ancestor::select').first
|
||||
end
|
||||
|
||||
|
||||
def has_value?
|
||||
native.object.hasAttribute('value')
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :app, :rack_server, :options
|
||||
|
|
|
@ -48,6 +48,14 @@ module Capybara
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def checked?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def selected?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def path
|
||||
raise NotSupportedByDriverError
|
||||
end
|
||||
|
|
|
@ -68,6 +68,14 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
string_node.visible?
|
||||
end
|
||||
|
||||
def checked?
|
||||
self[:checked]
|
||||
end
|
||||
|
||||
def selected?
|
||||
self[:selected]
|
||||
end
|
||||
|
||||
def path
|
||||
native.path
|
||||
end
|
||||
|
@ -97,6 +105,21 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
end
|
||||
|
||||
class Form < Node
|
||||
# This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
|
||||
# the class specifically when determing whether to consturct the request as multipart.
|
||||
# That check should be based solely on the form element's 'enctype' attribute value,
|
||||
# which should probably be provided to Rack::Test in its non-GET request methods.
|
||||
class NilUploadedFile < Rack::Test::UploadedFile
|
||||
def initialize
|
||||
@empty_file = Tempfile.new("nil_uploaded_file")
|
||||
@empty_file.close
|
||||
end
|
||||
|
||||
def original_filename; ""; end
|
||||
def content_type; "application/octet-stream"; end
|
||||
def path; @empty_file.path; end
|
||||
end
|
||||
|
||||
def params(button)
|
||||
params = {}
|
||||
|
||||
|
@ -122,14 +145,17 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
end
|
||||
end
|
||||
native.xpath(".//input[not(@disabled) and @type='file']").map do |input|
|
||||
unless input['value'].to_s.empty?
|
||||
if multipart?
|
||||
content_type = MIME::Types.type_for(input['value'].to_s).first.to_s
|
||||
file = Rack::Test::UploadedFile.new(input['value'].to_s, content_type)
|
||||
merge_param!(params, input['name'].to_s, file)
|
||||
else
|
||||
merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
|
||||
end
|
||||
if multipart?
|
||||
file = \
|
||||
if (value = input['value']).to_s.empty?
|
||||
NilUploadedFile.new
|
||||
else
|
||||
content_type = MIME::Types.type_for(value).first.to_s
|
||||
Rack::Test::UploadedFile.new(value, content_type)
|
||||
end
|
||||
merge_param!(params, input['name'].to_s, file)
|
||||
else
|
||||
merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
|
||||
end
|
||||
end
|
||||
merge_param!(params, button[:name], button[:value] || "") if button[:name]
|
||||
|
|
|
@ -43,7 +43,7 @@ class Capybara::Driver::Selenium < Capybara::Driver::Base
|
|||
if select_node['multiple'] != 'multiple' and select_node['multiple'] != 'true'
|
||||
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
||||
end
|
||||
native.clear
|
||||
native.toggle if selected?
|
||||
end
|
||||
|
||||
def click
|
||||
|
@ -59,9 +59,17 @@ class Capybara::Driver::Selenium < Capybara::Driver::Base
|
|||
end
|
||||
|
||||
def visible?
|
||||
native.displayed? and native.displayed? != "false"
|
||||
displayed = native.displayed?
|
||||
displayed and displayed != "false"
|
||||
end
|
||||
|
||||
def selected?
|
||||
selected = native.selected?
|
||||
selected and selected != "false"
|
||||
end
|
||||
|
||||
alias :checked? :selected?
|
||||
|
||||
def find(locator)
|
||||
native.find_elements(:xpath, locator).map { |n| self.class.new(driver, n) }
|
||||
end
|
||||
|
@ -83,7 +91,7 @@ class Capybara::Driver::Selenium < Capybara::Driver::Base
|
|||
|
||||
def browser
|
||||
unless @browser
|
||||
@browser = Selenium::WebDriver.for(options.delete(:browser) || :firefox, options)
|
||||
@browser = Selenium::WebDriver.for(options[:browser] || :firefox, options)
|
||||
at_exit do
|
||||
@browser.quit
|
||||
end
|
||||
|
|
|
@ -112,6 +112,26 @@ module Capybara
|
|||
base.visible?
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Whether or not the element is checked.
|
||||
#
|
||||
# @return [Boolean] Whether the element is checked
|
||||
#
|
||||
def checked?
|
||||
base.checked?
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Whether or not the element is selected.
|
||||
#
|
||||
# @return [Boolean] Whether the element is selected
|
||||
#
|
||||
def selected?
|
||||
base.selected?
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# An XPath expression describing where on the page the element can be found
|
||||
|
|
|
@ -24,7 +24,10 @@ module Capybara
|
|||
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
||||
#
|
||||
def find(*args)
|
||||
node = wait_conditionally_until { all(*args).first }
|
||||
begin
|
||||
node = wait_conditionally_until { first(*args) }
|
||||
rescue TimeoutError
|
||||
end
|
||||
unless node
|
||||
options = if args.last.is_a?(Hash) then args.last else {} end
|
||||
raise Capybara::ElementNotFound, options[:message] || "Unable to find '#{args[1] || args[0]}'"
|
||||
|
@ -112,23 +115,39 @@ module Capybara
|
|||
# @return [Capybara::Element] The found elements
|
||||
#
|
||||
def all(*args)
|
||||
options = if args.last.is_a?(Hash) then args.pop else {} end
|
||||
options = extract_normalized_options(args)
|
||||
|
||||
results = Capybara::Selector.normalize(*args).map do |path|
|
||||
find_in_base(path)
|
||||
end.flatten
|
||||
Capybara::Selector.normalize(*args).
|
||||
map { |path| find_in_base(path) }.flatten.
|
||||
select { |node| matches_options(node, options) }.
|
||||
map { |node| convert_element(node) }
|
||||
end
|
||||
|
||||
if text = options[:text]
|
||||
text = Regexp.escape(text) unless text.kind_of?(Regexp)
|
||||
##
|
||||
#
|
||||
# Find the first element on the page matching the given selector
|
||||
# and options, or nil if no element matches.
|
||||
#
|
||||
# When only the first matching element is needed, this method can
|
||||
# be faster than all(*args).first.
|
||||
#
|
||||
# @param [:css, :xpath, String] kind_or_locator Either the kind of selector or the selector itself
|
||||
# @param [String] locator The selector
|
||||
# @param [Hash{Symbol => Object}] options Additional options; see {all}
|
||||
# @return Capybara::Element The found element
|
||||
#
|
||||
def first(*args)
|
||||
options = extract_normalized_options(args)
|
||||
|
||||
results = results.select { |node| node.text.match(text) }
|
||||
Capybara::Selector.normalize(*args).each do |path|
|
||||
find_in_base(path).each do |node|
|
||||
if matches_options(node, options)
|
||||
return convert_element(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if options[:visible] or Capybara.ignore_hidden_elements
|
||||
results = results.select { |node| node.visible? }
|
||||
end
|
||||
|
||||
convert_elements(results)
|
||||
nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -137,14 +156,46 @@ module Capybara
|
|||
base.find(xpath)
|
||||
end
|
||||
|
||||
def convert_elements(elements)
|
||||
elements.map { |element| Capybara::Node::Element.new(session, element) }
|
||||
def convert_element(element)
|
||||
Capybara::Node::Element.new(session, element)
|
||||
end
|
||||
|
||||
def wait_conditionally_until
|
||||
if wait? then session.wait_until { yield } else yield end
|
||||
end
|
||||
|
||||
def extract_normalized_options(args)
|
||||
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
||||
|
||||
if text = options[:text]
|
||||
options[:text] = Regexp.escape(text) unless text.kind_of?(Regexp)
|
||||
end
|
||||
|
||||
if !options.has_key?(:visible)
|
||||
options[:visible] = Capybara.ignore_hidden_elements
|
||||
end
|
||||
|
||||
if selected = options[:selected]
|
||||
options[:selected] = [selected].flatten
|
||||
end
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
def matches_options(node, options)
|
||||
return false if options[:text] and not node.text.match(options[:text])
|
||||
return false if options[:visible] and not node.visible?
|
||||
return false if options[:with] and not node.value == options[:with]
|
||||
return false if options[:checked] and not node.checked?
|
||||
return false if options[:unchecked] and node.checked?
|
||||
return false if options[:selected] and not has_selected_options?(node, options[:selected])
|
||||
true
|
||||
end
|
||||
|
||||
def has_selected_options?(node, expected)
|
||||
actual = node.find('.//option').select { |option| option.selected? }.map { |option| option.text }
|
||||
(expected - actual).empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -262,7 +262,8 @@ module Capybara
|
|||
# @return [Boolean] Whether it exists
|
||||
#
|
||||
def has_field?(locator, options={})
|
||||
has_xpath?(XPath::HTML.field(locator, options))
|
||||
options, with = split_options(options, :with)
|
||||
has_xpath?(XPath::HTML.field(locator, options), with)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -275,7 +276,8 @@ module Capybara
|
|||
# @return [Boolean] Whether it doesn't exist
|
||||
#
|
||||
def has_no_field?(locator, options={})
|
||||
has_no_xpath?(XPath::HTML.field(locator, options))
|
||||
options, with = split_options(options, :with)
|
||||
has_no_xpath?(XPath::HTML.field(locator, options), with)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -288,7 +290,7 @@ module Capybara
|
|||
# @return [Boolean] Whether it exists
|
||||
#
|
||||
def has_checked_field?(locator)
|
||||
has_xpath?(XPath::HTML.field(locator, :checked => true))
|
||||
has_xpath?(XPath::HTML.field(locator), :checked => true)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -301,7 +303,7 @@ module Capybara
|
|||
# @return [Boolean] Whether it exists
|
||||
#
|
||||
def has_unchecked_field?(locator)
|
||||
has_xpath?(XPath::HTML.field(locator, :unchecked => true))
|
||||
has_xpath?(XPath::HTML.field(locator), :unchecked => true)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -328,7 +330,8 @@ module Capybara
|
|||
# @return [Boolean] Whether it exists
|
||||
#
|
||||
def has_select?(locator, options={})
|
||||
has_xpath?(XPath::HTML.select(locator, options))
|
||||
options, selected = split_options(options, :selected)
|
||||
has_xpath?(XPath::HTML.select(locator, options), selected)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -340,7 +343,8 @@ module Capybara
|
|||
# @return [Boolean] Whether it doesn't exist
|
||||
#
|
||||
def has_no_select?(locator, options={})
|
||||
has_no_xpath?(XPath::HTML.select(locator, options))
|
||||
options, selected = split_options(options, :selected)
|
||||
has_no_xpath?(XPath::HTML.select(locator, options), selected)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -375,6 +379,13 @@ module Capybara
|
|||
def has_no_table?(locator, options={})
|
||||
has_no_xpath?(XPath::HTML.table(locator, options))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def split_options(options, key)
|
||||
options = options.dup
|
||||
[options, if options.has_key?(key) then {key => options.delete(key)} else {} end]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -104,8 +104,8 @@ module Capybara
|
|||
native.xpath(xpath).map { |node| self.class.new(node) }
|
||||
end
|
||||
|
||||
def convert_elements(elements)
|
||||
elements
|
||||
def convert_element(element)
|
||||
element
|
||||
end
|
||||
|
||||
def wait?
|
||||
|
|
|
@ -2,13 +2,17 @@ require 'capybara'
|
|||
require 'capybara/dsl'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include Capybara
|
||||
config.include Capybara, :type => :acceptance
|
||||
config.after do
|
||||
Capybara.reset_sessions!
|
||||
Capybara.use_default_driver
|
||||
if example.metadata[:type] == :acceptance
|
||||
Capybara.reset_sessions!
|
||||
Capybara.use_default_driver
|
||||
end
|
||||
end
|
||||
config.before do
|
||||
Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
|
||||
Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
|
||||
if example.metadata[:type] == :acceptance
|
||||
Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
|
||||
Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ module Capybara
|
|||
#
|
||||
class Session
|
||||
DSL_METHODS = [
|
||||
:all, :attach_file, :body, :check, :choose, :click_link_or_button, :click_button, :click_link, :current_url, :drag, :evaluate_script,
|
||||
:all, :first, :attach_file, :body, :check, :choose, :click_link_or_button, :click_button, :click_link, :current_url, :drag, :evaluate_script,
|
||||
:field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?,
|
||||
:has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck,
|
||||
:visit, :wait_until, :within, :within_fieldset, :within_table, :within_frame, :within_window, :has_link?, :has_no_link?, :has_button?,
|
||||
|
@ -151,11 +151,11 @@ module Capybara
|
|||
# fill_in('Street', :with => '12 Main Street')
|
||||
# end
|
||||
#
|
||||
# @param [:css, :xpath, String] kind The type of selector or the selector if the second argument is blank
|
||||
# @param [String] selector The selector within which to execute the given block
|
||||
# @param (see Capybara::Node::Finders#all)
|
||||
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
|
||||
#
|
||||
def within(kind, selector=nil)
|
||||
new_scope = find(kind, selector, :message => "scope '#{selector || kind}' not found on page")
|
||||
def within(*args)
|
||||
new_scope = find(*args)
|
||||
begin
|
||||
scopes.push(new_scope)
|
||||
yield
|
||||
|
|
|
@ -71,6 +71,20 @@ shared_examples_for 'driver' do
|
|||
@driver.find('//div[@id="hidden"]')[0].should_not be_visible
|
||||
@driver.find('//div[@id="hidden_via_ancestor"]')[0].should_not be_visible
|
||||
end
|
||||
|
||||
it "should extract node checked state" do
|
||||
@driver.visit('/form')
|
||||
@driver.find('//input[@id="gender_female"]')[0].should be_checked
|
||||
@driver.find('//input[@id="gender_male"]')[0].should_not be_checked
|
||||
@driver.find('//h1')[0].should_not be_checked
|
||||
end
|
||||
|
||||
it "should extract node selected state" do
|
||||
@driver.visit('/form')
|
||||
@driver.find('//option[@value="en"]')[0].should be_selected
|
||||
@driver.find('//option[@value="sv"]')[0].should_not be_selected
|
||||
@driver.find('//h1')[0].should_not be_selected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -104,7 +118,7 @@ end
|
|||
shared_examples_for "driver with header support" do
|
||||
it "should make headers available through response_headers" do
|
||||
@driver.visit('/with_simple_html')
|
||||
@driver.response_headers['Content-Type'].should == 'text/html'
|
||||
@driver.response_headers['Content-Type'].should =~ /text\/html/
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ shared_examples_for "session" do
|
|||
end
|
||||
|
||||
it_should_behave_like "all"
|
||||
it_should_behave_like "first"
|
||||
it_should_behave_like "attach_file"
|
||||
it_should_behave_like "check"
|
||||
it_should_behave_like "choose"
|
||||
|
|
|
@ -56,6 +56,11 @@ shared_examples_for "all" do
|
|||
Capybara.ignore_hidden_elements = true
|
||||
@session.all("//a[@title='awesome title']").should have(1).elements
|
||||
end
|
||||
|
||||
it "should only find invisible nodes" do
|
||||
Capybara.ignore_hidden_elements = true
|
||||
@session.all("//a[@title='awesome title']", :visible => false).should have(2).elements
|
||||
end
|
||||
end
|
||||
|
||||
context "within a scope" do
|
||||
|
|
72
lib/capybara/spec/session/first_spec.rb
Normal file
72
lib/capybara/spec/session/first_spec.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
shared_examples_for "first" do
|
||||
describe '#first' do
|
||||
before do
|
||||
@session.visit('/with_html')
|
||||
end
|
||||
|
||||
it "should find the first element using the given locator" do
|
||||
@session.first('//h1').text.should == 'This is a test'
|
||||
@session.first("//input[@id='test_field']")[:value].should == 'monkey'
|
||||
end
|
||||
|
||||
it "should return nil when nothing was found" do
|
||||
@session.first('//div[@id="nosuchthing"]').should be_nil
|
||||
end
|
||||
|
||||
it "should accept an XPath instance" do
|
||||
@session.visit('/form')
|
||||
@xpath = XPath::HTML.fillable_field('Name')
|
||||
@session.first(@xpath).value.should == 'John Smith'
|
||||
end
|
||||
|
||||
context "with css selectors" do
|
||||
it "should find the first element using the given selector" do
|
||||
@session.first(:css, 'h1').text.should == 'This is a test'
|
||||
@session.first(:css, "input[id='test_field']")[:value].should == 'monkey'
|
||||
end
|
||||
end
|
||||
|
||||
context "with xpath selectors" do
|
||||
it "should find the first element using the given locator" do
|
||||
@session.first(:xpath, '//h1').text.should == 'This is a test'
|
||||
@session.first(:xpath, "//input[@id='test_field']")[:value].should == 'monkey'
|
||||
end
|
||||
end
|
||||
|
||||
context "with css as default selector" do
|
||||
before { Capybara.default_selector = :css }
|
||||
it "should find the first element using the given locator" do
|
||||
@session.first('h1').text.should == 'This is a test'
|
||||
@session.first("input[id='test_field']")[:value].should == 'monkey'
|
||||
end
|
||||
after { Capybara.default_selector = :xpath }
|
||||
end
|
||||
|
||||
context "with visible filter" do
|
||||
after { Capybara.ignore_hidden_elements = false }
|
||||
it "should only find visible nodes" do
|
||||
@session.first(:css, "a.visibility").should_not be_visible
|
||||
@session.first(:css, "a.visibility", :visible => true).should be_visible
|
||||
Capybara.ignore_hidden_elements = true
|
||||
@session.first(:css, "a.visibility").should be_visible
|
||||
end
|
||||
|
||||
it "should only find invisible nodes" do
|
||||
Capybara.ignore_hidden_elements = true
|
||||
@session.first(:css, "a.visibility", :visible => false).should_not be_visible
|
||||
end
|
||||
end
|
||||
|
||||
context "within a scope" do
|
||||
before do
|
||||
@session.visit('/with_scope')
|
||||
end
|
||||
|
||||
it "should find the first element using the given locator" do
|
||||
@session.within(:xpath, "//div[@id='for_bar']") do
|
||||
@session.first('.//form').should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,6 +25,16 @@ shared_examples_for "has_field" do
|
|||
@session.should_not have_field('Wrong Name', :with => 'John')
|
||||
@session.should_not have_field('Description', :with => 'Monkey')
|
||||
end
|
||||
|
||||
it "should be true after the field has been filled in with the given value" do
|
||||
@session.fill_in('First Name', :with => 'Jonas')
|
||||
@session.should have_field('First Name', :with => 'Jonas')
|
||||
end
|
||||
|
||||
it "should be false after the field has been filled in with a different value" do
|
||||
@session.fill_in('First Name', :with => 'Jonas')
|
||||
@session.should_not have_field('First Name', :with => 'John')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -54,6 +64,16 @@ shared_examples_for "has_field" do
|
|||
@session.should have_no_field('Wrong Name', :with => 'John')
|
||||
@session.should have_no_field('Description', :with => 'Monkey')
|
||||
end
|
||||
|
||||
it "should be false after the field has been filled in with the given value" do
|
||||
@session.fill_in('First Name', :with => 'Jonas')
|
||||
@session.should_not have_no_field('First Name', :with => 'Jonas')
|
||||
end
|
||||
|
||||
it "should be true after the field has been filled in with a different value" do
|
||||
@session.fill_in('First Name', :with => 'Jonas')
|
||||
@session.should have_no_field('First Name', :with => 'John')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -73,6 +93,26 @@ shared_examples_for "has_field" do
|
|||
it "should be false if no field is on the page" do
|
||||
@session.should_not have_checked_field('Does Not Exist')
|
||||
end
|
||||
|
||||
it "should be true after an unchecked checkbox is checked" do
|
||||
@session.check('form_pets_cat')
|
||||
@session.should have_checked_field('form_pets_cat')
|
||||
end
|
||||
|
||||
it "should be false after a checked checkbox is unchecked" do
|
||||
@session.uncheck('form_pets_dog')
|
||||
@session.should_not have_checked_field('form_pets_dog')
|
||||
end
|
||||
|
||||
it "should be true after an unchecked radio button is chosen" do
|
||||
@session.choose('gender_male')
|
||||
@session.should have_checked_field('gender_male')
|
||||
end
|
||||
|
||||
it "should be false after another radio button in the group is chosen" do
|
||||
@session.choose('gender_male')
|
||||
@session.should_not have_checked_field('gender_female')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_unchecked_field?' do
|
||||
|
@ -91,6 +131,26 @@ shared_examples_for "has_field" do
|
|||
it "should be false if no field is on the page" do
|
||||
@session.should_not have_unchecked_field('Does Not Exist')
|
||||
end
|
||||
|
||||
it "should be false after an unchecked checkbox is checked" do
|
||||
@session.check('form_pets_cat')
|
||||
@session.should_not have_unchecked_field('form_pets_cat')
|
||||
end
|
||||
|
||||
it "should be true after a checked checkbox is unchecked" do
|
||||
@session.uncheck('form_pets_dog')
|
||||
@session.should have_unchecked_field('form_pets_dog')
|
||||
end
|
||||
|
||||
it "should be false after an unchecked radio button is chosen" do
|
||||
@session.choose('gender_male')
|
||||
@session.should_not have_unchecked_field('gender_male')
|
||||
end
|
||||
|
||||
it "should be true after another radio button in the group is chosen" do
|
||||
@session.choose('gender_male')
|
||||
@session.should have_unchecked_field('gender_female')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -26,6 +26,26 @@ shared_examples_for "has_select" do
|
|||
@session.should_not have_select('Underwear', :selected => ['Briefs', 'Nonexistant'])
|
||||
@session.should_not have_select('Underwear', :selected => ['Briefs', 'Boxers'])
|
||||
end
|
||||
|
||||
it "should be true after the given value is selected" do
|
||||
@session.select('Swedish', :from => 'Locale')
|
||||
@session.should have_select('Locale', :selected => 'Swedish')
|
||||
end
|
||||
|
||||
it "should be false after a different value is selected" do
|
||||
@session.select('Swedish', :from => 'Locale')
|
||||
@session.should_not have_select('Locale', :selected => 'English')
|
||||
end
|
||||
|
||||
it "should be true after the given values are selected" do
|
||||
@session.select('Boxers', :from => 'Underwear')
|
||||
@session.should have_select('Underwear', :selected => ['Briefs', 'Boxers', 'Commando'])
|
||||
end
|
||||
|
||||
it "should be false after one of the values is unselected" do
|
||||
@session.unselect('Briefs', :from => 'Underwear')
|
||||
@session.should_not have_select('Underwear', :selected => ['Briefs', 'Commando'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with options' do
|
||||
|
@ -69,6 +89,26 @@ shared_examples_for "has_select" do
|
|||
@session.should have_no_select('Underwear', :selected => ['Briefs', 'Nonexistant'])
|
||||
@session.should have_no_select('Underwear', :selected => ['Briefs', 'Boxers'])
|
||||
end
|
||||
|
||||
it "should be false after the given value is selected" do
|
||||
@session.select('Swedish', :from => 'Locale')
|
||||
@session.should_not have_no_select('Locale', :selected => 'Swedish')
|
||||
end
|
||||
|
||||
it "should be true after a different value is selected" do
|
||||
@session.select('Swedish', :from => 'Locale')
|
||||
@session.should have_no_select('Locale', :selected => 'English')
|
||||
end
|
||||
|
||||
it "should be false after the given values are selected" do
|
||||
@session.select('Boxers', :from => 'Underwear')
|
||||
@session.should_not have_no_select('Underwear', :selected => ['Briefs', 'Boxers', 'Commando'])
|
||||
end
|
||||
|
||||
it "should be true after one of the values is unselected" do
|
||||
@session.unselect('Briefs', :from => 'Underwear')
|
||||
@session.should have_no_select('Underwear', :selected => ['Briefs', 'Commando'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with options' do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
shared_examples_for "within" do
|
||||
shared_examples_for "within" do
|
||||
describe '#within' do
|
||||
before do
|
||||
@session.visit('/with_scope')
|
||||
|
@ -11,6 +11,13 @@ shared_examples_for "within" do
|
|||
end
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
|
||||
it "should accept additional options" do
|
||||
@session.within(:css, "ul li", :text => 'With Simple HTML') do
|
||||
@session.click_link('Go')
|
||||
end
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
end
|
||||
|
||||
context "with XPath selector" do
|
||||
|
@ -32,14 +39,14 @@ shared_examples_for "within" do
|
|||
end
|
||||
|
||||
context "with the default selector set to CSS" do
|
||||
before { Capybara.default_selector = :css }
|
||||
before { Capybara.default_selector = :css }
|
||||
it "should use CSS" do
|
||||
@session.within("ul li[contains('With Simple HTML')]") do
|
||||
@session.click_link('Go')
|
||||
end
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
after { Capybara.default_selector = :xpath }
|
||||
after { Capybara.default_selector = :xpath }
|
||||
end
|
||||
|
||||
context "with click_link" do
|
||||
|
|
|
@ -77,6 +77,14 @@ class TestApp < Sinatra::Base
|
|||
'<pre id="results">' + params[:form].to_yaml + '</pre>'
|
||||
end
|
||||
|
||||
post '/upload_empty' do
|
||||
if params[:form][:file].nil?
|
||||
'Successfully ignored empty file field.'
|
||||
else
|
||||
'Something went wrong.'
|
||||
end
|
||||
end
|
||||
|
||||
post '/upload' do
|
||||
begin
|
||||
buffer = []
|
||||
|
|
|
@ -250,6 +250,17 @@
|
|||
<p>
|
||||
</form>
|
||||
|
||||
<form action="/upload_empty" method="post" enctype="multipart/form-data">
|
||||
<p>
|
||||
<label for="form_file_name">File Name</label>
|
||||
<input type="file" name="form[file]" id="form_file"/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" value="Upload Empty"/>
|
||||
<p>
|
||||
</form>
|
||||
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<p>
|
||||
<label for="form_file_name">File Name</label>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
et dolore magna aliqua. Ut enim ad minim veniam,
|
||||
quis nostrud exercitation <a href="/foo" id="foo">ullamco</a> laboris nisi
|
||||
ut aliquip ex ea commodo consequat.
|
||||
<a href="/with_simple_html"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="awesome image" /></a>
|
||||
<a href="/with_simple_html"><img width="20" height="20" alt="awesome image" /></a>
|
||||
</p>
|
||||
|
||||
<p class="para" id="second">
|
||||
|
@ -41,8 +41,8 @@
|
|||
<a href="/with_html#anchor">Anchor on same page</a>
|
||||
<input type="text" value="" id="test_field">
|
||||
<input type="text" checked="checked" id="checked_field">
|
||||
<a href="/redirect"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="very fine image" /></a>
|
||||
<a href="/with_simple_html"><img src="http://www.foobar.sun/dummy_image.jpg" width="20" height="20" alt="fine image" /></a>
|
||||
<a href="/redirect"><img width="20" height="20" alt="very fine image" /></a>
|
||||
<a href="/with_simple_html"><img width="20" height="20" alt="fine image" /></a>
|
||||
|
||||
<a href="?query_string=true">Naked Query String</a>
|
||||
<% if params[:query_string] %>
|
||||
|
@ -55,6 +55,14 @@
|
|||
<a href="/with_simple_html" title="awesome title" class="simple">hidden link</a>
|
||||
</div>
|
||||
|
||||
<div style="display: none;">
|
||||
<a class="visibility">hidden link</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a class="visibility">visible link</a>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li id="john_monkey">Monkey John</li>
|
||||
<li id="paul_monkey">Monkey Paul</li>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module Capybara
|
||||
VERSION = '0.4.0'
|
||||
VERSION = '0.4.1.rc'
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'capybara/rspec'
|
|||
|
||||
Capybara.app = TestApp
|
||||
|
||||
describe 'capybara/rspec' do
|
||||
describe 'capybara/rspec', :type => :acceptance do
|
||||
it "should include Capybara in rpsec" do
|
||||
visit('/foo')
|
||||
page.body.should include('Another World')
|
||||
|
@ -32,10 +32,16 @@ describe 'capybara/rspec' do
|
|||
end
|
||||
|
||||
it "switches to the javascript driver when giving it as metadata", :js => true do
|
||||
Capybara.current_driver.should == :selenium
|
||||
Capybara.current_driver.should == Capybara.javascript_driver
|
||||
end
|
||||
|
||||
it "switches to the given driver when giving it as metadata", :driver => :culerity do
|
||||
Capybara.current_driver.should == :culerity
|
||||
end
|
||||
end
|
||||
|
||||
describe 'capybara/rspec', :type => :other do
|
||||
it "should not include Capybara" do
|
||||
expect { visit('/') }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,16 @@ describe Capybara::Session do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#attach_file" do
|
||||
context "with multipart form" do
|
||||
it "should submit an empty form-data section if no file is submitted" do
|
||||
@session.visit("/form")
|
||||
@session.click_button("Upload Empty")
|
||||
@session.body.should include('Successfully ignored empty file field.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_should_behave_like "session"
|
||||
it_should_behave_like "session without javascript support"
|
||||
it_should_behave_like "session with headers support"
|
||||
|
|
1
xpath
Submodule
1
xpath
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit c0beb95150f7b6d4cf28d0df40019f68da5ad741
|
Loading…
Reference in a new issue