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
fe911868fc
17 changed files with 243 additions and 57 deletions
|
@ -51,6 +51,7 @@ spec/dsl/select_spec.rb
|
|||
spec/dsl/uncheck_spec.rb
|
||||
spec/dsl/within_spec.rb
|
||||
spec/dsl_spec.rb
|
||||
spec/fixtures/capybara.jpg
|
||||
spec/fixtures/test_file.txt
|
||||
spec/public/jquery-ui.js
|
||||
spec/public/jquery.js
|
||||
|
@ -72,6 +73,7 @@ spec/test_app.rb
|
|||
spec/views/buttons.erb
|
||||
spec/views/fieldsets.erb
|
||||
spec/views/form.erb
|
||||
spec/views/postback.erb
|
||||
spec/views/tables.erb
|
||||
spec/views/with_html.erb
|
||||
spec/views/with_js.erb
|
||||
|
|
|
@ -337,6 +337,7 @@ The following people have dedicated their time and effort to Capybara:
|
|||
* Aaron Patterson
|
||||
* Dan Dofter
|
||||
* Thorbjørn Hermansen
|
||||
* Louis T.
|
||||
|
||||
== License:
|
||||
|
||||
|
|
|
@ -2,13 +2,14 @@ require 'timeout'
|
|||
require 'nokogiri'
|
||||
|
||||
module Capybara
|
||||
VERSION = '0.2.0'
|
||||
VERSION = '0.3.0'
|
||||
|
||||
class CapybaraError < StandardError; end
|
||||
class DriverNotFoundError < CapybaraError; end
|
||||
class ElementNotFound < CapybaraError; end
|
||||
class NotSupportedByDriverError < CapybaraError; end
|
||||
class TimeoutError < CapybaraError; end
|
||||
class InfiniteRedirectError < TimeoutError; end
|
||||
|
||||
class << self
|
||||
attr_accessor :debug, :asset_root, :app_host
|
||||
|
|
|
@ -38,7 +38,7 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
|
||||
def click
|
||||
if tag_name == 'a'
|
||||
driver.visit(self[:href])
|
||||
driver.visit(self[:href].to_s)
|
||||
elsif (tag_name == 'input' or tag_name == 'button') and %w(submit image).include?(type)
|
||||
Form.new(driver, form).submit(self)
|
||||
end
|
||||
|
@ -100,23 +100,19 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
end
|
||||
|
||||
def submit(button)
|
||||
if post?
|
||||
driver.submit(node['action'].to_s, params(button))
|
||||
else
|
||||
driver.visit(node['action'].to_s, params(button))
|
||||
end
|
||||
driver.submit(method, node['action'].to_s, params(button))
|
||||
end
|
||||
|
||||
def multipart?
|
||||
self[:enctype] == "multipart/form-data"
|
||||
end
|
||||
|
||||
def post?
|
||||
self[:method] =~ /post/i
|
||||
private
|
||||
|
||||
def method
|
||||
self[:method] =~ /post/i ? :post : :get
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_param!(params, key, value)
|
||||
collection = key.sub!(/\[\]$/, '')
|
||||
if collection
|
||||
|
@ -143,22 +139,24 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
end
|
||||
|
||||
def visit(path, attributes = {})
|
||||
return if path.gsub(/^#{current_path}/, '') =~ /^#/
|
||||
get(path, attributes)
|
||||
follow_redirect! while response.redirect?
|
||||
follow_redirects!
|
||||
cache_body
|
||||
end
|
||||
|
||||
def current_url
|
||||
request.url
|
||||
request.url rescue ""
|
||||
end
|
||||
|
||||
def response_headers
|
||||
response.headers
|
||||
end
|
||||
|
||||
def submit(path, attributes)
|
||||
post(path, attributes)
|
||||
follow_redirect! while response.redirect?
|
||||
def submit(method, path, attributes)
|
||||
path = current_path if not path or path.empty?
|
||||
send(method, path, attributes)
|
||||
follow_redirects!
|
||||
cache_body
|
||||
end
|
||||
|
||||
|
@ -166,7 +164,21 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
|
|||
html.xpath(selector).map { |node| Node.new(self, node) }
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def current_path
|
||||
request.path rescue ""
|
||||
end
|
||||
|
||||
def follow_redirects!
|
||||
Capybara::WaitUntil.timeout(4) do
|
||||
redirect = response.redirect?
|
||||
follow_redirect! if redirect
|
||||
not redirect
|
||||
end
|
||||
rescue Capybara::TimeoutError
|
||||
raise Capybara::InfiniteRedirectError, "infinite redirect detected!"
|
||||
end
|
||||
|
||||
def cache_body
|
||||
@body = response.body
|
||||
|
|
|
@ -48,16 +48,7 @@ module Capybara
|
|||
Capybara.current_session
|
||||
end
|
||||
|
||||
SESSION_METHODS = [
|
||||
:visit, :current_url, :body, :click_link, :click_button, :drag, :fill_in,
|
||||
:choose, :has_xpath?, :has_no_xpath?, :has_css?, :has_no_css?,
|
||||
:check, :uncheck, :attach_file, :select, :source,
|
||||
:has_content?, :has_no_content?, :within, :within_fieldset, :within_table,
|
||||
:save_and_open_page, :find, :find_field, :find_link, :find_button,
|
||||
:field_labeled, :all, :locate, :evaluate_script,
|
||||
:click, :wait_until
|
||||
]
|
||||
SESSION_METHODS.each do |method|
|
||||
Session::DSL_METHODS.each do |method|
|
||||
class_eval <<-RUBY, __FILE__, __LINE__+1
|
||||
def #{method}(*args, &block)
|
||||
page.#{method}(*args, &block)
|
||||
|
|
|
@ -18,7 +18,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def find_by_id(id)
|
||||
find(Xpath.for_css("##{id}"))
|
||||
find(XPath.for_css("##{id}"))
|
||||
end
|
||||
|
||||
def all(locator, options = {})
|
||||
|
|
|
@ -4,6 +4,13 @@ module Capybara
|
|||
class Session
|
||||
include Searchable
|
||||
|
||||
DSL_METHODS = [
|
||||
:all, :attach_file, :body, :check, :choose, :click, :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
|
||||
]
|
||||
|
||||
attr_reader :mode, :app
|
||||
|
||||
def initialize(mode, app)
|
||||
|
|
|
@ -34,11 +34,13 @@ module Capybara
|
|||
end
|
||||
|
||||
def field(locator)
|
||||
fillable_field(locator).file_field(locator).checkbox(locator).radio_button(locator).select(locator)
|
||||
fillable_field(locator).input_field(:file, locator).checkbox(locator).radio_button(locator).select(locator)
|
||||
end
|
||||
|
||||
def fillable_field(locator)
|
||||
text_field(locator).password_field(locator).text_area(locator)
|
||||
[:text, :password, :email, :url, :search, :tel, :color].inject(text_area(locator)) do |all, type|
|
||||
all.input_field(type, locator)
|
||||
end
|
||||
end
|
||||
|
||||
def content(locator)
|
||||
|
@ -54,40 +56,27 @@ module Capybara
|
|||
end
|
||||
|
||||
def link(locator)
|
||||
append("//a[@id=#{s(locator)} or contains(.,#{s(locator)}) or @title=#{s(locator)}]")
|
||||
xpath = append("//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)})]")
|
||||
xpath.prepend("//a[@href][text()=#{s(locator)} or @title=#{s(locator)}]")
|
||||
end
|
||||
|
||||
def button(locator)
|
||||
xpath = append("//input[@type='submit' or @type='image'][@id=#{s(locator)} or contains(@value,#{s(locator)})]")
|
||||
xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]")
|
||||
end
|
||||
|
||||
def text_field(locator)
|
||||
add_field(locator, "//input[@type='text']")
|
||||
end
|
||||
|
||||
def password_field(locator)
|
||||
add_field(locator, "//input[@type='password']")
|
||||
xpath = xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]")
|
||||
xpath = xpath.prepend("//input[@type='submit' or @type='image'][@value=#{s(locator)}]")
|
||||
xpath = xpath.prepend("//button[@value=#{s(locator)} or text()=#{s(locator)}]")
|
||||
end
|
||||
|
||||
def text_area(locator)
|
||||
add_field(locator, "//textarea")
|
||||
end
|
||||
|
||||
def radio_button(locator)
|
||||
add_field(locator, "//input[@type='radio']")
|
||||
end
|
||||
|
||||
def checkbox(locator)
|
||||
add_field(locator, "//input[@type='checkbox']")
|
||||
end
|
||||
|
||||
def select(locator)
|
||||
add_field(locator, "//select")
|
||||
end
|
||||
|
||||
def file_field(locator)
|
||||
add_field(locator, "//input[@type='file']")
|
||||
def input_field(type, locator)
|
||||
add_field(locator, "//input[@type='#{type}']")
|
||||
end
|
||||
|
||||
def scope(scope)
|
||||
|
@ -106,6 +95,22 @@ module Capybara
|
|||
XPath.new(*[XPath.wrap(path).paths, @paths].flatten)
|
||||
end
|
||||
|
||||
def checkbox(locator)
|
||||
input_field(:checkbox, locator)
|
||||
end
|
||||
|
||||
def radio_button(locator)
|
||||
input_field(:radio, locator)
|
||||
end
|
||||
|
||||
[:text, :password, :email, :url, :search, :tel, :color, :file].each do |type|
|
||||
class_eval <<-RUBY, __FILE__, __LINE__+1
|
||||
def #{type}_field(locator)
|
||||
input_field(:#{type}, locator)
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def add_field(locator, field)
|
||||
|
|
|
@ -34,10 +34,6 @@ shared_examples_for 'driver' do
|
|||
@driver.visit('/with_html')
|
||||
end
|
||||
|
||||
it "should find the correct number of elements" do
|
||||
@driver.find('//a').size.should == 3
|
||||
end
|
||||
|
||||
it "should extract node texts" do
|
||||
@driver.find('//a')[0].text.should == 'labore'
|
||||
@driver.find('//a')[1].text.should == 'ullamco'
|
||||
|
|
|
@ -115,6 +115,11 @@ module ClickButtonSpec
|
|||
@session.click_button('Click')
|
||||
extract_results(@session)['first_name'].should == 'John'
|
||||
end
|
||||
|
||||
it "should prefer exact matches over partial matches" do
|
||||
@session.click_button('Just an input')
|
||||
extract_results(@session)['button'].should == 'button_second'
|
||||
end
|
||||
end
|
||||
|
||||
context "with id given on a button defined by <button> tag" do
|
||||
|
@ -134,6 +139,11 @@ module ClickButtonSpec
|
|||
@session.click_button('ck_me')
|
||||
extract_results(@session)['first_name'].should == 'John'
|
||||
end
|
||||
|
||||
it "should prefer exact matches over partial matches" do
|
||||
@session.click_button('Just a button')
|
||||
extract_results(@session)['button'].should == 'Just a button'
|
||||
end
|
||||
end
|
||||
|
||||
context "with a locator that doesn't exist" do
|
||||
|
@ -156,6 +166,18 @@ module ClickButtonSpec
|
|||
@session.click_button('Go FAR')
|
||||
@session.body.should include('You landed')
|
||||
end
|
||||
|
||||
it "should post pack to the same URL when no action given" do
|
||||
@session.visit('/postback')
|
||||
@session.click_button('With no action')
|
||||
@session.body.should include('Postback')
|
||||
end
|
||||
|
||||
it "should post pack to the same URL when blank action given" do
|
||||
@session.visit('/postback')
|
||||
@session.click_button('With blank action')
|
||||
@session.body.should include('Postback')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,6 +17,16 @@ module ClickLinkSpec
|
|||
@session.click_link('labore')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
|
||||
it "should accept partial matches" do
|
||||
@session.click_link('abo')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
|
||||
it "should prefer exact matches over partial matches" do
|
||||
@session.click_link('A link')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
end
|
||||
|
||||
context "with title given" do
|
||||
|
@ -24,6 +34,16 @@ module ClickLinkSpec
|
|||
@session.click_link('awesome title')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
|
||||
it "should accept partial matches" do
|
||||
@session.click_link('some tit')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
|
||||
it "should prefer exact matches over partial matches" do
|
||||
@session.click_link('a fine link')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
end
|
||||
|
||||
context "with a locator that doesn't exist" do
|
||||
|
@ -38,6 +58,31 @@ module ClickLinkSpec
|
|||
@session.click_link('Redirect')
|
||||
@session.body.should include('You landed')
|
||||
end
|
||||
|
||||
it "should do nothing on anchor links" do
|
||||
@session.fill_in("test_field", :with => 'blah')
|
||||
@session.click_link('Anchor')
|
||||
@session.find_field("test_field").value.should == 'blah'
|
||||
@session.click_link('Blank Anchor')
|
||||
@session.find_field("test_field").value.should == 'blah'
|
||||
end
|
||||
|
||||
it "should do nothing on URL+anchor links for the same page" do
|
||||
@session.fill_in("test_field", :with => 'blah')
|
||||
@session.click_link('Anchor on same page')
|
||||
@session.find_field("test_field").value.should == 'blah'
|
||||
end
|
||||
|
||||
it "should follow link on URL+anchor links for a different page" do
|
||||
@session.click_link('Anchor on different page')
|
||||
@session.body.should include('Bar')
|
||||
end
|
||||
|
||||
it "raise an error with links with no href" do
|
||||
running do
|
||||
@session.click_link('No Href')
|
||||
end.should raise_error(Capybara::ElementNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
spec/dsl/find_by_id_spec.rb
Normal file
18
spec/dsl/find_by_id_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module FindByIdSpec
|
||||
shared_examples_for "find_by_id" do
|
||||
describe '#find_by_id' do
|
||||
before do
|
||||
@session.visit('/with_html')
|
||||
end
|
||||
|
||||
it "should find any element by id" do
|
||||
@session.find_by_id('red').tag_name.should == 'a'
|
||||
@session.find_by_id('hidden_via_ancestor').tag_name.should == 'div'
|
||||
end
|
||||
|
||||
it "should return nil if no element with id is found" do
|
||||
@session.find_by_id('nothing_with_this_id').should be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -46,6 +46,7 @@ shared_examples_for "session" do
|
|||
it_should_behave_like "find_button"
|
||||
it_should_behave_like "find_field"
|
||||
it_should_behave_like "find_link"
|
||||
it_should_behave_like "find_by_id"
|
||||
it_should_behave_like "find"
|
||||
it_should_behave_like "has_content"
|
||||
it_should_behave_like "has_css"
|
||||
|
|
|
@ -150,3 +150,35 @@
|
|||
<input type="submit" value="Go FAR"/>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<form action="/form" method="post">
|
||||
<p>
|
||||
<label for="html5_email">Html5 Email</label>
|
||||
<input type="email" name="form[html5_email]" value="person@email.com" id="html5_email"/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="html5_url">Html5 Url</label>
|
||||
<input type="url" name="form[html5_url]" value="http://www.example.com" id="html5_url"/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="html5_search">Html5 Search</label>
|
||||
<input type="search" name="form[html5_search]" value="what are you looking for" id="html5_search"/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="html5_tel">Html5 Tel</label>
|
||||
<input type="tel" name="form[html5_tel]" value="911" id="html5_tel"/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="html5_color">Html5 Color</label>
|
||||
<input type="color" name="form[html5_color]" value="#FFF" id="html5_color"/>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<form action="/form" method="post">
|
||||
<p>
|
||||
<button type="submit" name="form[button]" value="button_first">Just an input that came first</button>
|
||||
<button type="submit" name="form[button]" value="button_second">Just an input</button>
|
||||
<input type="submit" name="form[button]" value="Just a button that came first"/>
|
||||
<input type="submit" name="form[button]" value="Just a button"/>
|
||||
</p>
|
||||
</form>
|
13
spec/views/postback.erb
Normal file
13
spec/views/postback.erb
Normal file
|
@ -0,0 +1,13 @@
|
|||
<h1>Postback</h1>
|
||||
|
||||
<form method="get">
|
||||
<p>
|
||||
<input type="submit" value="With no action">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<form action="" method="get">
|
||||
<p>
|
||||
<input type="submit" value="With blank action">
|
||||
</p>
|
||||
</form>
|
|
@ -16,6 +16,15 @@
|
|||
|
||||
<p>
|
||||
<input type="text" id="test_field" value="monkey"/>
|
||||
<a title="twas a fine link" href="/redirect">A link came first</a>
|
||||
<a title="a fine link" href="/with_simple_html">A link</a>
|
||||
<a>No Href</a>
|
||||
<a href="">Blank Href</a>
|
||||
<a href="#">Blank Anchor</a>
|
||||
<a href="#anchor">Anchor</a>
|
||||
<a href="/with_simple_html#anchor">Anchor on different page</a>
|
||||
<a href="/with_html#anchor">Anchor on same page</a>
|
||||
<input type="text" value="" id="test_field">
|
||||
</p>
|
||||
|
||||
<div id="hidden" style="display:none;">
|
||||
|
|
|
@ -83,9 +83,9 @@ describe Capybara::XPath do
|
|||
end
|
||||
|
||||
it "should be chainable" do
|
||||
@query = @xpath.field('First Name').password_field('First Name').to_s
|
||||
@query = @xpath.field('First Name').input_field(:password, 'First Name').to_s
|
||||
@driver.find(@query).first.value.should == 'John'
|
||||
@query = @xpath.field('Password').password_field('Password').to_s
|
||||
@query = @xpath.field('Password').input_field(:password, 'Password').to_s
|
||||
@driver.find(@query).first.value.should == 'seeekrit'
|
||||
end
|
||||
end
|
||||
|
@ -99,7 +99,7 @@ describe Capybara::XPath do
|
|||
@query = @xpath.fillable_field('Description').to_s
|
||||
@driver.find(@query).first.text.should == 'Descriptive text goes here'
|
||||
end
|
||||
|
||||
|
||||
it "should be chainable" do
|
||||
@query = @xpath.fillable_field('First Name').password_field('First Name').to_s
|
||||
@driver.find(@query).first.value.should == 'John'
|
||||
|
@ -220,4 +220,35 @@ describe Capybara::XPath do
|
|||
end
|
||||
end
|
||||
|
||||
[ [:email_field, 'html5_email', 'Html5 Email', 'person@email.com'],
|
||||
[:url_field, 'html5_url', 'Html5 Url', 'http://www.example.com'],
|
||||
[:search_field, 'html5_search', 'Html5 Search', 'what are you looking for'],
|
||||
[:tel_field, 'html5_tel', 'Html5 Tel', '911'],
|
||||
[:color_field, 'html5_color', 'Html5 Color', '#FFF']].each do |method, id, label, output|
|
||||
describe "##{method}" do
|
||||
it "should find a file field by label" do
|
||||
@query = @xpath.send(method, label).to_s
|
||||
@driver.find(@query).first.value.should == output
|
||||
end
|
||||
|
||||
it "should find a file field by id" do
|
||||
@query = @xpath.send(method, id).to_s
|
||||
@driver.find(@query).first.value.should == output
|
||||
end
|
||||
|
||||
it "should be chainable" do
|
||||
@query = @xpath.send(method, label).password_field(label).to_s
|
||||
@driver.find(@query).first.value.should == output
|
||||
@query = @xpath.send(method, 'Password').password_field('Password').to_s
|
||||
@driver.find(@query).first.value.should == 'seeekrit'
|
||||
end
|
||||
|
||||
it "should be a #fillable_field" do
|
||||
@query = @xpath.fillable_field(label).to_s
|
||||
@driver.find(@query).first.value.should == output
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue