README.rdoc in capybara-0.3.9 vs README.rdoc in capybara-0.4.0.rc
- old
+ new
@@ -1,18 +1,21 @@
-= capybara
+= Capybara
* http://github.com/jnicklas/capybara
== Description:
Capybara aims to simplify the process of integration testing Rack applications,
-such as Rails, Sinatra or Merb. It is inspired by and aims to replace Webrat as
-a DSL for interacting with a webapplication. It is agnostic about the driver
-running your tests and currently comes bundled with rack-test, Culerity,
-Celerity and Selenium support built in. env.js support is available as the
+such as Rails, Sinatra or Merb. Capybara simulates how a real user would
+interact with a web application. It is agnostic about the driver running your
+tests and currently comes bundled with rack-test, Culerity, Celerity and Selenium
+support built in. env.js support is available as the
{capybara-envjs gem}[http://github.com/smparkes/capybara-envjs].
+Online documentation is availbable
+{at rdoc.info}[http://rdoc.info/projects/jnicklas/capybara].
+
== Install:
Install as a gem:
sudo gem install capybara
@@ -29,39 +32,40 @@
Pull requests are very welcome! Make sure your patches are well tested, Capybara is
a testing tool after all. Please create a topic branch for every separate change
you make.
+Capybara uses bundler in development. To set up a development environment, simply do:
+
+ gem install bundler --pre
+ bundle install
+
== Using Capybara with Cucumber
-Capybara is built to work nicely with Cucumber. The API is very similar to
-Webrat, so if you know Webrat you should feel right at home. Support for
-Capybara is built into cucumber-rails 0.2. In your Rails app, just run:
+Capybara is built to work nicely with Cucumber. Support for Capybara is built into
+cucumber-rails. In your Rails app, just run:
- script/generate cucumber --capybara
+ rails generate cucumber:install --capybara
And everything should be set up and ready to go.
If you want to use Capybara with Cucumber outside Rails (for example with Merb
-or Sinatra), you'll need require capybara and set the Rack app manually:
+or Sinatra), you'll need to require Capybara and set the Rack app manually:
require 'capybara/cucumber'
Capybara.app = MyRackApp
Now you can use it in your steps:
When /I sign in/ do
- within("//form[@id='session']") do
+ within("#session") do
fill_in 'Login', :with => 'user@example.com'
fill_in 'Password', :with => 'password'
end
click_link 'Sign in'
end
-Please note that while Capybara uses XPath selectors by default, Cucumber explicitly
-changes this to CSS in `env.rb`. See "XPath and CSS" below.
-
== 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:
@@ -101,26 +105,17 @@
At the moment, Capybara supports Webdriver, also called Selenium 2.0, *not*
Selenium RC. Provided Firefox is installed, everything is set up for you, and
you should be able to start using Selenium right away.
-If desired, you can change Selenium browser to :chrome or :ie:
-
- require "selenium-webdriver"
- Selenium::WebDriver.for :chrome
-
== Celerity
Celerity only runs on JRuby, so you'll need to install the celerity gem under
JRuby:
jruby -S gem install celerity
-Note that some specs currently fail on celerity 0.7.5, due to a bug in recent
-versions of HTMLUnit. It is recommended you use celerity 0.7.4 for the time
-being.
-
== Culerity
Install celerity as noted above, make sure JRuby is in your path. Note that
Culerity doesn't seem to be working under Ruby 1.9 at the moment.
@@ -128,11 +123,13 @@
The {capybara-envjs driver}[http://github.com/smparkes/capybara-envjs]
uses the envjs gem ({GitHub}[http://github.com/smparkes/env-js],
{rubygems.org}[http://rubygems.org/gems/envjs]) to interpret
JavaScript outside the browser. The driver is installed by installing the capybara-envjs gem:
+
gem install capybara-envjs
+
More info about the driver and env.js are available through the links above. The envjs gem only supports
Ruby 1.8.7 at this time.
== The DSL
@@ -162,12 +159,12 @@
automatically follows any redirects, and submits forms associated with buttons.
click_link('id-of-link')
click_link('Link Text')
click_button('Save')
- click('Link Text') # Click either a link or a button
- click('Button Value')
+ click_link_or_button('Link Text')
+ click_link_or_button('Button Value')
=== Interacting with forms
Forms are everywhere in webapps, there are a number of tools for interacting
with the various form elements:
@@ -179,81 +176,86 @@
check('A Checkbox')
uncheck('A Checkbox')
attach_file('Image', '/path/to/image.jpg')
select('Option', :from => 'Select Box')
-=== Scoping
-
-Capybara makes it possible to restrict certain actions, such as interacting with
-forms or clicking links and buttons, to within a specific area of the page. For
-this purpose you can use the generic <tt>within</tt> method. Optionally you can
-specify which kind of selector (CSS or XPath to use).
-
- within("//li[@id='employee']") do
- fill_in 'Name', :with => 'Jimmy'
- end
-
- within(:css, "li#employee") do
- fill_in 'Name', :with => 'Jimmy'
- end
-
-You can choose which kind of selector Capybara uses by default, by setting
-<tt>Capybara.default_selector</tt>.
-
-There are special methods for restricting the scope to a specific fieldset,
-identified by either an id or the text of the fieldet's legend tag, and to a
-specific table, identified by either id or text of the table's caption tag.
-
- within_fieldset('Employee') do
- fill_in 'Name', :with => 'Jimmy'
- end
-
- within_table('Employee') do
- fill_in 'Name', :with => 'Jimmy'
- end
-
-You can also specify a scope to be used for future searches with the <tt>scope_to</tt>.
-It returns a handle to the session scoped to the provided selector.
-
- scope = page.scope_to("//div[@id='foo']")
- scope.has_xpath("//a[@class='bar']")
-
-You can also chain scopes by calling <tt>scope_to</tt> on an existing scope.
-
- more_scope = scope.scope_to("//p")
- more_scope.has_xpath("//a[@class='baz']")
-
=== Querying
Capybara has a rich set of options for querying the page for the existence of
certain elements, and working with and manipulating those elements.
+ page.has_selector?('table tr')
+ page.has_selector?(:xpath, '//table/tr')
+ page.has_no_selector?(:content)
+
page.has_xpath?('//table/tr')
page.has_css?('table tr.foo')
page.has_content?('foo')
-You can use with RSpecs magic matchers:
+You can these use with RSpec's magic matchers:
+ page.should have_selector('table tr')
+ page.should have_selector(:xpath, '//table/tr')
+ page.should have_no_selector(:content)
+
page.should have_xpath('//table/tr')
page.should have_css('table tr.foo')
page.should have_content('foo')
page.should have_no_content('foo')
Note that <tt>page.should have_no_xpath</tt> is preferred over
<tt>page.should_not have_xpath</tt>. Read the section on asynchronous JavaScript
for an explanation.
+=== Finding
+
You can also find specific elements, in order to manipulate them:
find_field('First Name').value
find_link('Hello').visible?
find_button('Send').click
- find('//table/tr').click
- locate("//*[@id='overlay'").find("//h1").click
+ find(:xpath, "//table/tr").click
+ find("#overlay").find("h1").click
all('a').each { |a| a[:href] }
+Note that <tt>find</tt> will wait for an element to appear on the page, as explained in the
+AJAX section. If the element does not appear it will raise an error.
+
+These elements all have all the Capybara DSL methods available, so you can restrict them
+to specific parts of the page:
+
+ find('#navigation').click_link('Home')
+ find('#navigation').should have_button('Sign out')
+
+=== Scoping
+
+Capybara makes it possible to restrict certain actions, such as interacting with
+forms or clicking links and buttons, to within a specific area of the page. For
+this purpose you can use the generic <tt>within</tt> method. Optionally you can
+specify which kind of selector to use.
+
+ within("li#employee") do
+ fill_in 'Name', :with => 'Jimmy'
+ end
+
+ within(:xpath, "//li[@id='employee']") do
+ fill_in 'Name', :with => 'Jimmy'
+ end
+
+There are special methods for restricting the scope to a specific fieldset,
+identified by either an id or the text of the fieldet's legend tag, and to a
+specific table, identified by either id or text of the table's caption tag.
+
+ within_fieldset('Employee') do
+ fill_in 'Name', :with => 'Jimmy'
+ end
+
+ within_table('Employee') do
+ fill_in 'Name', :with => 'Jimmy'
+ end
+
=== Scripting
In drivers which support it, you can easily execute JavaScript:
page.execute_script("$('body').empty()")
@@ -295,12 +297,12 @@
Capybara.default_wait_time = 5
Be aware that because of this behaviour, the following two statements are *not*
equivalent, and you should *always* use the latter!
- page.should_not have_xpath('//a')
- page.should have_no_xpath('//a')
+ page.should_not have_xpath('a')
+ page.should have_no_xpath('a')
The former would incorrectly wait for the content to appear, since the
asynchronous process has not yet removed the element from the page, it would
therefore fail, even though the code might be working correctly. The latter
correctly waits for the element to disappear from the page.
@@ -327,22 +329,27 @@
end
end
== Calling remote servers
-Normally Capybara expects to be testing an in-process Rack application, but you can also use it to talk to a web server running anywhere on the internets, by setting app_host:
+Normally Capybara expects to be testing an in-process Rack application, but you
+can also use it to talk to a web server running anywhere on the internets, by
+setting app_host:
Capybara.current_driver = :selenium
Capybara.app_host = 'http://www.google.com'
- ...
+ ...
visit('/')
-Note that rack-test does not support running against a remote server. With drivers that support it, you can also visit any URL directly:
+Note that rack-test does not support running against a remote server. With
+drivers that support it, you can also visit any URL directly:
visit('http://www.google.com')
-By default Capybara will try to boot a rack application automatically. You might want to switch off Capybara's rack server if you are running against a remote application:
+By default Capybara will try to boot a rack application automatically. You
+might want to switch off Capybara's rack server if you are running against a
+remote application:
Capybara.run_server = false
== Using the sessions manually
@@ -355,45 +362,108 @@
session.fill_in 'Login', :with => 'user@example.com'
session.fill_in 'Password', :with => 'password'
end
session.click_link 'Sign in'
-== XPath and CSS
+== XPath, CSS and selectors
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
+if you want to use XPath with your 'within' declarations for example, you'll need
to do:
- within(:css, 'ul li') { ... }
- find(:css, 'ul li').text
- locate(:css, 'input#name').value
+ within(:xpath, '//ul/li') { ... }
+ find(:xpath, '//ul/li').text
+ find(:xpath, '//li[contains(.//a[@href = "#"]/text(), "foo")]').value
-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:
+Alternatively you can set the default selector to XPath:
- Capybara.default_selector = :css
- within('ul li') { ... }
- find('ul li').text
- locate('input#name').value
+ Capybara.default_selector = :xpath
+ find('//ul/li').text
-== Gotchas:
+Capybara allows you to add custom selectors, which can be very useful if you
+find yourself using the same kinds of selectors very often:
-* Domain names (including subdomains) don't work under rack-test. Since it's a
- pain to set up subdomains for the other drivers anyway, you should consider an
- alternate solution. You might use
- {default_url_options}[https://gist.github.com/643a758320a2926bd2ed] in Rails
- for example.
+ Capybara::Selector.add(:id) { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
+ Capybara::Selector.add(:row) { |num| ".//tbody/tr[#{num}]" }
-* Access to session, request and response from the test is not possible. Maybe
- we'll do response headers at some point in the future, but the others really
- shouldn't be touched in an integration test anyway.
+These must always return an XPath expression as a String, or an XPath expression
+generated through the XPath gem. You can now use these selectors like this:
+ find(:id, 'post_123')
+ find(:row, 3)
+
+You can specify an optional :for option which will automatically use the
+selector if it matches the argument to find using ===:
+
+ Capybara::Selector.add(:id, :for => Symbol) { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
+
+Now use it like this:
+
+ find(:post_123)
+
+This :id selector is already built into Capybara by default, so you don't
+need to add it yourself.
+
+== Beware the XPath // trap
+
+In XPath the expression // means something very specific, and it might not be what
+you think. Contrary to common belief, // means "anywhere in the document" not "anywhere
+in the current context". As an example:
+
+ page.find(:xpath, '//body').all(:xpath, '//script')
+
+You might expect this to find all script tags in the body, but actually, it finds all
+script tags in the entire document, not only those in the body! What you're looking
+for is the .// expression which means "any descendant of the current node":
+
+ page.find(:xpath, '//body').all(:xpath, './/script')
+
+The same thing goes for within:
+
+ within(:xpath, '//body') do
+ page.find(:xpath, './/script')
+ within(:xpath, './/table/tbody') do
+ ...
+ end
+ end
+
+== Configuring and adding drivers
+
+Capybara makes it convenient to switch between different drivers. It also exposes
+an API to tweak those drivers with whatever settings you want, or to add your own
+drivers. This is how to switch the selenium driver to use chrome:
+
+ Capybara.register_driver :selenium do |app|
+ Capybara::Driver::Selenium.new(app, :browser => :chrome)
+ end
+
+However, it's also possible to give this a different name, so tests can switch
+between using different browsers effortlessly:
+
+ Capybara.register_driver :selenium_chrome do |app|
+ Capybara::Driver::Selenium.new(app, :browser => :chrome)
+ end
+
+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.
+
+== Gotchas:
+
+* Access to session and request is not possible from the test, Access to
+ response is limited. Some drivers allow access to response headers and HTTP
+ status code, but this kind of functionality is not provided by some drivers,
+ such as Selenium.
+
* Access to Rails specific stuff (such as <tt>controller</tt>) is unavailable,
since we're not using Rails' integration testing.
-* <tt><a href="#"></tt> Will cause problems under rack-test, please do
- <tt><a href="/same/url#"></tt> instead. You can achieve this in Rails with
- <tt>link_to('foo', :anchor => '')</tt>
+* Freezing time: It's common practice to mock out the Time so that features
+ that depend on the current Date work as expected. This can be problematic,
+ since Capybara's AJAX timing uses the system time, resulting in Capybara
+ never timing out and just hanging when a failure occurs. It's still possible to
+ use plugins which allow you to travel in time, rather than freeze time.
+ One such plugin is {Timecop}[http://github.com/jtrupiano/timecop].
== License:
(The MIT License)