Branch merge

This commit is contained in:
Andrea Fazzi 2009-11-27 22:11:17 +01:00
commit ea6857b52b
8 changed files with 283 additions and 183 deletions

View File

@ -153,6 +153,20 @@ For ultimate control, you can instantiate and use a session manually.
end
session.click_link 'Sign in'
== XPath an CSS
Capybara does not try to guess what kind of selector you are going to give it,
if you want to use CSS with your 'within' declarations for example, you'll need
to do:
within(:css, 'ul li') { ... }
Alternatively you can set the default selector to CSS, which may help if you are
moving from Webrat and used CSS a lot, or simply generally prefer CSS:
Capybara.default_selector = :css
within('ul li') { ... }
== Gotchas:
* Install JRuby and the 'celerity' gem, version 0.7.4 (0.7.5 has a bug with
@ -186,6 +200,14 @@ For ultimate control, you can instantiate and use a session manually.
<tt><a href="/same/url#"></tt> instead. You can achieve this in Rails with
<tt>link_to('foo', :anchor => '')</tt>
== Contributors:
The following people have dedicated their time and effort to Capybara:
* Jonas Nicklas
* Dennis Rogenius
* Rob Holland
== License:
(The MIT License)

View File

@ -22,9 +22,8 @@ $hoe = Hoe.spec 'capybara' do
['selenium-webdriver', '>= 0.0.3'],
['rack', '>= 1.0.0'],
['rack-test', '>= 0.5.2'],
['database_cleaner', '>= 0.2.3']
]
self.extra_dev_deps = [
['sinatra', '>= 0.9.4'],
['rspec', '>= 1.2.9']

View File

@ -7,10 +7,6 @@ After do
Capybara.reset_sessions!
end
require 'database_cleaner'
require 'database_cleaner/cucumber'
DatabaseCleaner.strategy = :truncation
Before('@javascript') do
Capybara.current_driver = Capybara.javascript_driver
end
@ -29,4 +25,4 @@ end
After do
Capybara.use_default_driver
end
end

View File

@ -9,7 +9,7 @@ module Capybara
end
def current_driver
@current_driver || default_driver
@current_driver || default_driver
end
alias_method :mode, :current_driver
@ -18,11 +18,17 @@ module Capybara
end
def use_default_driver
@current_driver = nil
@current_driver = nil
end
def current_session
session_pool["#{current_driver}#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
session_pool["#{current_driver}#{app.object_id}"] ||= begin
Capybara::Session.new(current_driver, app)
end
end
def current_session?
session_pool.has_key?("#{current_driver}#{app.object_id}")
end
def reset_sessions!

View File

@ -3,10 +3,14 @@ module Capybara
extend(self)
def save_and_open_page(html)
require 'tempfile'
tempfile = Tempfile.new("capybara#{rand(1000000)}")
name="capybara-#{Time.new.strftime("%Y%m%d%H%M%S")}.html"
FileUtils.touch(name) unless File.exist?(name)
tempfile = File.new(name,'w')
tempfile.write(rewrite_css_and_image_references(html))
tempfile.close
open_in_browser(tempfile.path)
end

View File

@ -1,190 +1,200 @@
class Capybara::Session
FIELDS_PATHS = {
:text_field => proc { |id| "//input[@type='text'][@id='#{id}']" },
:text_area => proc { |id| "//textarea[@id='#{id}']" },
:password_field => proc { |id| "//input[@type='password'][@id='#{id}']" },
:radio => proc { |id| "//input[@type='radio'][@id='#{id}']" },
:hidden_field => proc { |id| "//input[@type='hidden'][@id='#{id}']" },
:checkbox => proc { |id| "//input[@type='checkbox'][@id='#{id}']" },
:select => proc { |id| "//select[@id='#{id}']" },
:file_field => proc { |id| "//input[@type='file'][@id='#{id}']" }
}
attr_reader :mode, :app
module Capybara
def initialize(mode, app)
@mode = mode
@app = app
end
class << self
attr_writer :default_selector
def driver
@driver ||= case mode
when :rack_test
Capybara::Driver::RackTest.new(app)
when :culerity
Capybara::Driver::Culerity.new(app)
when :selenium
Capybara::Driver::Selenium.new(app)
else
raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
def default_selector
@default_selector ||= :xpath
end
end
def visit(path)
driver.visit(path)
end
def click_link(locator)
find_link(locator).click
end
class Session
def click_button(locator)
find_button(locator).click
end
FIELDS_PATHS = {
:text_field => proc { |id| "//input[@type='text'][@id='#{id}']" },
:text_area => proc { |id| "//textarea[@id='#{id}']" },
:password_field => proc { |id| "//input[@type='password'][@id='#{id}']" },
:radio => proc { |id| "//input[@type='radio'][@id='#{id}']" },
:hidden_field => proc { |id| "//input[@type='hidden'][@id='#{id}']" },
:checkbox => proc { |id| "//input[@type='checkbox'][@id='#{id}']" },
:select => proc { |id| "//select[@id='#{id}']" },
:file_field => proc { |id| "//input[@type='file'][@id='#{id}']" }
}
def fill_in(locator, options={})
find_field(locator, :text_field, :text_area, :password_field).set(options[:with])
end
def choose(locator)
find_field(locator, :radio).set(true)
end
def check(locator)
find_field(locator, :checkbox).set(true)
end
def uncheck(locator)
find_field(locator, :checkbox).set(false)
end
def select(value, options={})
find_field(options[:from], :select).select(value)
end
def attach_file(locator, path)
find_field(locator, :file_field).set(path)
end
attr_reader :mode, :app
def body
driver.body
end
def has_content?(content)
has_xpath?("//*[contains(.,'#{content}')]")
end
def has_xpath?(path, options={})
results = find(path)
if options[:text]
results = filter_by_text(results, options[:text])
def initialize(mode, app)
@mode = mode
@app = app
end
if options[:count]
results.size == options[:count]
else
results.size > 0
end
end
def has_css?(path, options={})
has_xpath?(css_to_xpath(path), options)
end
def within(kind, scope=nil)
kind, scope = :xpath, kind unless scope
scope = css_to_xpath(scope) if kind == :css
raise Capybara::ElementNotFound, "scope '#{scope}' not found on page" if find(scope).empty?
scopes.push(scope)
yield
scopes.pop
end
def within_fieldset(locator)
within "//fieldset[@id='#{locator}' or contains(legend,'#{locator}')]" do
yield
end
end
def within_table(locator)
within "//table[@id='#{locator}' or contains(caption,'#{locator}')]" do
yield
end
end
def save_and_open_page
require 'capybara/save_and_open_page'
Capybara::SaveAndOpenPage.save_and_open_page(body)
end
def find_field(locator, *kinds)
kinds = FIELDS_PATHS.keys if kinds.empty?
field = find_field_by_id(locator, *kinds) || find_field_by_label(locator, *kinds)
raise Capybara::ElementNotFound, "no field of kind #{kinds.inspect} with id or label '#{locator}' found" unless field
field
end
alias_method :field_labeled, :find_field
def find_link(locator)
link = find("//a[@id='#{locator}' or contains(.,'#{locator}') or @title='#{locator}']").first
raise Capybara::ElementNotFound, "no link with title, id or text '#{locator}' found" unless link
link
end
def find_button(locator)
button = find("//input[@type='submit' or @type='image'][@id='#{locator}' or @value='#{locator}']").first || find("//button[@id='#{locator}' or @value='#{locator}' or contains(.,'#{locator}')]").first
raise Capybara::ElementNotFound, "no button with value or id '#{locator}' found" unless button
button
end
private
def css_to_xpath(css)
Nokogiri::CSS.xpath_for(css).first
end
def filter_by_text(nodes, text)
nodes.select do |node|
case text
when String
node.text.include?(text)
when Regexp
node.text =~ text
def driver
@driver ||= case mode
when :rack_test
Capybara::Driver::RackTest.new(app)
when :culerity
Capybara::Driver::Culerity.new(app)
when :selenium
Capybara::Driver::Selenium.new(app)
else
raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
end
end
end
def current_scope
scopes.join('')
end
def scopes
@scopes ||= []
end
def find_field_by_id(locator, *kinds)
kinds.each do |kind|
path = FIELDS_PATHS[kind]
element = find(path.call(locator)).first
return element if element
def visit(path)
driver.visit(path)
end
return nil
end
def find_field_by_label(locator, *kinds)
kinds.each do |kind|
label = find("//label[contains(.,'#{locator}')]").first
if label
element = find_field_by_id(label[:for], kind)
def click_link(locator)
find_link(locator).click
end
def click_button(locator)
find_button(locator).click
end
def fill_in(locator, options={})
find_field(locator, :text_field, :text_area, :password_field).set(options[:with])
end
def choose(locator)
find_field(locator, :radio).set(true)
end
def check(locator)
find_field(locator, :checkbox).set(true)
end
def uncheck(locator)
find_field(locator, :checkbox).set(false)
end
def select(value, options={})
find_field(options[:from], :select).select(value)
end
def attach_file(locator, path)
find_field(locator, :file_field).set(path)
end
def body
driver.body
end
def has_content?(content)
has_xpath?("//*[contains(.,'#{content}')]")
end
def has_xpath?(path, options={})
results = find(path)
if options[:text]
results = filter_by_text(results, options[:text])
end
if options[:count]
results.size == options[:count]
else
results.size > 0
end
end
def has_css?(path, options={})
has_xpath?(css_to_xpath(path), options)
end
def within(kind, scope=nil)
kind, scope = Capybara.default_selector, kind unless scope
scope = css_to_xpath(scope) if kind == :css
raise Capybara::ElementNotFound, "scope '#{scope}' not found on page" if find(scope).empty?
scopes.push(scope)
yield
scopes.pop
end
def within_fieldset(locator)
within "//fieldset[@id='#{locator}' or contains(legend,'#{locator}')]" do
yield
end
end
def within_table(locator)
within "//table[@id='#{locator}' or contains(caption,'#{locator}')]" do
yield
end
end
def save_and_open_page
require 'capybara/save_and_open_page'
Capybara::SaveAndOpenPage.save_and_open_page(body)
end
def find_field(locator, *kinds)
kinds = FIELDS_PATHS.keys if kinds.empty?
field = find_field_by_id(locator, *kinds) || find_field_by_label(locator, *kinds)
raise Capybara::ElementNotFound, "no field of kind #{kinds.inspect} with id or label '#{locator}' found" unless field
field
end
alias_method :field_labeled, :find_field
def find_link(locator)
link = find("//a[@id='#{locator}' or contains(.,'#{locator}') or @title='#{locator}']").first
raise Capybara::ElementNotFound, "no link with title, id or text '#{locator}' found" unless link
link
end
def find_button(locator)
button = find("//input[@type='submit' or @type='image'][@id='#{locator}' or @value='#{locator}']").first || find("//button[@id='#{locator}' or @value='#{locator}' or contains(.,'#{locator}')]").first
raise Capybara::ElementNotFound, "no button with value or id '#{locator}' found" unless button
button
end
private
def css_to_xpath(css)
Nokogiri::CSS.xpath_for(css).first
end
def filter_by_text(nodes, text)
nodes.select do |node|
case text
when String
node.text.include?(text)
when Regexp
node.text =~ text
end
end
end
def current_scope
scopes.join('')
end
def scopes
@scopes ||= []
end
def find_field_by_id(locator, *kinds)
kinds.each do |kind|
path = FIELDS_PATHS[kind]
element = find(path.call(locator)).first
return element if element
end
return nil
end
def find_field_by_label(locator, *kinds)
kinds.each do |kind|
label = find("//label[contains(.,'#{locator}')]").first
if label
element = find_field_by_id(label[:for], kind)
return element if element
end
end
return nil
end
def find(locator)
locator = current_scope.to_s + locator
driver.find(locator)
end
return nil
end
def find(locator)
locator = current_scope.to_s + locator
driver.find(locator)
end
end

View File

@ -0,0 +1,43 @@
require File.expand_path('spec_helper', File.dirname(__FILE__))
require 'capybara/save_and_open_page'
require 'launchy'
describe Capybara::SaveAndOpenPage do
describe "#save_save_and_open_page" do
before do
@time = Time.new.strftime("%Y%m%d%H%M%S")
@name = "capybara-#{@time}.html"
@temp_file = mock("FILE")
@temp_file.stub!(:write)
@temp_file.stub!(:close)
@temp_file.stub!(:path).and_return(@name)
File.should_receive(:exist?).and_return true
File.should_receive(:new).and_return @temp_file
@html = <<-HTML
<html>
<head>
</head>
<body>
<h1>test</h1>
</body>
<html>
HTML
Launchy::Browser.stub(:run)
end
it "should create a new temporary file" do
@temp_file.should_receive(:write).with @html
Capybara::SaveAndOpenPage.save_and_open_page @html
end
it "should open the file in the browser" do
Capybara::SaveAndOpenPage.should_receive(:open_in_browser).with(@name)
Capybara::SaveAndOpenPage.save_and_open_page @html
end
end
end

View File

@ -542,7 +542,27 @@ shared_examples_for "session" do
@session.body.should include('<h1>Bar</h1>')
end
end
context "with the default selector" do
it "should use XPath" do
@session.within("//li[contains(., 'With Simple HTML')]") do
@session.click_link('Go')
end
@session.body.should include('<h1>Bar</h1>')
end
end
context "with the default selector set to CSS" do
it "should use CSS" do
Capybara.default_selector = :css
@session.within("ul li[contains('With Simple HTML')]") do
@session.click_link('Go')
end
@session.body.should include('<h1>Bar</h1>')
Capybara.default_selector = :xpath
end
end
context "with click_link" do
it "should click links in the given scope" do
@session.within("//li[contains(.,'With Simple HTML')]") do