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
2666951cac
29 changed files with 661 additions and 108 deletions
67
Gemfile.lock
67
Gemfile.lock
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
capybara (0.4.1.rc)
|
||||
capybara (0.4.1.1)
|
||||
celerity (>= 0.7.9)
|
||||
culerity (>= 0.2.4)
|
||||
mime-types (>= 1.16)
|
||||
|
@ -20,51 +20,67 @@ PATH
|
|||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
celerity (0.8.2)
|
||||
childprocess (0.1.4)
|
||||
builder (3.0.0)
|
||||
celerity (0.8.7)
|
||||
childprocess (0.1.6)
|
||||
ffi (~> 0.6.3)
|
||||
configuration (1.1.0)
|
||||
culerity (0.2.12)
|
||||
configuration (1.2.0)
|
||||
cucumber (0.10.0)
|
||||
builder (>= 2.1.2)
|
||||
diff-lcs (~> 1.1.2)
|
||||
gherkin (~> 2.3.2)
|
||||
json (~> 1.4.6)
|
||||
term-ansicolor (~> 1.0.5)
|
||||
culerity (0.2.15)
|
||||
diff-lcs (1.1.2)
|
||||
ffi (0.6.3)
|
||||
rake (>= 0.8.7)
|
||||
ffi (0.6.3-java)
|
||||
fuubar (0.0.1)
|
||||
progressbar (~> 0.9)
|
||||
fuubar (0.0.3)
|
||||
rspec (~> 2.0)
|
||||
rspec-instafail (~> 0.1)
|
||||
rspec-instafail (~> 0.1.4)
|
||||
ruby-progressbar (~> 0.0.9)
|
||||
gherkin (2.3.3)
|
||||
json (~> 1.4.6)
|
||||
gherkin (2.3.3-java)
|
||||
json (~> 1.4.6)
|
||||
json (1.4.6)
|
||||
json (1.4.6-java)
|
||||
json_pure (1.4.6)
|
||||
launchy (0.3.7)
|
||||
configuration (>= 0.0.5)
|
||||
rake (>= 0.8.1)
|
||||
mime-types (1.16)
|
||||
nokogiri (1.4.3.1)
|
||||
nokogiri (1.4.3.1-java)
|
||||
nokogiri (1.4.4)
|
||||
nokogiri (1.4.4-java)
|
||||
weakling (>= 0.0.3)
|
||||
progressbar (0.9.0)
|
||||
rack (1.2.1)
|
||||
rack-test (0.5.6)
|
||||
rack-test (0.5.7)
|
||||
rack (>= 1.0)
|
||||
rake (0.8.7)
|
||||
rspec (2.1.0)
|
||||
rspec-core (~> 2.1.0)
|
||||
rspec-expectations (~> 2.1.0)
|
||||
rspec-mocks (~> 2.1.0)
|
||||
rspec-core (2.1.0)
|
||||
rspec-expectations (2.1.0)
|
||||
rspec (2.4.0)
|
||||
rspec-core (~> 2.4.0)
|
||||
rspec-expectations (~> 2.4.0)
|
||||
rspec-mocks (~> 2.4.0)
|
||||
rspec-core (2.4.0)
|
||||
rspec-expectations (2.4.0)
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-instafail (0.1.3)
|
||||
rspec-mocks (2.1.0)
|
||||
rspec-instafail (0.1.5)
|
||||
rspec-mocks (2.4.0)
|
||||
ruby-progressbar (0.0.9)
|
||||
rubyzip (0.9.4)
|
||||
selenium-webdriver (0.0.29)
|
||||
childprocess (>= 0.0.7)
|
||||
selenium-webdriver (0.1.2)
|
||||
childprocess (~> 0.1.5)
|
||||
ffi (~> 0.6.3)
|
||||
json_pure
|
||||
rubyzip
|
||||
sinatra (1.0)
|
||||
rack (>= 1.0)
|
||||
sinatra (1.1.2)
|
||||
rack (~> 1.1)
|
||||
tilt (~> 1.2)
|
||||
term-ansicolor (1.0.5)
|
||||
tilt (1.2.2)
|
||||
weakling (0.0.4-java)
|
||||
yard (0.6.1)
|
||||
yard (0.6.4)
|
||||
|
||||
PLATFORMS
|
||||
java
|
||||
|
@ -74,6 +90,7 @@ DEPENDENCIES
|
|||
bundler (~> 1.0)
|
||||
capybara!
|
||||
celerity (>= 0.7.9)
|
||||
cucumber (>= 0.10)
|
||||
culerity (>= 0.2.4)
|
||||
fuubar (>= 0.0.1)
|
||||
launchy (>= 0.3.5)
|
||||
|
|
|
@ -8,7 +8,7 @@ Release date:
|
|||
|
||||
# Version 0.4.1
|
||||
|
||||
Release date:
|
||||
Release date: 2011-01-21
|
||||
|
||||
### Added
|
||||
|
||||
|
@ -40,6 +40,8 @@ Release date:
|
|||
* 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]
|
||||
* Preserve order of rack params [Joel Chippindale]
|
||||
* RackTest#reset! is more thorough [Joel Chippindale]
|
||||
|
||||
# Version 0.4.0
|
||||
|
||||
|
|
34
README.rdoc
34
README.rdoc
|
@ -83,14 +83,14 @@ There are also explicit <tt>@selenium</tt>, <tt>@culerity</tt> and
|
|||
|
||||
== Using Capybara with RSpec
|
||||
|
||||
If you prefer RSpec to using Cucumber, you can use the built in RSpec support:
|
||||
If you prefer RSpec to using Cucumber, you can use the built in RSpec support
|
||||
by adding the following line (typically to your <tt>spec_helper.rb</tt> file):
|
||||
|
||||
require 'capybara/rspec'
|
||||
Capybara.app = MyRackApp
|
||||
|
||||
You can now use it in your examples:
|
||||
|
||||
describe "the signup process", :type => :acceptance do
|
||||
describe "the signup process", :type => :request do
|
||||
it "signs me in" do
|
||||
within("#session") do
|
||||
fill_in 'Login', :with => 'user@example.com'
|
||||
|
@ -100,8 +100,14 @@ You can now use it in your examples:
|
|||
end
|
||||
end
|
||||
|
||||
Capybara is only included for examples which have the type
|
||||
<tt>:acceptance</tt>.
|
||||
Capybara is only included for examples with <tt>:type => :request</tt> (or
|
||||
<tt>:acceptance</tt> for compatibility).
|
||||
|
||||
Note that if you use the <tt>rspec-rails</tt> gem, <tt>:type => :request</tt>
|
||||
is automatically set on all files under <tt>spec/requests</tt> (and also
|
||||
<tt>spec/integration</tt>), so that's a good directory to place your Capybara specs
|
||||
in. <tt>rspec-rails</tt> will also automatically include Capybara in
|
||||
<tt>:controller</tt> and <tt>:mailer</tt> examples.
|
||||
|
||||
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
|
||||
|
@ -115,13 +121,25 @@ RSpec's metadata feature can be used to switch to a different driver. Use
|
|||
Note that Capybara's built in RSpec support only works with RSpec 2.0 or later.
|
||||
You'll need to roll your own for earlier versions of RSpec.
|
||||
|
||||
== Using Capybara with Ruby on Rails
|
||||
|
||||
You can use the built-in Rails support to easily get Capybara running with
|
||||
Rails:
|
||||
|
||||
requires 'capybara/rails'
|
||||
|
||||
== Using Capybara with Rack
|
||||
|
||||
If you're using Capybara with a non-Rails Rack application, set
|
||||
<tt>Capybara.app</tt> to your application class:
|
||||
|
||||
Capybara.app = MyRackApp
|
||||
|
||||
== Default and current driver
|
||||
|
||||
You can set up a default driver for your features. For example if you'd prefer
|
||||
to run Selenium, you could do:
|
||||
|
||||
require 'capybara/rails'
|
||||
require 'capybara/cucumber'
|
||||
Capybara.default_driver = :selenium
|
||||
|
||||
You can change the driver temporarily:
|
||||
|
@ -223,7 +241,7 @@ certain elements, and working with and manipulating those elements.
|
|||
page.has_css?('table tr.foo')
|
||||
page.has_content?('foo')
|
||||
|
||||
You can these use with RSpec's magic matchers:
|
||||
You can use these with RSpec's magic matchers:
|
||||
|
||||
page.should have_selector('table tr')
|
||||
page.should have_selector(:xpath, '//table/tr')
|
||||
|
|
|
@ -36,4 +36,5 @@ Gem::Specification.new do |s|
|
|||
s.add_development_dependency("launchy", [">= 0.3.5"])
|
||||
s.add_development_dependency("yard", [">= 0.5.8"])
|
||||
s.add_development_dependency("fuubar", [">= 0.0.1"])
|
||||
s.add_development_dependency("cucumber", [">= 0.10"])
|
||||
end
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
ENV['RACK_ENV'] = "production"
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
require File.expand_path('lib/capybara/spec/test_app', File.dirname(__FILE__))
|
||||
|
||||
run TestApp
|
23
features/capybara.feature
Normal file
23
features/capybara.feature
Normal file
|
@ -0,0 +1,23 @@
|
|||
Feature: Capybara's cucumber integration
|
||||
In order to integrate with the lovely plain text testing framework
|
||||
As Capybara
|
||||
I want to integrate successfully with Cucumber
|
||||
|
||||
Scenario: hello world
|
||||
When I visit the root page
|
||||
Then I should see "Hello world!"
|
||||
|
||||
@javascript
|
||||
Scenario: javascript tag
|
||||
When I visit the root page
|
||||
Then Capybara should use the "selenium" driver
|
||||
|
||||
@selenium
|
||||
Scenario: selenium tag
|
||||
When I visit the root page
|
||||
Then Capybara should use the "selenium" driver
|
||||
|
||||
Scenario: matchers
|
||||
When I visit the root page
|
||||
And I use a matcher that fails
|
||||
Then the failing exception should be nice
|
24
features/step_definitions/capybara_steps.rb
Normal file
24
features/step_definitions/capybara_steps.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
When /^I visit the root page$/ do
|
||||
visit('/')
|
||||
end
|
||||
|
||||
Then /^I should see "([^"]*)"$/ do |text|
|
||||
page.should have_content(text)
|
||||
end
|
||||
|
||||
Then /^Capybara should use the "([^"]*)" driver$/ do |driver|
|
||||
Capybara.current_driver.should == driver.to_sym
|
||||
end
|
||||
|
||||
When /^I use a matcher that fails$/ do
|
||||
begin
|
||||
page.should have_css('h1#doesnotexist')
|
||||
rescue StandardError => e
|
||||
@error_message = e.message
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the failing exception should be nice$/ do
|
||||
@error_message.should =~ %r(expected css \"h1#doesnotexist\" to return)
|
||||
end
|
||||
|
7
features/support/env.rb
Normal file
7
features/support/env.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
|
||||
require 'capybara/cucumber'
|
||||
require 'capybara/spec/test_app'
|
||||
|
||||
Capybara.app = TestApp
|
|
@ -74,7 +74,7 @@ module Capybara
|
|||
# xpath { |num| ".//tbody/tr[#{num}]" }
|
||||
# end
|
||||
#
|
||||
# This makes it possible to use this selector in a cariety of ways:
|
||||
# This makes it possible to use this selector in a variety of ways:
|
||||
#
|
||||
# find(:row, 3)
|
||||
# page.find('table#myTable').find(:row, 3).text
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
require 'capybara'
|
||||
require 'capybara/dsl'
|
||||
require 'capybara/rspec_matchers'
|
||||
|
||||
World(Capybara)
|
||||
World(Capybara::RSpecMatchers)
|
||||
|
||||
After do
|
||||
Capybara.reset_sessions!
|
||||
|
|
|
@ -123,39 +123,43 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
def params(button)
|
||||
params = {}
|
||||
|
||||
native.xpath(".//input[not(@disabled) and (not(@type) or (@type!='radio' and @type!='file' and @type!='checkbox' and @type!='submit' and @type!='image'))]").map do |input|
|
||||
merge_param!(params, input['name'].to_s, input['value'].to_s)
|
||||
end
|
||||
native.xpath(".//textarea[not(@disabled)]").map do |textarea|
|
||||
merge_param!(params, textarea['name'].to_s, textarea.text.to_s)
|
||||
end
|
||||
native.xpath(".//input[not(@disabled) and (@type='radio' or @type='checkbox')]").map do |input|
|
||||
merge_param!(params, input['name'].to_s, input['value'].to_s) if input['checked']
|
||||
end
|
||||
native.xpath(".//select[not(@disabled)]").map do |select|
|
||||
if select['multiple'] == 'multiple'
|
||||
options = select.xpath(".//option[@selected]")
|
||||
options.each do |option|
|
||||
merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s)
|
||||
end
|
||||
else
|
||||
option = select.xpath(".//option[@selected]").first
|
||||
option ||= select.xpath('.//option').first
|
||||
merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s) if option
|
||||
end
|
||||
end
|
||||
native.xpath(".//input[not(@disabled) and @type='file']").map do |input|
|
||||
if multipart?
|
||||
file = \
|
||||
if (value = input['value']).to_s.empty?
|
||||
NilUploadedFile.new
|
||||
native.xpath("(.//input|.//select|.//textarea)[not(@disabled)]").map do |field|
|
||||
case field.name
|
||||
when 'input'
|
||||
if %w(radio checkbox).include? field['type']
|
||||
merge_param!(params, field['name'].to_s, field['value'].to_s) if field['checked']
|
||||
elsif %w(submit image).include? field['type']
|
||||
# TO DO identify the click button here (in document order, rather
|
||||
# than leaving until the end of the params)
|
||||
elsif field['type'] =='file'
|
||||
if multipart?
|
||||
file = \
|
||||
if (value = field['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, field['name'].to_s, file)
|
||||
else
|
||||
content_type = MIME::Types.type_for(value).first.to_s
|
||||
Rack::Test::UploadedFile.new(value, content_type)
|
||||
merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
|
||||
end
|
||||
merge_param!(params, input['name'].to_s, file)
|
||||
else
|
||||
merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
|
||||
else
|
||||
merge_param!(params, field['name'].to_s, field['value'].to_s)
|
||||
end
|
||||
when 'select'
|
||||
if field['multiple'] == 'multiple'
|
||||
options = field.xpath(".//option[@selected]")
|
||||
options.each do |option|
|
||||
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
|
||||
end
|
||||
else
|
||||
option = field.xpath(".//option[@selected]").first
|
||||
option ||= field.xpath('.//option').first
|
||||
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
|
||||
end
|
||||
when 'textarea'
|
||||
merge_param!(params, field['name'].to_s, field.text.to_s)
|
||||
end
|
||||
end
|
||||
merge_param!(params, button[:name], button[:value] || "") if button[:name]
|
||||
|
@ -249,7 +253,7 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
alias_method :source, :body
|
||||
|
||||
def reset!
|
||||
clear_cookies
|
||||
clear_rack_mock_session
|
||||
end
|
||||
|
||||
def get(*args, &block); reset_cache; super; end
|
||||
|
@ -275,6 +279,13 @@ private
|
|||
Rack::MockSession.new(app, Capybara.default_host || "www.example.com")
|
||||
end
|
||||
|
||||
# Rack::Test::Methods does not provide methods for manipulating the session
|
||||
# list so these must be manipulated directly.
|
||||
def clear_rack_mock_session
|
||||
@_rack_test_sessions = nil
|
||||
@_rack_mock_sessions = nil
|
||||
end
|
||||
|
||||
def request_path
|
||||
request.path rescue ""
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ require 'capybara'
|
|||
|
||||
module Capybara
|
||||
class << self
|
||||
attr_writer :default_driver, :current_driver, :javascript_driver
|
||||
attr_writer :default_driver, :current_driver, :javascript_driver, :session_name
|
||||
|
||||
attr_accessor :app
|
||||
|
||||
|
@ -57,7 +57,7 @@ module Capybara
|
|||
# @return [Capybara::Session] The currently used session
|
||||
#
|
||||
def current_session
|
||||
session_pool["#{current_driver}#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
|
||||
session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -70,6 +70,27 @@ module Capybara
|
|||
end
|
||||
alias_method :reset!, :reset_sessions!
|
||||
|
||||
##
|
||||
#
|
||||
# The current session name.
|
||||
#
|
||||
# @return [Symbol] The name of the currently used session.
|
||||
#
|
||||
def session_name
|
||||
@session_name ||= :default
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Yield a block using a specific session name.
|
||||
#
|
||||
def using_session(name)
|
||||
self.session_name = name
|
||||
yield
|
||||
ensure
|
||||
self.session_name = :default
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def session_pool
|
||||
|
@ -79,6 +100,15 @@ module Capybara
|
|||
|
||||
extend(self)
|
||||
|
||||
##
|
||||
#
|
||||
# Shortcut to working in a different session. This is useful when Capybara is included
|
||||
# in a class or module.
|
||||
#
|
||||
def using_session(name, &block)
|
||||
Capybara.using_session(name, &block)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Shortcut to accessing the current session. This is useful when Capybara is included in a
|
||||
|
|
|
@ -29,8 +29,11 @@ module Capybara
|
|||
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]}'"
|
||||
options = extract_normalized_options(args)
|
||||
normalized = Capybara::Selector.normalize(*args)
|
||||
message = options[:message] || "Unable to find #{normalized[:selector].name} #{normalized[:locator].inspect}"
|
||||
message = normalized[:selector].failure_message.call(self) if normalized[:selector].failure_message
|
||||
raise Capybara::ElementNotFound, message
|
||||
end
|
||||
return node
|
||||
end
|
||||
|
@ -117,7 +120,7 @@ module Capybara
|
|||
def all(*args)
|
||||
options = extract_normalized_options(args)
|
||||
|
||||
Capybara::Selector.normalize(*args).
|
||||
Capybara::Selector.normalize(*args)[:xpaths].
|
||||
map { |path| find_in_base(path) }.flatten.
|
||||
select { |node| matches_options(node, options) }.
|
||||
map { |node| convert_element(node) }
|
||||
|
@ -140,7 +143,7 @@ module Capybara
|
|||
options = extract_normalized_options(args)
|
||||
found_elements = []
|
||||
|
||||
Capybara::Selector.normalize(*args).each do |path|
|
||||
Capybara::Selector.normalize(*args)[:xpaths].each do |path|
|
||||
find_in_base(path).each do |node|
|
||||
if matches_options(node, options)
|
||||
found_elements << convert_element(node)
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
require 'capybara'
|
||||
require 'capybara/dsl'
|
||||
require 'rspec/core'
|
||||
require 'capybara/rspec_matchers'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include Capybara, :type => :request
|
||||
config.include Capybara, :type => :acceptance
|
||||
config.include Capybara::RSpecMatchers, :type => :request
|
||||
config.include Capybara::RSpecMatchers, :type => :acceptance
|
||||
# The before and after blocks must run instantaneously, because Capybara
|
||||
# might not actually be used in all examples where it's included.
|
||||
config.after do
|
||||
if example.metadata[:type] == :acceptance
|
||||
if self.class.include?(Capybara)
|
||||
Capybara.reset_sessions!
|
||||
Capybara.use_default_driver
|
||||
end
|
||||
end
|
||||
config.before do
|
||||
if example.metadata[:type] == :acceptance
|
||||
if self.class.include?(Capybara)
|
||||
Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
|
||||
Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
|
||||
end
|
||||
|
|
61
lib/capybara/rspec_matchers.rb
Normal file
61
lib/capybara/rspec_matchers.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
module Capybara
|
||||
module RSpecMatchers
|
||||
class HaveSelector
|
||||
def initialize(*args)
|
||||
@args = args
|
||||
end
|
||||
|
||||
def matches?(actual)
|
||||
@actual = wrap(actual)
|
||||
@actual.has_selector?(*@args)
|
||||
end
|
||||
|
||||
def does_not_match?(actual)
|
||||
@actual = wrap(actual)
|
||||
@actual.has_no_selector?(*@args)
|
||||
end
|
||||
|
||||
def failure_message_for_should
|
||||
if normalized[:selector].failure_message
|
||||
normalized[:selector].failure_message.call(@actual)
|
||||
else
|
||||
"expected #{selector_name} to return something"
|
||||
end
|
||||
end
|
||||
|
||||
def failure_message_for_should_not
|
||||
"expected #{selector_name} not to return anything"
|
||||
end
|
||||
|
||||
def selector_name
|
||||
name = "#{normalized[:selector].name} #{normalized[:locator].inspect}"
|
||||
name << " with text #{normalized[:options][:text].inspect}" if normalized[:options][:text]
|
||||
name
|
||||
end
|
||||
|
||||
def wrap(actual)
|
||||
if actual.respond_to?("has_selector?")
|
||||
actual
|
||||
else
|
||||
Capybara.string(actual.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def normalized
|
||||
@normalized ||= Capybara::Selector.normalize(*@args)
|
||||
end
|
||||
end
|
||||
|
||||
def have_selector(*args)
|
||||
HaveSelector.new(*args)
|
||||
end
|
||||
|
||||
def have_xpath(*args)
|
||||
HaveSelector.new(:xpath, *args)
|
||||
end
|
||||
|
||||
def have_css(*args)
|
||||
HaveSelector.new(:css, *args)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,19 +15,26 @@ module Capybara
|
|||
all.delete(name.to_sym)
|
||||
end
|
||||
|
||||
def normalize(name_or_locator, locator=nil)
|
||||
xpath = if locator
|
||||
all[name_or_locator.to_sym].call(locator)
|
||||
def normalize(*args)
|
||||
result = {}
|
||||
result[:options] = if args.last.is_a?(Hash) then args.pop else {} end
|
||||
|
||||
if args[1]
|
||||
result[:selector] = all[args[0]]
|
||||
result[:locator] = args[1]
|
||||
else
|
||||
selector = all.values.find { |s| s.match?(name_or_locator) }
|
||||
selector ||= all[Capybara.default_selector]
|
||||
selector.call(name_or_locator)
|
||||
result[:selector] = all.values.find { |s| s.match?(args[0]) }
|
||||
result[:locator] = args[0]
|
||||
end
|
||||
result[:selector] ||= all[Capybara.default_selector]
|
||||
|
||||
xpath = result[:selector].call(result[:locator])
|
||||
if xpath.respond_to?(:to_xpaths)
|
||||
xpath.to_xpaths
|
||||
result[:xpaths] = xpath.to_xpaths
|
||||
else
|
||||
[xpath.to_s].flatten
|
||||
result[:xpaths] = [xpath.to_s].flatten
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -46,6 +53,11 @@ module Capybara
|
|||
@match
|
||||
end
|
||||
|
||||
def failure_message(&block)
|
||||
@failure_message = block if block
|
||||
@failure_message
|
||||
end
|
||||
|
||||
def call(locator)
|
||||
@xpath.call(locator)
|
||||
end
|
||||
|
|
|
@ -26,6 +26,20 @@ shared_examples_for 'driver' do
|
|||
@driver.visit('/with_simple_html')
|
||||
@driver.body.should include('Bar')
|
||||
end
|
||||
|
||||
if "".respond_to?(:encoding)
|
||||
context "encoding of response between ascii and utf8" do
|
||||
it "should be valid with html entities" do
|
||||
@driver.visit('/with_html_entities')
|
||||
lambda { @driver.body.encode!("UTF-8") }.should_not raise_error
|
||||
end
|
||||
|
||||
it "should be valid without html entities" do
|
||||
@driver.visit('/with_html')
|
||||
lambda { @driver.body.encode!("UTF-8") }.should_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find' do
|
||||
|
|
|
@ -75,18 +75,25 @@ shared_examples_for "session" do
|
|||
@session.visit('/form')
|
||||
@session.fill_in('address1_city', :with =>'Paris')
|
||||
@session.fill_in('address1_street', :with =>'CDG')
|
||||
@session.fill_in('address1_street', :with =>'CDG')
|
||||
@session.select("France", :from => 'address1_country')
|
||||
|
||||
@session.fill_in('address2_city', :with => 'Mikolaiv')
|
||||
@session.fill_in('address2_street', :with => 'PGS')
|
||||
@session.select("Ukraine", :from => 'address2_country')
|
||||
|
||||
@session.click_button "awesome"
|
||||
|
||||
addresses=extract_results(@session)["addresses"]
|
||||
addresses.should have(2).addresses
|
||||
|
||||
addresses[0]["street"].should == 'CDG'
|
||||
addresses[0]["city"].should == 'Paris'
|
||||
addresses[0]["street"].should == 'CDG'
|
||||
addresses[0]["city"].should == 'Paris'
|
||||
addresses[0]["country"].should == 'France'
|
||||
|
||||
addresses[1]["street"].should == 'PGS'
|
||||
addresses[1]["city"].should == 'Mikolaiv'
|
||||
addresses[1]["street"].should == 'PGS'
|
||||
addresses[1]["city"].should == 'Mikolaiv'
|
||||
addresses[1]["country"].should == 'Ukraine'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -79,6 +79,18 @@ shared_examples_for "find" do
|
|||
end
|
||||
end
|
||||
|
||||
context "with custom selector with failure_message option", :focus => true do
|
||||
it "it should raise an error with the failure message if the element is not found" do
|
||||
Capybara.add_selector(:monkey) do
|
||||
xpath { |num| ".//*[contains(@id, 'monkey')][#{num}]" }
|
||||
failure_message { |node| node.all(".//*[contains(@id, 'monkey')]").map { |node| node.text }.sort.join(', ') }
|
||||
end
|
||||
running do
|
||||
@session.find(:monkey, '14').text.should == 'Monkey Paul'
|
||||
end.should raise_error(Capybara::ElementNotFound, "Monkey John, Monkey Paul")
|
||||
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
|
||||
|
@ -97,7 +109,7 @@ shared_examples_for "find" do
|
|||
it "should raise ElementNotFound with a useful default message if nothing was found" do
|
||||
running do
|
||||
@session.find(:xpath, '//div[@id="nosuchthing"]').should be_nil
|
||||
end.should raise_error(Capybara::ElementNotFound, "Unable to find '//div[@id=\"nosuchthing\"]'")
|
||||
end.should raise_error(Capybara::ElementNotFound, "Unable to find xpath \"//div[@id=\\\"nosuchthing\\\"]\"")
|
||||
end
|
||||
|
||||
it "should accept an XPath instance and respect the order of paths" do
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
shared_examples_for "session with headers support" do
|
||||
describe '#response_headers' do
|
||||
it "should return response headers" do
|
||||
@session.visit('/with_simple_html')
|
||||
@session.response_headers['Content-Type'].should == 'text/html'
|
||||
@session.visit('/with_simple_html')
|
||||
@session.response_headers['Content-Type'].should =~ %r(text/html)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,6 +69,10 @@ class TestApp < Sinatra::Base
|
|||
request.cookies['capybara']
|
||||
end
|
||||
|
||||
get '/get_header' do
|
||||
env['HTTP_FOO']
|
||||
end
|
||||
|
||||
get '/:view' do |view|
|
||||
erb view.to_sym
|
||||
end
|
||||
|
|
|
@ -163,18 +163,30 @@
|
|||
<span>First address<span>
|
||||
<label for='address1_street'>Street</label>
|
||||
<input type="text" name="form[addresses][][street]" value="" id="address1_street">
|
||||
|
||||
<label for='address1_city'>City</label>
|
||||
<input type="text" name="form[addresses][][city]" value="" id="address1_city">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for='address1_city'>City</label>
|
||||
<input type="text" name="form[addresses][][city]" value="" id="address1_city">
|
||||
|
||||
<label for='address1_country'>Country</label>
|
||||
<select name="form[addresses][][country]" id="address1_country">
|
||||
<option>France</option>
|
||||
<option>Ukraine</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span>Second address<span>
|
||||
<label for='address2_street'>Street</label>
|
||||
<input type="text" name="form[addresses][][street]" value="" id="address2_street">
|
||||
|
||||
|
||||
<label for='address2_city'>City</label>
|
||||
<input type="text" name="form[addresses][][city]" value="" id="address2_city">
|
||||
|
||||
<label for='address2_country'>Country</label>
|
||||
<select name="form[addresses][][country]" id="address2_country">
|
||||
<option>France</option>
|
||||
<option>Ukraine</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<div style="display:none;">
|
||||
|
@ -207,7 +219,7 @@
|
|||
|
||||
<p>
|
||||
<label for="form_disabled_radio">
|
||||
Disabled Checkbox
|
||||
Disabled Radio
|
||||
<input type="radio" name="form[disabled_radio]" value="Should not see me" id="form_disabled_radio" checked="checked" disabled="disabled" />
|
||||
</label>
|
||||
</p>
|
||||
|
|
1
lib/capybara/spec/views/with_html_entities.erb
Normal file
1
lib/capybara/spec/views/with_html_entities.erb
Normal file
|
@ -0,0 +1 @@
|
|||
Encoding with — html entities »
|
|
@ -21,7 +21,8 @@ module Capybara
|
|||
require "launchy"
|
||||
Launchy::Browser.run(path)
|
||||
rescue LoadError
|
||||
warn "Sorry, you need to install launchy to open pages: `gem install launchy`"
|
||||
warn "Sorry, you need to install launchy (`gem install launchy`) and " <<
|
||||
"make sure it's available to open pages with `save_and_open_page`."
|
||||
end
|
||||
|
||||
def rewrite_css_and_image_references(response_html) # :nodoc:
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module Capybara
|
||||
VERSION = '0.4.1.rc'
|
||||
VERSION = '0.4.1.1'
|
||||
end
|
||||
|
|
|
@ -53,4 +53,32 @@ describe Capybara::Driver::RackTest do
|
|||
it_should_behave_like "driver with status code support"
|
||||
it_should_behave_like "driver with cookies support"
|
||||
it_should_behave_like "driver with infinite redirect detection"
|
||||
|
||||
describe '#reset!' do
|
||||
it { @driver.visit('/foo'); lambda { @driver.reset! }.should change(@driver, :current_url).to('') }
|
||||
|
||||
it 'should reset headers' do
|
||||
@driver.header('FOO', 'BAR')
|
||||
@driver.visit('/get_header')
|
||||
@driver.body.should include('BAR')
|
||||
|
||||
@driver.reset!
|
||||
@driver.visit('/get_header')
|
||||
@driver.body.should_not include('BAR')
|
||||
end
|
||||
|
||||
it 'should reset response' do
|
||||
@driver.visit('/foo')
|
||||
lambda { @driver.response }.should_not raise_error
|
||||
@driver.reset!
|
||||
lambda { @driver.response }.should raise_error
|
||||
end
|
||||
|
||||
it 'should request response' do
|
||||
@driver.visit('/foo')
|
||||
lambda { @driver.request }.should_not raise_error
|
||||
@driver.reset!
|
||||
lambda { @driver.request }.should raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ describe Capybara do
|
|||
end
|
||||
|
||||
after do
|
||||
Capybara.session_name = nil
|
||||
Capybara.default_driver = nil
|
||||
Capybara.use_default_driver
|
||||
end
|
||||
|
@ -123,6 +124,48 @@ describe Capybara do
|
|||
Capybara.current_session.object_id.should_not == object_id
|
||||
Capybara.current_session.app.should == Capybara.app
|
||||
end
|
||||
|
||||
it "should change when the session name changes" do
|
||||
object_id = Capybara.current_session.object_id
|
||||
Capybara.session_name = :administrator
|
||||
Capybara.session_name.should == :administrator
|
||||
Capybara.current_session.object_id.should_not == object_id
|
||||
Capybara.session_name = :default
|
||||
Capybara.session_name.should == :default
|
||||
Capybara.current_session.object_id.should == object_id
|
||||
end
|
||||
end
|
||||
|
||||
describe "#using_session" do
|
||||
it "should change the session name for the duration of the block" do
|
||||
Capybara.session_name.should == :default
|
||||
Capybara.using_session(:administrator) do
|
||||
Capybara.session_name.should == :administrator
|
||||
end
|
||||
Capybara.session_name.should == :default
|
||||
end
|
||||
|
||||
it "should reset the session to the default, even if an exception occurs" do
|
||||
begin
|
||||
Capybara.using_session(:raise) do
|
||||
raise
|
||||
end
|
||||
rescue Exception
|
||||
end
|
||||
Capybara.session_name.should == :default
|
||||
end
|
||||
|
||||
it "should yield the passed block" do
|
||||
called = false
|
||||
Capybara.using_session(:administrator) { called = true }
|
||||
called.should == true
|
||||
end
|
||||
end
|
||||
|
||||
describe "#session_name" do
|
||||
it "should default to :default" do
|
||||
Capybara.session_name.should == :default
|
||||
end
|
||||
end
|
||||
|
||||
describe 'the DSL' do
|
||||
|
@ -152,6 +195,15 @@ describe Capybara do
|
|||
foo.page.click_link('ullamco')
|
||||
foo.page.body.should include('Another World')
|
||||
end
|
||||
|
||||
it "should provide an 'using_session' shortcut" do
|
||||
klass = Class.new do
|
||||
include Capybara
|
||||
end
|
||||
Capybara.should_receive(:using_session).with(:name)
|
||||
foo = klass.new
|
||||
foo.using_session(:name)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
211
spec/rspec_matchers_spec.rb
Normal file
211
spec/rspec_matchers_spec.rb
Normal file
|
@ -0,0 +1,211 @@
|
|||
require 'spec_helper'
|
||||
require 'capybara/dsl'
|
||||
require 'capybara/rspec_matchers'
|
||||
|
||||
Capybara.app = TestApp
|
||||
|
||||
describe Capybara::RSpecMatchers do
|
||||
include Capybara
|
||||
include Capybara::RSpecMatchers
|
||||
|
||||
describe "have_css matcher" do
|
||||
context "on a string" do
|
||||
context "with should" do
|
||||
it "passes if has_css? returns true" do
|
||||
"<h1>Text</h1>".should have_css('h1')
|
||||
end
|
||||
|
||||
it "fails if has_css? returns false" do
|
||||
expect do
|
||||
"<h1>Text</h1>".should have_css('h2')
|
||||
end.to raise_error(/expected css "h2" to return something/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with should_not" do
|
||||
it "passes if has_no_css? returns true" do
|
||||
"<h1>Text</h1>".should_not have_css('h2')
|
||||
end
|
||||
|
||||
it "fails if has_no_css? returns false" do
|
||||
expect do
|
||||
"<h1>Text</h1>".should_not have_css('h1')
|
||||
end.to raise_error(/expected css "h1" not to return anything/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "on a page or node" do
|
||||
before do
|
||||
visit('/with_html')
|
||||
end
|
||||
|
||||
context "with should" do
|
||||
it "passes if has_css? returns true" do
|
||||
page.should have_css('h1')
|
||||
end
|
||||
|
||||
it "fails if has_css? returns false" do
|
||||
expect do
|
||||
page.should have_css('h1#doesnotexist')
|
||||
end.to raise_error(/expected css "h1#doesnotexist" to return something/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with should_not" do
|
||||
it "passes if has_no_css? returns true" do
|
||||
page.should_not have_css('h1#doesnotexist')
|
||||
end
|
||||
|
||||
it "fails if has_no_css? returns false" do
|
||||
expect do
|
||||
page.should_not have_css('h1')
|
||||
end.to raise_error(/expected css "h1" not to return anything/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "have_xpath matcher" do
|
||||
context "on a string" do
|
||||
context "with should" do
|
||||
it "passes if has_css? returns true" do
|
||||
"<h1>Text</h1>".should have_xpath('//h1')
|
||||
end
|
||||
|
||||
it "fails if has_css? returns false" do
|
||||
expect do
|
||||
"<h1>Text</h1>".should have_xpath('//h2')
|
||||
end.to raise_error(%r(expected xpath "//h2" to return something))
|
||||
end
|
||||
end
|
||||
|
||||
context "with should_not" do
|
||||
it "passes if has_no_css? returns true" do
|
||||
"<h1>Text</h1>".should_not have_xpath('//h2')
|
||||
end
|
||||
|
||||
it "fails if has_no_css? returns false" do
|
||||
expect do
|
||||
"<h1>Text</h1>".should_not have_xpath('//h1')
|
||||
end.to raise_error(%r(expected xpath "//h1" not to return anything))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "on a page or node" do
|
||||
before do
|
||||
visit('/with_html')
|
||||
end
|
||||
|
||||
context "with should" do
|
||||
it "passes if has_css? returns true" do
|
||||
page.should have_xpath('//h1')
|
||||
end
|
||||
|
||||
it "fails if has_css? returns false" do
|
||||
expect do
|
||||
page.should have_xpath("//h1[@id='doesnotexist']")
|
||||
end.to raise_error(%r(expected xpath "//h1\[@id='doesnotexist'\]" to return something))
|
||||
end
|
||||
end
|
||||
|
||||
context "with should_not" do
|
||||
it "passes if has_no_css? returns true" do
|
||||
page.should_not have_xpath('//h1[@id="doesnotexist"]')
|
||||
end
|
||||
|
||||
it "fails if has_no_css? returns false" do
|
||||
expect do
|
||||
page.should_not have_xpath('//h1')
|
||||
end.to raise_error(%r(expected xpath "//h1" not to return anything))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "have_selector matcher" do
|
||||
context "on a string" do
|
||||
context "with should" do
|
||||
it "passes if has_css? returns true" do
|
||||
"<h1>Text</h1>".should have_selector('//h1')
|
||||
end
|
||||
|
||||
it "fails if has_css? returns false" do
|
||||
expect do
|
||||
"<h1>Text</h1>".should have_selector('//h2')
|
||||
end.to raise_error(%r(expected xpath "//h2" to return something))
|
||||
end
|
||||
|
||||
it "fails with the selector's failure_message if set" do
|
||||
Capybara.add_selector(:monkey) do
|
||||
xpath { |num| ".//*[contains(@id, 'monkey')][#{num}]" }
|
||||
failure_message { |node| node.all(".//*[contains(@id, 'monkey')]").map { |node| node.text }.sort.join(', ') }
|
||||
end
|
||||
expect do
|
||||
'<h1 id="monkey_paul">Monkey John</h1>'.should have_selector(:monkey, 14)
|
||||
end.to raise_error("Monkey John")
|
||||
end
|
||||
end
|
||||
|
||||
context "with should_not" do
|
||||
it "passes if has_no_css? returns true" do
|
||||
"<h1>Text</h1>".should_not have_selector(:css, 'h2')
|
||||
end
|
||||
|
||||
it "fails if has_no_css? returns false" do
|
||||
expect do
|
||||
"<h1>Text</h1>".should_not have_selector(:css, 'h1')
|
||||
end.to raise_error(%r(expected css "h1" not to return anything))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "on a page or node" do
|
||||
before do
|
||||
visit('/with_html')
|
||||
end
|
||||
|
||||
context "with should" do
|
||||
it "passes if has_css? returns true" do
|
||||
page.should have_selector('//h1', :text => 'test')
|
||||
end
|
||||
|
||||
it "fails if has_css? returns false" do
|
||||
expect do
|
||||
page.should have_selector("//h1[@id='doesnotexist']")
|
||||
end.to raise_error(%r(expected xpath "//h1\[@id='doesnotexist'\]" to return something))
|
||||
end
|
||||
|
||||
it "includes text in error message" do
|
||||
expect do
|
||||
page.should have_selector("//h1", :text => 'wrong text')
|
||||
end.to raise_error(%r(expected xpath "//h1" with text "wrong text" to return something))
|
||||
end
|
||||
|
||||
it "fails with the selector's failure_message if set" do
|
||||
Capybara.add_selector(:monkey) do
|
||||
xpath { |num| ".//*[contains(@id, 'monkey')][#{num}]" }
|
||||
failure_message { |node| node.all(".//*[contains(@id, 'monkey')]").map { |node| node.text }.sort.join(', ') }
|
||||
end
|
||||
expect do
|
||||
page.should have_selector(:monkey, 14)
|
||||
end.to raise_error("Monkey John, Monkey Paul")
|
||||
end
|
||||
end
|
||||
|
||||
context "with should_not" do
|
||||
it "passes if has_no_css? returns true" do
|
||||
page.should_not have_selector(:css, 'h1#doesnotexist')
|
||||
end
|
||||
|
||||
it "fails if has_no_css? returns false" do
|
||||
expect do
|
||||
page.should_not have_selector(:css, 'h1', :text => 'test')
|
||||
end.to raise_error(%r(expected css "h1" with text "test" not to return anything))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,8 +3,8 @@ require 'capybara/rspec'
|
|||
|
||||
Capybara.app = TestApp
|
||||
|
||||
describe 'capybara/rspec', :type => :acceptance do
|
||||
it "should include Capybara in rpsec" do
|
||||
describe 'capybara/rspec', :type => :request do
|
||||
it "should include Capybara in rspec" do
|
||||
visit('/foo')
|
||||
page.body.should include('Another World')
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue