Compare commits

..

7 Commits

Author SHA1 Message Date
Bryan Helmkamp
6da3371d84 Bumping version number to 0.3.4 2008-12-29 00:38:40 -05:00
Bryan Helmkamp
3a956304a3 History 2008-12-29 00:38:23 -05:00
Bryan Helmkamp
3d522a476a Fixing require statement for Nokogiri on JRuby 2008-12-29 00:36:53 -05:00
Bryan Helmkamp
6d5205b7b7 Bump nokogiri dependency version to 1.1.0 2008-12-29 00:36:00 -05:00
Bryan Helmkamp
5ccb10a411 History 2008-12-28 18:26:43 -05:00
Bryan Helmkamp
ce799388c2 Bumping version number to 0.3.3 2008-12-27 20:41:35 -05:00
Bryan Helmkamp
95e3260142 Update requires to fix compatibility with Nokogiri 1.1.0 2008-12-27 20:41:29 -05:00
246 changed files with 3694 additions and 10641 deletions

View File

@ -1,4 +0,0 @@
README.rdoc
lib/**/*.rb
History.txt
MIT-LICENSE.txt

12
.gitignore vendored
View File

@ -4,15 +4,3 @@ pkg
doc doc
ri ri
email.txt email.txt
.svn
log
.project
.loadpath
*.swp
results
test_apps
*.tmproj
*.log
*.pid
bin
vendor/gems

13
Gemfile
View File

@ -1,13 +0,0 @@
gem "rake", "0.8.7"
gem "rspec", "1.2.9"
gem "diff-lcs", "1.1.2"
gem "rack", "1.0.1"
gem "nokogiri", "1.4.0"
gem "merb-core", "1.0.15"
gem "selenium-client", "1.2.17"
gem "mechanize", "0.9.3"
gem "rails", "2.3.4"
gem "launchy", "0.3.3"
gem "rack-test", "0.5.2"
gem "sinatra", "0.9.4"
gem "mongrel", "1.1.5"

View File

@ -1,358 +1,44 @@
== 0.7.1 / 2010-04-26
* Minor enhancements
* Move verbose selenium output that can clutter build output behind setting
* Added application_port_for_selenium to webrat configuration. The use case is when you want to test through a web server sitting in front of your application server. (Luke Melia)
* New webrat configuration option selenium_firefox_profile which is passed to selenium server
* Allow submit_form to select by CSS too (Damian Janowski)
* Bug fixes
* Fix that current_url wasn't reflecting redirects in Mechanize [#332] (Emrys Ingersoll)
* Fix attach_file with nested params [#341] (Álvaro Gil)
* Fix that a 304 was considered a redirect (Larry Marburger)
* Fix selection of LABEL elements in Selenium mode under IE [#317] (Damian Janowski, Noah Davis)
* Fix have_xpath not matching negative expectation in the block [#182] (Luismi Cavallé)
== 0.7.0 / 2010-01-17
* Major enhancements
* Upgrade bundled Selenium server JAR to 2.0a1 (Henry Poydar and Jake Scruggs)
* Minor enhancements
* Save and open page directory specified via configuration, defaults to tmp dir otherwise current dir (Noah Davis)
* Bug fixes
* Added missing dependency "rack-test" to gemspec (LH #339) (Noah Davis)
* Removed save_and_open_page's rewriting of static asset paths before saving (was not actually working) (Noah Davis)
* Make "should contain" ignore extra whitespace when doing string comparisons (Noah Davis)
* Make selenium matchers handle negative match more consistently with positive match (Luke Melia)
== 0.6.0 / 2009-11-28
REMOVED: Support for Hpricot + REXML as an alternative to Nokogiri.
Hpricot and REXML were difficult to work with, REXML is terribly slow,
and Nokogiri is recommended even by the author of Hpricot (_why). Now
that Nokogiri works on JRuby, Webrat is going to use it as its sole
XML backend.
CHANGED: Due to a reorganization, if you're currently requiring "webrat/rspec-rails",
please change your code to require "webrat/integrations/rspec-rails"
* Minor enhancements
* Support Rails 2.3.4 JavaScript form authenticity tokens in simulated mode (Jonathan Weiss)
* When a timeout occurs in wait_for, include the HTML from Selenium in the exception
* Update the Merb support to be based directly on Rack (Simon Rozet)
* Support multiple select fields (Kieran P)
* When locating select options, always match against text, not HTML
* Bug fixes
* Remove newlines from HTTP Basic authentication credentials (Michael Klett)
* Require nokogiri form rspec-rails.rb (David Chelimsky)
* Fix logger issue when running inside Cucumber (Damian Janowski)
* Fix various issues related to submitting values with HTML entities (Kieran P)
* Call #to_i on the :count option in matchers (Michael Christenson II)
* Fix bug where multiline param values were getting truncated
== 0.5.3 / 2009-08-27
* Minor enhancements
* Remove unnecessary requires which are to the wrong paths on Edge Rails
== 0.5.1 / 2009-08-18
* Minor enhancements
* Base Webrat::MIME on Rack::Mime (Simon Rozet)
* Support file uploads in Rack mode (Simon Rozet)
* Bug fixes
* Fix bug in Webrat::Methods preventing Selenium mode from working [#277]
== 0.5.0 / 2009-08-12
* Major enhancements
* Depreacate :rack_test and :sinatra in favor of :rack, which uses Rack::Test (Simon Rozet)
* Minor enhancements
* Don't require rubygems at runtime (Simon Rozet)
== 0.4.5 / 2009-08-10
* Major enhancements
* Ruby 1.9 compatibility (Michael Fellinger, Jakub Kuźma)
* Improve performance (~2x) on JRuby by supporting Nokogiri
* Added support for external app servers in selenium mode (basically don't start one) (Mike Gaffney)
* Added support for uploading files for Merb (Ryan Carver)
* Minor enhancements
* Upgrade to selenium-client to 1.2.16 (Brian Landau)
* Upgrade selenium-server.jar to 1.0.1 (Brian Landau)
* Make redirect detection work in the face of rational maths (like when ruby-units is active) (Piers Cawley)
* Use Launchy to handle opening pages in the browser with cross-platform compatibility (Bryan Helmkamp)
* Added support for field_labeled_locators ending in non word characters
lh 148 (Zach Dennis)
* Filled in tests on click link lh 195 (diabolo)
* Added current_url to selenium session lh 215 (Luke Amdor)
* Added silence spec to selenium lh 238 (Martin Gamsjaeger aka snusnu)
* Added ability to configure the browser startup timeout for selenium lh 242 (Mike Gaffney, Mark Dodwell)
* Added delegation for field_named lh194 (pivotal labs)
* Added fix to keep from escaping field values in mechanize mode lh256 (jish)
* Adding fixes for click button/link and made the integration specs pass for the same in selenium lh254 (Ldiehl, Matthias Marschall)
* Adding clicking link by id in selenium mode lh221 (larrytheliquid)
* Adding fix for have_selector and have_xpath in descendent blocks lh234 (Thomas Jack)
* Adding fix for fields with labels with special characters (Thomas Jack, Mike Gaffney, Bryan Hemlkamp)
* Deprecated current_page lh50 (Mike Gaffney)
* Fixed issue with redirects and multiple hosts lh168 (Mutwin Kraus)
* Bug fixes
* Wait for application servers socket on 0.0.0.0 instead of the application_address
* Translate CSS and image paths with single quotes in save_and_open_page (Erin Staniland)
== 0.4.4 / 2009-04-06
* Major enhancements
* Make selenium process management code more robust and informative
* Minor enhancements
* Add support for Rails javascript post links (Mark Menard)
* Upgrade selenium-client dependency to 1.2.14, and update for new waiting
API (Balint Erdi)
* Change default app environment from "selenium" to "test"
* Bug fixes
* Don't create a new instance of WWW::Mechanize for each request
(Mark Menard)
* Select fields with duplicate selected options sent an incorrect value (Noah Davis)
== 0.4.3 / 2009-03-17
* Minor enhancements
* Support Rails 2.3. Use Rack::Utils to parse params (Matthew Ford)
* Support for "modular" Sinatra app style (Simon Rozet)
* Initial Merb and Sinatra compatibility for Selenium mode (Corey Donohoe)
* When faced with a label with no for attribute, that contains a hidden field
and another field, as can be the case in Rails 2.3's checkbox view,
webrat now locates the non-hidden field. (Luke Melia)
* Add application_framework config for Selenium mode to determine how to
start and stop the app server (Corey Donohoe)
* Bug fixes
* Fix following of absolute redirect URL in Sinatra (Simon Rozet)
== 0.4.2 / 2009-02-24
* Major enhancements
* Significant improvements to have_selector. It now supports specifying
attributes in a hash and :count and :content options. See
have_selector_spec.rb for more.
* Add the same functionality mentioned above to have_xpath
* Minor enhancements
* Squeeze extra whitespace out of failures messages from contain
matcher
* Detect infinite redirects and raise a Webrat::InfiniteRedirectError
(Daniel Lucraft)
* Bug fixes
* Properly quote single and double quotes strings in XPath
* Fix warning caused by Nokogiri deprecating CSS::Parser.parse
(Aaron Patterson)
* Accept do/end blocks in matchers. [#157] (Peter Jaros)
* Quote --chdir option to mongrel_rails to support RAILS_ROOTs with spaces
(T.J. VanSlyke)
== 0.4.1 / 2009-01-31
* Minor enhancements
* Support Sinatra 0.9 (Harry Vangberg)
* Update query param parsing to work with latest Edge Rails
* Added #redirected_to method to easily check where an external redirect was
redirected to (Adam Greene)
* Recognize input tags with type button (Lena Herrmann)
* Add uncheck method to Selenium mode (Lee Bankewitz)
* Bug fixes
* Make requests to a Rails app using a full URL instead of a relative path. This change
is helpful for Rails apps that use subdomains. (John Hwang and Zach Dennis)
* Follow redirects that are on the same domain but a different subdomain
(Adam Greene)
* rescue from Webrat::TimeoutError in selenium matchers which allows NegativeMatchers
to behave correctly (Noah Davis)
* Switch to using selenium.click instead of .check when checking a checkbox
(Noah Davis)
* Create tmp/pids directory if directory does not exist. (Amos King and Mike Gaffney)
* Setup deprecated writers for the selenium_environment= and selenium_port= config
* Ensure the previous pages params aren't passed through redirect (Daniel Lucraft and
Bryan Helmkamp)
* Labels should only search for fields within the current scope (Kyle Hargraves)
== 0.4.0 / 2009-01-18
* _IMPORTANT_ Breaking change:
* Removed init.rb auto-require of webrat/rails
* Removed auto-require of webrat/rails when requiring webrat when RAILS_ENV is
defined
In your env.rb or test_helper.rb file, ensure you have something like:
require "webrat"
Webrat.configure do |config|
config.mode = :rails
end
* Major enhancements
* Major improvements to Webrat::Selenium (Many contributors listed here)
* Add assert_* methods for using Webrat's matchers w/o RSpec (Mike Gaffney, Amos King)
* Added Webrat.configure method for Webrat configuration [#33] (Mike Gaffney)
* Added select_time, select_date, and select_datetime to API. [#36] (Ben Mabey)
* Use Hpricot and REXML when not parsing with Nokogiri (on JRuby, for example)
* Minor enhancements
* Added Selenium grid configuration and support. (Amos King && Cornel Borcean)
* Support passing an ActiveRecord model to #within when in Rails mode [#68] (Luke Melia)
* Make assert_* matchers in rails mode increment the assertions count [#123] (Amos King)
* Added assert_* matchers to selenium matchers [#110] (Amos King)
* Added assert_contain, assert_not_contain [#86] (Mike Gaffney, Amos King)
* Add configuration options for the Selenium environment and port (Kieran Pilkington)
* Maximize the browser window after initializing Selenium (Luke Melia)
* Better inspect output for Webrat elements
* Sets the Webrat mode with Configuration#mode= in the config block [#85] (Mike Gaffney)
* Detect if the document is XML or HTML using the Content-Type when in Rails mode
* Expose #selenium method for direct access to Selenium client
* Check nokogiri gem version before requiring nokogiri
* Include the Selenium server jar file in the gem (Bryan Helmkamp, Ben Schwarz)
* Added key_down, key_up and fire_event to Selenium session (Fernando Garcia)
* Fix outputing README during Rails plugin install (Fernando Garcia)
* Strip newlines when matching label text (Miha Filej)
* Add simple support for accessing Webrat's matchers from RSpec by requiring
"webrat/rspec-rails" (David Chelimsky)
* Support save_and_open_page in Windows and Cygwin (Mike Gaffney)
* Added RadioField#checked? to indicated whether or not a radio button is checked
(Luke Melia)
* Add spec:jruby rake task for running Webrat's spec suite with JRuby
* Added field_by_xpath to locate a Webrat::Field using arbitrary XPath expressions
* Helpful error message for missing option values [#40] (Ben Mabey)
* Add set_hidden_field method (Noah Davis, Bryan Helmkamp)
* Add submit_form method for submitting a form by ID (Noah Davis, Bryan Helmkamp)
* Switch to using Nokogiri.parse for simple XML/XHTML autodetection [#66]
* Removed Webrat.root method, which seemed to be unused
* Added automatic starting and stopping of the Selenium server and a Mongrel Rails
app server when using webrat/selenium
* Switch to using selenium-client gem and vendor selenium-server.jar (Luke Melia)
* Added gemspec so the gem builds on GitHub now
* Deprecate old style methods (fills_in is deprecated in favor of fill_in, etc.)
* Improvements to the README and RDoc (Bryan Helmkamp, Mike Gaffney)
* Allow clicking links by id and id regexp (Mike Gaffney)
* Raise Webrat::DisabledFieldError when attempting to manipulate a disabled field
* Raise Webrat::NotFoundErrors when an element is not found
* Raise Webrat::PageLoadError when a failure occurs so that application exceptions
can be more accurately tested (Ryan Briones)
* Helpful error message for missing option in select box. [#40] (Ben Mabey)
* Extracted save_and_open page to make it usable in Selenium mode (Luke Melia)
* Added save_and_open_screengrab for Selenium mode (Luke Melia)
* Bug fixes
* field_labeled should disregard labels without matching fields (Kyle Hargraves)
* Fixed bug where Scope was creating a new DOM rather than re-using the existing DOM.
[#105] (Zach Dennis)
* Support Rails > v2.2 by using ActionController::RequestParser for param parsing [#107]
(Marcello Nuccio)
* Raise a Webrat::NotFoundError if the scope passed to #within doesn't exist [#90]
* Match against link _text_ which decodes character references.
Useful for multibyte languages like Japanese (moronatural@gmail.com)
* Fix params hash generation for Mechanize when Merb is not defined [#62]
* Expose some Webrat methods that were missing from the Webrat::Methods module
(Low Chin Chau)
* Allow mechanize session to pass through basic auth (Ryan Briones)
* Improvements to the Mechanize support (Jeremy Burks)
* Fix following fully qualified local links (Lawrence Pit)
* Fixed bug where Webrat would lose complex parameters (like foo[bar[baz]][])
in Merb due to not correctly merging Mashes. (Drew Colthorp)
* Extend Rails' ActionController::IntegrationTest instead of
ActionController::Integration::Session (Fixes using Webrat's #select method and
avoids usage of method_missing)
* Ensure that Webrat::MechanizeSession.request_page always uses an absolute
URL. (Graham Ashton)
* Strip anchor tags from URIs before passing to Rails integration session
(Noah Davis)
* Treat text and regexp when matching Selenium buttons (Ross Kaffenberger)
* Allow SeleniumSession's click_button to be called without an argument without
blowing up (Luke Melia)
== 0.3.4 / 2008-12-29 == 0.3.4 / 2008-12-29
* 1 Minor enhancement * 1 Minor enhancement
* Fix compatibility with Nokogiri 1.1.0 on JRuby * Fix compatibility with Nokogiri 1.1.0 on JRuby
* 1 Bug fix * 1 Bug fix
* Correct version for Nokogiri dependency in gem * Correct version for Nokogiri dependency in gem
== 0.3.3 / 2008-12-28 == 0.3.3 / 2008-12-28
* 1 Minor enhancement * 1 Minor enhancement
* Fix compatibility with Nokogiri 1.1.0 on MRI * Fix compatibility with Nokogiri 1.1.0 on MRI
== 0.3.2 / 2008-11-08 == 0.3.2 / 2008-11-08
* 1 Minor enhancement * 1 Minor enhancement
* Fixes behavior or have_tag when a block is passed. It passes the matched node(s) * Fixes behavior or have_tag when a block is passed. It passes the matched node(s) to the block for further specs again. (Carl Lerche)
to the block for further specs again. (Carl Lerche)
== 0.3.1 / 2008-11-07 == 0.3.1 / 2008-11-07
* 1 Minor enhancement * 1 Minor enhancement
* Use @_webrat_session instance variable instead of @session for Merb integration * Use @_webrat_session instance variable instead of @session for Merb integration to avoid collisions
to avoid collisions
== 0.3.0 / 2008-11-07 == 0.3.0 / 2008-11-07
* 4 Major enhancements * Major enhancements
* Added Merb support (Gwyn Morfey, Jeremy Burks, Rob Kaufman, Yehuda Katz) * Added Merb support (Gwyn Morfey, Jeremy Burks, Rob Kaufman, Yehuda Katz)
* Added experimental Selenium support (Luke Melia) * Added experimental Selenium support (Luke Melia)
* Add have_selector, have_xpath, have_tag and contain matchers from Merb * Add have_selector, have_xpath, have_tag and contain matchers from Merb
* Switch from Hpricot to Nokogiri for XML parsing (thanks, Aaron Patterson) * Switch from Hpricot to Nokogiri for XML parsing (thanks, Aaron Patterson)
* 37 Minor enhancements * Minor enhancements
* Added #within for manipulating the current page within a selector scope * Added #within for manipulating the current page within a selector scope
* Add support for file fields via #attaches_file method (Rails only at the moment) * Add support for file fields via #attaches_file method (Rails only at the moment) (Kyle Hargraves)
(Kyle Hargraves)
* Add support for simulating SSL requests (Luke Melia) * Add support for simulating SSL requests (Luke Melia)
* Added #basic_auth(user, pass) to support HTTP Basic Auth (Aslak Hellesøy) * Added #basic_auth(user, pass) to support HTTP Basic Auth (Aslak Hellesøy)
* Added support for Sinatra and Rack (Aslak Hellesøy) * Added support for Sinatra and Rack (Aslak Hellesøy)
@ -365,37 +51,33 @@ CHANGED: Due to a reorganization, if you're currently requiring "webrat/rspec-ra
* Allow clicking links by a regular expression * Allow clicking links by a regular expression
* Add #http_accept for including MIME type HTTP "Accept" headers (Ryan Briones) * Add #http_accept for including MIME type HTTP "Accept" headers (Ryan Briones)
* Add #header to support inclusion of custom HTTP headers (Ryan Briones) * Add #header to support inclusion of custom HTTP headers (Ryan Briones)
* Consider response codes 200-499 as successful enough to not raise a Webrat error * Consider response codes 200-499 as successful enough to not raise a Webrat error (David Leal)
(David Leal) * Add Webrat.root method for cross-framework support (Krzysztof Zylawy)
* Add support for clicking areas of an image map (Alex Lang) * Add support for clicking areas of an image map (Alex Lang)
* Support relative links, including href="?foo=bar" (Kyle Hargraves) * Support relative links, including href="?foo=bar" (Kyle Hargraves)
* Separated Rails-specific code from the Webrat core to make it easier to use * Separated Rails-specific code from the Webrat core to make it easier to use Webrat with other environments
Webrat with other environments
* Alias visits as visit, clicks_link as click_link, etc. for better readability * Alias visits as visit, clicks_link as click_link, etc. for better readability
* Raise error when trying to interact with a disabled form element (Luke Melia) * Raise error when trying to interact with a disabled form element (Luke Melia)
* Don't send disabled form elements to the server (Nicholas A. Evans) * Don't send disabled form elements to the server (Nicholas A. Evans)
* Display response body when the page load is not successful (David Leal) * Display response body when the page load is not successful (David Leal)
* CGI escape form field values (Miha Filej) * CGI escape form field values (Miha Filej)
* Add support for redirect_to :back by sending HTTP_REFERER headers * Add support for redirect_to :back by sending HTTP_REFERER headers (Hendrik Volkmer)
(Hendrik Volkmer)
* Expose current DOM (as an Hpricot object) as #current_dom * Expose current DOM (as an Hpricot object) as #current_dom
* Add support for disabling JavaScript when clicking a link to enable testing of * Add support for disabling JavaScript when clicking a link to enable testing of both JS
both JS and non-JS implementations (Luke Melia and Bryan Helmkamp) and non-JS implementations (Luke Melia and Bryan Helmkamp)
* Support &nbsp's as spaces in matching link text (Luke Melia) * Support &nbsp's as spaces in matching link text (Luke Melia)
* Support button elements (Nick Sieger) * Support button elements (Nick Sieger)
* Support matching select options by regexp (Kyle Hargraves) * Support matching select options by regexp (Kyle Hargraves)
* save_and_open_page rewrites css and image references to provide a friendlier * save_and_open_page rewrites css and image references to provide a friendlier debugging experience (Luke Melia)
debugging experience (Luke Melia) * Added support for matching alt attributes in fields (primarly for clicks_button) (Aaron Quint)
* Added support for matching alt attributes in fields (primarly for clicks_button)
(Aaron Quint)
* Support '&' in submitted values (Kyle Hargraves) * Support '&' in submitted values (Kyle Hargraves)
* Support clicking links by title (Dan Barry) * Support clicking links by title (Dan Barry)
* Added missing spec for clicking image buttons (Tim Harper) * Added missing spec for clicking image buttons (Tim Harper)
* Switched tests to specs, and from Mocha to RSpec's mocking library * Switched tests to specs, and from Mocha to RSpec's mocking library
* Add support to click_button for IDs (Gwyn Morfey) * Add support to click_button for IDs (Gwyn Morfey)
* Miscellaneous core refactorings (Jan Suchal) * Miscellaneous core refactorings (Jan Suchal)
* 8 Bug fixes * Bug fixes
* Fix initialization of WWW::Mechanize (Derek Kastner) * Fix initialization of WWW::Mechanize (Derek Kastner)
* Don't open blank pages in the browser (Kyle Hargraves) * Don't open blank pages in the browser (Kyle Hargraves)
@ -404,9 +86,8 @@ CHANGED: Due to a reorganization, if you're currently requiring "webrat/rspec-ra
* Fix bug with empty select list option (Kyle Hargraves) * Fix bug with empty select list option (Kyle Hargraves)
* Fix regression of not sending default values in password fields * Fix regression of not sending default values in password fields
* Don't explode if encountering inputs with no type attribute (assume text) * Don't explode if encountering inputs with no type attribute (assume text)
* Fix bug where choosing a radio button in a series with a default submitted the * Fix bug where choosing a radio button in a series with a default submitted the incorrect field value (Luke Melia)
incorrect field value (Luke Melia)
== 0.2.0 / 2008-04-04 == 0.2.0 / 2008-04-04
* 4 Major enhancements * 4 Major enhancements
@ -416,35 +97,31 @@ CHANGED: Due to a reorganization, if you're currently requiring "webrat/rspec-ra
* Add basic support for Rails-generated JavaScript link tags * Add basic support for Rails-generated JavaScript link tags
* Add support for checkboxes (Patches from Kyle Hargraves and Jarkko Laine) * Add support for checkboxes (Patches from Kyle Hargraves and Jarkko Laine)
* Add support for textarea fields (Sacha Schlegel) * Add support for textarea fields (Sacha Schlegel)
* 8 Minor enhancements * 8 Minor enhancements
* Added reloads method to reload the page (Kamal Fariz Mahyuddi) * Added reloads method to reload the page (Kamal Fariz Mahyuddi)
* Prevent making a request if clicking on local anchor link (Kamal Fariz Mahyuddi) * Prevent making a request if clicking on local anchor link (Kamal Fariz Mahyuddi)
* Added clicks_link_within(selector, link_text), allowing restricting link search * Added clicks_link_within(selector, link_text), allowing restricting link search
to within a given css selector (Luke Melia) to within a given css selector (Luke Melia)
* Allow specifying the input name/label when doing a select (David Chelimsky) * Allow specifying the input name/label when doing a select (David Chelimsky)
* Raise a specific exception if the developer tries to manipulate form elements * Raise a specific exception if the developer tries to manipulate form elements before loading a page (James Deville)
before loading a page (James Deville)
* Add support for alternate POST, PUT and DELETE link clicking (Kyle Hargraves) * Add support for alternate POST, PUT and DELETE link clicking (Kyle Hargraves)
* Change clicks_link to find the shortest matching link (Luke Melia) * Change clicks_link to find the shortest matching link (Luke Melia)
* Improve matching for labels in potentially ambiguous cases * Improve matching for labels in potentially ambiguous cases
* 7 Bug fixes * 7 Bug fixes
* Fix incorrect serializing of collection inputs, i.e. name contains [] * Fix incorrect serializing of collection inputs, i.e. name contains [] (Kamal Fariz Mahyuddi)
(Kamal Fariz Mahyuddi)
* Serialize empty text field values just like browsers (Kamal Fariz Mahyuddi) * Serialize empty text field values just like browsers (Kamal Fariz Mahyuddi)
* Quick fix to avoid @dom not initialized warnings (Kamal Fariz Mahyuddi) * Quick fix to avoid @dom not initialized warnings (Kamal Fariz Mahyuddi)
* Docfix: bad reference to #select method in README (Luke Melia) * Docfix: bad reference to #select method in README (Luke Melia)
* Ensure Rails-style checkboxes work properly (checkboxes followed by a hidden * Ensure Rails-style checkboxes work properly (checkboxes followed by a hidden input with the same name)
input with the same name)
* Fix Edge Rails (a.k.a. 2.0 RC) compatibility (David Chelimsky) * Fix Edge Rails (a.k.a. 2.0 RC) compatibility (David Chelimsky)
* Support param hashes nested more than one level (David Chelimsky) * Support param hashes nested more than one level (David Chelimsky)
== 0.1.0 / 2007-11-28 == 0.1.0 / 2007-11-28
* 1 major enhancement * 1 major enhancement
* Birthday! * Birthday!

View File

@ -1,85 +0,0 @@
= Webrat - Ruby Acceptance Testing for Web applications
- http://gitrdoc.com/brynary/webrat
- http://groups.google.com/group/webrat
- http://webrat.lighthouseapp.com/
- http://github.com/brynary/webrat
- #webrat on Freenode
== Description
Webrat lets you quickly write expressive and robust acceptance tests for a Ruby
web application.
== Features
* Browser Simulator for expressive, high level acceptance testing without the
performance hit and browser dependency of Selenium or Watir (See Webrat::Session)
* Use the same API for Browser Simulator and real Selenium tests using
Webrat::Selenium when necessary (eg. for testing AJAX interactions)
* Supports multiple Ruby web frameworks: Rails, Merb and Sinatra
* Supports popular test frameworks: RSpec, Cucumber, Test::Unit and Shoulda
* Webrat::Matchers API for verifying rendered HTML using CSS, XPath, etc.
== Example
class SignupTest < ActionController::IntegrationTest
def test_trial_account_sign_up
visit home_path
click_link "Sign up"
fill_in "Email", :with => "good@example.com"
select "Free account"
click_button "Register"
end
end
Behind the scenes, Webrat will ensure:
* If a link, form field or button is missing, the test will fail.
* If a URL is invalid, the test will fail.
* If a page load or form submission is unsuccessful, the test will fail.
== Installing Nokogiri
Users of Debian Linux (e.g. Ubuntu) need to run:
sudo apt-get install libxslt1-dev libxml2-dev.
Otherwise the Nokogiri gem, which Webrat depends on, won't install properly.
== Install for Rails
To install the latest release as a gem:
sudo gem install webrat
To install the latest code as a plugin: (_Note:_ This may be less stable than using a released version)
script/plugin install git://github.com/brynary/webrat.git
In your test_helper.rb or env.rb (for Cucumber) add:
require "webrat"
Webrat.configure do |config|
config.mode = :rails
end
== Install with Merb
Merb 1.0 has built-in, seamless Webrat support. Just start using
methods from Webrat::Session in your specs.
== Authors
- Maintained by {Bryan Helmkamp}[mailto:bryan@brynary.com]
- Original code written by {Seth Fitzsimmons}[mailto:seth@mojodna.net]
- Initial development was sponsored by EastMedia[http://www.eastmedia.com]
- Many other contributors. See attributions in History.txt
== License
Copyright (c) 2007-2008 Bryan Helmkamp, Seth Fitzsimmons.
See MIT-LICENSE.txt in this directory.

90
README.txt Normal file
View File

@ -0,0 +1,90 @@
=== Webrat
- [Code on GitHub](http://github.com/brynary/webrat)
- [Tickets on Lighthouse](http://webrat.lighthouseapp.com/)
=== Description
Webrat (_Ruby Acceptance Testing for Web applications_)
lets you quickly write robust and thorough acceptance tests for a Ruby
web application. By leveraging the DOM, it can run tests similarly to an
in-browser testing solution without the associated performance hit (and
browser dependency). The result is tests that are less fragile and more
effective at verifying that the app will respond properly to users.
When comparing Webrat with an in-browser testing solution like Watir or
Selenium, the primary consideration should be how much JavaScript the
application uses. In-browser testing is currently the only way to test JS, and
that may make it a requirement for your project. If JavaScript is not central
to your application, Webrat is a simpler, effective solution that will let you
run your tests much faster and more frequently.
Initial development was sponsored by [EastMedia](http://www.eastmedia.com).
=== Synopsis
def test_sign_up
visit "/"
click_link "Sign up"
fill_in "Email", :with => "good@example.com"
select "Free account"
click_button "Register"
...
end
Behind the scenes, this will perform the following work:
1. Verify that loading the home page is successful
2. Verify that a "Sign up" link exists on the home page
3. Verify that loading the URL pointed to by the "Sign up" link leads to a
successful page
4. Verify that there is an "Email" input field on the Sign Up page
5. Verify that there is an select field on the Sign Up page with an option for
"Free account"
6. Verify that there is a "Register" submit button on the page
7. Verify that submitting the Sign Up form with the values "good@example.com"
and "Free account" leads to a successful page
Take special note of the things _not_ specified in that test, that might cause
tests to break unnecessarily as your application evolves:
- The input field IDs or names (e.g. "user_email" or "user[email]"), which
could change if you rename a model
- The ID of the form element (Webrat can do a good job of guessing, even if
there are multiple forms on the page.)
- The URLs of links followed
- The URL the form submission should be sent to, which could change if you
adjust your routes or controllers
- The HTTP method for the login request
A test written with Webrat can handle these changes to these without any modifications.
=== Merb
To avoid losing sessions, you need this in environments/test.rb:
Merb::Config.use do |c|
c[:session_store] = 'memory'
end
=== Install
To install the latest release:
sudo gem install webrat
In your stories/helper.rb:
require "webrat"
You could also unpack the gem into vendor/plugins.
=== Authors
- Maintained by [Bryan Helmkamp](mailto:bryan@brynary.com)
- Original code written by [Seth Fitzsimmons](mailto:seth@mojodna.net)
- Many other contributors. See attributions in History.txt
=== License
Copyright (c) 2007 Bryan Helmkamp, Seth Fitzsimmons.
See MIT-LICENSE.txt in this directory.

212
Rakefile
View File

@ -1,143 +1,89 @@
begin require 'rubygems'
require 'spec/rake/spectask' require "rake/gempackagetask"
rescue LoadError require 'rake/rdoctask'
desc "Run specs" require "rake/clean"
task(:spec) { $stderr.puts '`gem install rspec` to run specs' } require 'spec'
else require 'spec/rake/spectask'
desc "Run API and Core specs" require 'spec/rake/verify_rcov'
Spec::Rake::SpecTask.new do |t| require './lib/webrat.rb'
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
t.spec_files = FileList['spec/public/**/*_spec.rb'] + FileList['spec/private/**/*_spec.rb']
end
desc "Run all specs in spec directory with RCov" ##############################################################################
Spec::Rake::SpecTask.new(:rcov) do |t| # Package && release
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""] ##############################################################################
t.spec_files = FileList['spec/public/**/*_spec.rb'] + FileList['spec/private/**/*_spec.rb'] spec = Gem::Specification.new do |s|
t.rcov = true s.name = "webrat"
t.rcov_opts = lambda do s.version = Webrat::VERSION
IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten s.platform = Gem::Platform::RUBY
end s.author = "Bryan Helmkamp"
s.email = "bryan" + "@" + "brynary.com"
s.homepage = "http://github.com/brynary/webrat"
s.summary = "Webrat. Ruby Acceptance Testing for Web applications"
s.bindir = "bin"
s.description = s.summary
s.require_path = "lib"
s.files = %w(History.txt init.rb install.rb MIT-LICENSE.txt README.txt Rakefile TODO.txt) + Dir["lib/**/*"]
# rdoc
s.has_rdoc = true
s.extra_rdoc_files = %w(README.txt MIT-LICENSE.txt)
# Dependencies
s.add_dependency "nokogiri", ">= 1.1.0"
end
Rake::GemPackageTask.new(spec) do |package|
package.gem_spec = spec
end
desc 'Show information about the gem.'
task :debug_gem do
puts spec.to_ruby
end
CLEAN.include ["pkg", "*.gem", "doc", "ri", "coverage"]
desc "Upload rdoc to brynary.com"
task :publish_rdoc => :docs do
sh "scp -r doc/ brynary.com:/apps/uploads/webrat"
end
desc "Run API and Core specs"
Spec::Rake::SpecTask.new do |t|
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
t.spec_files = FileList['spec/**/*_spec.rb']
end
desc "Run all specs in spec directory with RCov"
Spec::Rake::SpecTask.new(:rcov) do |t|
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
t.spec_files = FileList['spec/**/*_spec.rb']
t.rcov = true
t.rcov_opts = lambda do
IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
end end
end end
desc "Run everything against multiruby" RCov::VerifyTask.new(:verify_rcov => :rcov) do |t|
task :multiruby do t.threshold = 96.2 # Make sure you have rcov 0.7 or higher!
result = system "multiruby -S rake spec"
raise "Multiruby tests failed" unless result
result = system "jruby -S rake spec"
raise "JRuby tests failed" unless result
Dir.chdir "spec/integration/rails" do
result = system "multiruby -S rake test_unit:rails"
raise "Rails integration tests failed" unless result
result = system "jruby -S rake test_unit:rails"
raise "Rails integration tests failed" unless result
end
Dir.chdir "spec/integration/merb" do
result = system "multiruby -S rake spec"
raise "Merb integration tests failed" unless result
result = system "jruby -S rake spec"
raise "Rails integration tests failed" unless result
end
Dir.chdir "spec/integration/sinatra" do
result = system "multiruby -S rake test"
raise "Sinatra integration tests failed" unless result
result = system "jruby -S rake test"
raise "Sinatra integration tests failed" unless result
end
Dir.chdir "spec/integration/rack" do
result = system "multiruby -S rake test"
raise "Rack integration tests failed" unless result
result = system "jruby -S rake test"
raise "Rack integration tests failed" unless result
end
puts
puts "Multiruby OK!"
end end
desc "Run each spec in isolation to test for dependency issues" task :default do
task :spec_deps do Rake::Task["verify_rcov"].invoke
Dir["spec/**/*_spec.rb"].each do |test|
if !system("spec #{test} &> /dev/null")
puts "Dependency Issues: #{test}"
end
end
end end
namespace :spec do desc 'Install the package as a gem.'
desc "Run the integration specs" task :install_gem => [:clean, :package] do
task :integration => [ gem = Dir['pkg/*.gem'].first
"integration:rack", sh "sudo gem install --local #{gem}"
"integration:sinatra",
"integration:merb",
"integration:mechanize",
"integration:rails:webrat",
"integration:rails:selenium",
]
namespace :integration do
namespace :rails do
task :selenium do
Dir.chdir "spec/integration/rails" do
result = system "rake -rubygems test_unit:selenium"
raise "Rails integration tests failed" unless result
end
end
task :webrat do
Dir.chdir "spec/integration/rails" do
result = system "rake -rubygems test_unit:rails"
raise "Rails integration tests failed" unless result
end
end
end
desc "Run the Merb integration specs"
task :merb do
Dir.chdir "spec/integration/merb" do
result = system "rake -rubygems spec"
raise "Merb integration tests failed" unless result
end
end
desc "Run the Sinatra integration specs"
task :sinatra do
Dir.chdir "spec/integration/sinatra" do
result = system "rake -rubygems test"
raise "Sinatra integration tests failed" unless result
end
end
desc "Run the Sinatra integration specs"
task :rack do
Dir.chdir "spec/integration/rack" do
result = system "rake -rubygems test"
raise "Rack integration tests failed" unless result
end
end
desc "Run the Mechanize integration specs"
task :mechanize do
Dir.chdir "spec/integration/mechanize" do
result = system "rake -rubygems spec"
raise "Mechanize integration tests failed" unless result
end
end
end
end end
desc 'Removes trailing whitespace' Rake::RDocTask.new(:docs) do |rd|
task :whitespace do rd.main = "README.txt"
sh %{find . -name '*.rb' -exec sed -i '' 's/ *$//g' {} \\;} rd.rdoc_dir = 'doc'
end files = spec.files.grep(/^(lib|bin|ext)|txt$/)
files -= ["TODO.txt"]
task :default => :spec files -= files.grep(/\.js$/)
rd.rdoc_files = files.uniq
title = "webrat-#{Webrat::VERSION} Documentation"
rd.options << "-t #{title}"
end

10
TODO.txt Normal file
View File

@ -0,0 +1,10 @@
Run tests in separate processes to eliminate constant-level dependencies
Add rake tasks for selenium runs
Add tests for selenium
Add tests for locator strategies
Use Webrat::Methods for Rails integration
Get file uploads workign with merb
Fix #within scoping for forms that exist outside the scope
Make current_url work with redirections
Track the current form based on the location of the last manipulated input, use this as a default for click_button
Verify SSL support in Rails and Merb

118
Thorfile
View File

@ -1,118 +0,0 @@
module GemHelpers
def generate_gemspec
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "lib")))
require "webrat"
Gem::Specification.new do |s|
s.name = "webrat"
s.version = Webrat::VERSION
s.author = "Bryan Helmkamp"
s.email = "bryan@brynary.com"
s.homepage = "http://github.com/brynary/webrat"
s.summary = "Ruby Acceptance Testing for Web applications"
s.description = <<-EOS.strip
Webrat lets you quickly write expressive and robust acceptance tests
for a Ruby web application. It supports simulating a browser inside
a Ruby process to avoid the performance hit and browser dependency of
Selenium or Watir, but the same API can also be used to drive real
Selenium tests when necessary (eg. for testing AJAX interactions).
Most Ruby web frameworks and testing frameworks are supported.
EOS
s.rubyforge_project = "webrat"
require "git"
repo = Git.open(".")
s.files = normalize_files(repo.ls_files.keys - repo.lib.ignored_files)
s.test_files = normalize_files(Dir['spec/**/*.rb'] - repo.lib.ignored_files)
s.has_rdoc = true
s.extra_rdoc_files = %w[README.rdoc MIT-LICENSE.txt History.txt]
s.add_dependency "nokogiri", ">= 1.2.0"
s.add_dependency "rack", ">= 1.0"
s.add_dependency "rack-test", ">= 0.5.3"
end
end
def normalize_files(array)
# only keep files, no directories, and sort
array.select do |path|
File.file?(path)
end.sort
end
# Adds extra space when outputting an array. This helps create better version
# control diffs, because otherwise it is all on the same line.
def prettyify_array(gemspec_ruby, array_name)
gemspec_ruby.gsub(/s\.#{array_name.to_s} = \[.+?\]/) do |match|
leadin, files = match[0..-2].split("[")
leadin + "[\n #{files.split(",").join(",\n ")}\n ]"
end
end
def read_gemspec
@read_gemspec ||= eval(File.read("webrat.gemspec"))
end
def sh(command)
puts command
system command
end
end
class Default < Thor
include GemHelpers
desc "gemspec", "Regenerate webrat.gemspec"
def gemspec
File.open("webrat.gemspec", "w") do |file|
gemspec_ruby = generate_gemspec.to_ruby
gemspec_ruby = prettyify_array(gemspec_ruby, :files)
gemspec_ruby = prettyify_array(gemspec_ruby, :test_files)
gemspec_ruby = prettyify_array(gemspec_ruby, :extra_rdoc_files)
file.write gemspec_ruby
end
puts "Wrote gemspec to webrat.gemspec"
read_gemspec.validate
end
desc "build", "Build a webrat gem"
def build
sh "gem build webrat.gemspec"
FileUtils.mkdir_p "pkg"
FileUtils.mv read_gemspec.file_name, "pkg"
end
desc "install", "Install the latest built gem"
def install
sh "gem install --local pkg/#{read_gemspec.file_name}"
end
desc "release", "Release the current branch to GitHub and Gemcutter"
def release
gemspec
build
Release.new.tag
Release.new.gem
end
end
class Release < Thor
include GemHelpers
desc "tag", "Tag the gem on the origin server"
def tag
release_tag = "v#{read_gemspec.version}"
sh "git tag -a #{release_tag} -m 'Tagging #{release_tag}'"
sh "git push origin #{release_tag}"
end
desc "gem", "Push the gem to Gemcutter"
def gem
sh "gem push pkg/#{read_gemspec.file_name}"
end
end

3
init.rb Normal file
View File

@ -0,0 +1,3 @@
if RAILS_ENV == "test" || RAILS_ENV == "selenium"
require File.join(File.dirname(__FILE__), "lib", "webrat")
end

View File

@ -1 +1 @@
puts IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) puts IO.read(File.join(File.dirname(__FILE__), 'README'))

View File

@ -1,18 +1,39 @@
require "rack" require "rubygems"
require "nokogiri"
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
module Webrat module Webrat
VERSION = "0.7.2.pre" VERSION = '0.3.4'
autoload :MechanizeAdapter, "webrat/adapters/mechanize" def self.root #:nodoc:
autoload :MerbAdapter, "webrat/adapters/merb" defined?(RAILS_ROOT) ? RAILS_ROOT : Merb.root
autoload :RackAdapter, "webrat/adapters/rack" end
autoload :RailsAdapter, "webrat/adapters/rails"
autoload :SinatraAdapter, "webrat/adapters/sinatra" def self.require_xml
gem "nokogiri", ">= 1.1.0"
# The common base class for all exceptions raised by Webrat.
class WebratError < StandardError # We need Nokogiri's CSS to XPath support, even if using
# REXML and Hpricot for parsing and searching
require "nokogiri"
if on_java?
require "hpricot"
require "rexml/document"
else
require "webrat/core/nokogiri"
end
end
def self.on_java?
RUBY_PLATFORM =~ /java/
end end
end end
Webrat.require_xml
require "webrat/core" require "webrat/core"
# TODO: This is probably not a good idea.
# Probably better for webrat users to require "webrat/rails" etc. directly
if defined?(RAILS_ENV)
require "webrat/rails"
end

View File

@ -1,94 +0,0 @@
require "mechanize"
module Webrat #:nodoc:
class MechanizeAdapter #:nodoc:
extend Forwardable
Mechanize = WWW::Mechanize if defined?(WWW::Mechanize)
attr_accessor :response
alias :page :response
def initialize(*args)
end
def request_page(url, http_method, data) #:nodoc:
super(absolute_url(url), http_method, data)
end
def get(url, data, headers_argument_not_used = nil)
@response = mechanize.get(url, data)
end
def post(url, data, headers_argument_not_used = nil)
post_data = data.inject({}) do |memo, param|
case param
when Hash
param.each {|attribute, value| memo[attribute] = value }
memo
when Array
case param.last
when Hash
param.last.each {|attribute, value| memo["#{param.first}[#{attribute}]"] = value }
else
memo[param.first] = param.last
end
memo
end
end
@response = mechanize.post(url, post_data)
end
def response_body
@response.content
end
def response_code
@response.code.to_i
end
def response_headers
@response.header
end
def mechanize
@mechanize ||= begin
mechanize = Mechanize.new
mechanize.redirect_ok = false
mechanize
end
end
def_delegators :mechanize, :basic_auth
def absolute_url(url) #:nodoc:
current_host, current_path = split_current_url
if url =~ Regexp.new('^https?://')
url
elsif url =~ Regexp.new('^/')
current_host + url
elsif url =~ Regexp.new('^\.')
current_host + absolute_path(current_path, url)
else
url
end
end
private
def split_current_url
current_url =~ Regexp.new('^(https?://[^/]+)(/.*)?')
[Regexp.last_match(1), Regexp.last_match(2)]
end
def absolute_path(current_path, url)
levels_up = url.split('/').find_all { |x| x == '..' }.size
ancestor = if current_path.nil?
""
else
current_path.split("/")[0..(-1 - levels_up)].join("/")
end
descendent = url.split("/")[levels_up..-1].join
"#{ancestor}/#{descendent}"
end
end
end

View File

@ -1,11 +0,0 @@
require "webrat/integrations/merb"
module Webrat
class MerbAdapter < RackAdapter #:nodoc:
def initialize(context=nil)
app = context.respond_to?(:app) ?
context.app : Merb::Rack::Application.new
super(Rack::Test::Session.new(Rack::MockSession.new(app, "www.example.com")))
end
end
end

View File

@ -1,29 +0,0 @@
require "rack/test"
module Webrat
class RackAdapter
extend Forwardable
def_delegators :@session, :get, :post, :put, :delete
def initialize(session) #:nodoc:
@session = session
end
def response_body
response.body
end
def response_code
response.status
end
def response_headers
response.headers
end
def response
@session.last_response
end
end
end

View File

@ -1,77 +0,0 @@
require "webrat/integrations/rails"
require "action_controller/record_identifier"
module Webrat
class RailsAdapter #:nodoc:
include ActionController::RecordIdentifier
attr_reader :integration_session
def initialize(session)
@integration_session = session
end
def get(url, data, headers = nil)
do_request(:get, url, data, headers)
end
def post(url, data, headers = nil)
do_request(:post, url, data, headers)
end
def put(url, data, headers = nil)
do_request(:put, url, data, headers)
end
def delete(url, data, headers = nil)
do_request(:delete, url, data, headers)
end
def response_body
response.body
end
def response_code
response.code.to_i
end
def response_headers
response.headers
end
def xml_content_type?
response.headers["Content-Type"].to_s =~ /xml/
end
protected
def do_request(http_method, url, data, headers) #:nodoc:
update_protocol(url)
integration_session.send(http_method, normalize_url(url), data, headers)
end
# remove protocol, host and anchor
def normalize_url(href) #:nodoc:
uri = URI.parse(href)
normalized_url = []
normalized_url << "#{uri.scheme}://" if uri.scheme
normalized_url << uri.host if uri.host
normalized_url << ":#{uri.port}" if uri.port && ![80,443].include?(uri.port)
normalized_url << uri.path if uri.path
normalized_url << "?#{uri.query}" if uri.query
normalized_url.join
end
def update_protocol(href) #:nodoc:
if href =~ /^https:/
integration_session.https!(true)
elsif href =~ /^http:/
integration_session.https!(false)
end
end
def response #:nodoc:
integration_session.response
end
end
end

View File

@ -1,9 +0,0 @@
module Webrat
class SinatraAdapter < RackAdapter
def initialize(context)
app = context.respond_to?(:app) ? context.app : Sinatra::Application
super(Rack::Test::Session.new(Rack::MockSession.new(app, "www.example.com")))
end
end
end

View File

@ -1,13 +1,12 @@
require "webrat/core/configuration" require "webrat/core/nokogiri"
require "webrat/core/xml"
require "webrat/core/logging" require "webrat/core/logging"
require "webrat/core/elements/form" require "webrat/core/flunk"
require "webrat/core/form"
require "webrat/core/scope" require "webrat/core/scope"
require "webrat/core/elements/link" require "webrat/core/link"
require "webrat/core/elements/area" require "webrat/core/area"
require "webrat/core/elements/label" require "webrat/core/label"
require "webrat/core/elements/select_option" require "webrat/core/select_option"
require "webrat/core/session" require "webrat/core/session"
require "webrat/core/methods" require "webrat/core/methods"
require "webrat/core/matchers" require "webrat/core/matchers"
require "webrat/core/save_and_open_page"

View File

@ -1,22 +1,35 @@
require "webrat/core/elements/element"
module Webrat module Webrat
class Area < Element #:nodoc: class Area #:nodoc:
def self.xpath_search def initialize(session, element)
[".//area"] @session = session
@element = element
end end
def click(method = nil, options = {}) def click(method = nil, options = {})
@session.request_page(absolute_href, :get, {}) @session.request_page(absolute_href, :get, {})
end end
protected def matches_text?(id_or_title)
matcher = /#{Regexp.escape(id_or_title.to_s)}/i
title =~ matcher || id =~ matcher
end
protected
def href def href
@element["href"] @element["href"]
end end
def title
@element["title"]
end
def id
@element["id"]
end
def absolute_href def absolute_href
if href =~ /^\?/ if href =~ /^\?/
"#{@session.current_url}#{href}" "#{@session.current_url}#{href}"
@ -26,6 +39,6 @@ module Webrat
href href
end end
end end
end end
end end

View File

@ -1,114 +0,0 @@
require "webrat/core_extensions/deprecate"
require "pathname"
module Webrat
# Configures Webrat. If this is not done, Webrat will be created
# with all of the default settings.
def self.configure(configuration = Webrat.configuration)
yield configuration if block_given?
@@configuration = configuration
end
def self.configuration # :nodoc:
@@configuration ||= Webrat::Configuration.new
end
# Webrat can be configured using the Webrat.configure method. For example:
#
# Webrat.configure do |config|
# config.mode = :sinatra
# end
class Configuration
# Webrat's mode, set automatically when requiring webrat/rails, webrat/merb, etc.
attr_reader :mode # :nodoc:
# Save and open pages with error status codes (500-599) in a browser? Defualts to true.
attr_writer :open_error_files
# Save and open page storage directory, defaults to "tmp" under current directory if exists, otherwise current directory
attr_accessor :saved_pages_dir
# Which rails environment should the selenium tests be run in? Defaults to test.
attr_accessor :application_environment
webrat_deprecate :selenium_environment, :application_environment
webrat_deprecate :selenium_environment=, :application_environment=
# Which port is the application running on for selenium testing? Defaults to 3001.
attr_accessor :application_port
webrat_deprecate :selenium_port, :application_port
webrat_deprecate :selenium_port=, :application_port=
# Which port should selenium use to access the application. Defaults to application_port
attr_writer :application_port_for_selenium
def application_port_for_selenium
@application_port_for_selenium || self.application_port
end
# Which underlying app framework we're testing with selenium
attr_accessor :application_framework
# Which server the application is running on for selenium testing? Defaults to localhost
attr_accessor :application_address
# Which server Selenium server is running on. Defaults to nil(server starts in webrat process and runs locally)
attr_accessor :selenium_server_address
# Which server Selenium port is running on. Defaults to 4444
attr_accessor :selenium_server_port
# Set the key that Selenium uses to determine the browser running. Default *firefox
attr_accessor :selenium_browser_key
# Set the timeout for waiting for the browser process to start
attr_accessor :selenium_browser_startup_timeout
# Set the firefox profile for selenium to use
attr_accessor :selenium_firefox_profile
# How many redirects to the same URL should be halted as an infinite redirect
# loop? Defaults to 10
attr_accessor :infinite_redirect_limit
# Print out the full HTML on wait failure
# Defaults to false
attr_accessor :selenium_verbose_output
def initialize # :nodoc:
self.open_error_files = true
self.application_environment = :test
self.application_port = 3001
self.application_address = 'localhost'
self.application_framework = :rails
self.selenium_server_port = 4444
self.infinite_redirect_limit = 10
self.selenium_browser_key = '*firefox'
self.selenium_browser_startup_timeout = 5
self.selenium_verbose_output = false
tmp_dir = Pathname.new(Dir.pwd).join("tmp")
self.saved_pages_dir = tmp_dir.exist? ? tmp_dir : Dir.pwd
end
def open_error_files? #:nodoc:
@open_error_files ? true : false
end
# Allows setting of webrat's mode, valid modes are:
# :rails, :selenium, :rack, :sinatra, :mechanize, :merb
def mode=(mode)
@mode = mode.to_sym
begin
require("webrat/integrations/#{mode}")
rescue LoadError
# Only some modes have integration code that needs to
# be loaded, so this is OK
end
end
end
end

View File

@ -1,33 +0,0 @@
module Webrat
class Element # :nodoc:
def self.load_all(session, dom)
dom.xpath(*xpath_search).map do |element|
load(session, element)
end
end
def self.load(session, element)
return nil if element.nil?
session.elements[element.path] ||= self.new(session, element)
end
attr_reader :element
def initialize(session, element)
@session = session
@element = element
end
def path
@element.path
end
def inspect
"#<#{self.class} @element=#{element.inspect}>"
end
end
end

View File

@ -1,438 +0,0 @@
require "cgi"
require "digest/md5"
require "webrat/core_extensions/blank"
require "webrat/core_extensions/nil_to_query_string"
require "webrat/core/elements/element"
module Webrat
# Raised when Webrat is asked to manipulate a disabled form field
class DisabledFieldError < WebratError
end
class Field < Element #:nodoc:
attr_reader :value
def self.xpath_search
".//button|.//input|.//textarea|.//select"
end
def self.xpath_search_excluding_hidden
[".//button", ".//input[ @type != 'hidden']", ".//textarea", ".//select"]
end
def self.field_classes
@field_classes || []
end
def self.inherited(klass)
@field_classes ||= []
@field_classes << klass
# raise args.inspect
end
def self.load(session, element)
return nil if element.nil?
session.elements[element.path] ||= field_class(element).new(session, element)
end
def self.field_class(element)
case element.name
when "button" then ButtonField
when "select"
if element.attributes["multiple"].nil?
SelectField
else
MultipleSelectField
end
when "textarea" then TextareaField
else
case element["type"]
when "checkbox" then CheckboxField
when "hidden" then HiddenField
when "radio" then RadioField
when "password" then PasswordField
when "file" then FileField
when "reset" then ResetField
when "submit" then ButtonField
when "button" then ButtonField
when "image" then ButtonField
else TextField
end
end
end
def initialize(*args)
super
@value = default_value
end
def label_text
return nil if labels.empty?
labels.first.text
end
def id
@element["id"]
end
def disabled?
@element.attributes.has_key?("disabled") && @element["disabled"] != 'false'
end
def raise_error_if_disabled
return unless disabled?
raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})")
end
def to_query_string
return nil if disabled?
query_string = case Webrat.configuration.mode
when :rails, :merb, :rack, :sinatra
build_query_string
when :mechanize
build_query_string(false)
end
query_string
end
def set(value)
@value = value
end
def unset
@value = default_value
end
protected
def form
Form.load(@session, form_element)
end
def form_element
parent = @element.parent
while parent.respond_to?(:parent)
return parent if parent.name == 'form'
parent = parent.parent
end
end
def name
@element["name"]
end
def build_query_string(escape_value=true)
if @value.is_a?(Array)
@value.collect {|value| "#{name}=#{ escape_value ? escape(value) : value }" }.join("&")
else
"#{name}=#{ escape_value ? escape(value) : value }"
end
end
def escape(value)
CGI.escape(value.to_s)
end
def escaped_value
CGI.escape(@value.to_s)
end
def labels
@labels ||= label_elements.map do |element|
Label.load(@session, element)
end
end
def label_elements
return @label_elements unless @label_elements.nil?
@label_elements = []
parent = @element.parent
while parent.respond_to?(:parent)
if parent.name == 'label'
@label_elements.push parent
break
end
parent = parent.parent
end
unless id.blank?
@label_elements += form.element.xpath(".//label[@for = '#{id}']")
end
@label_elements
end
def default_value
@element["value"]
end
end
class ButtonField < Field #:nodoc:
def self.xpath_search
[".//button", ".//input[@type = 'submit']", ".//input[@type = 'button']", ".//input[@type = 'image']"]
end
def to_query_string
return nil if @value.nil?
super
end
def default_value
nil
end
def click
raise_error_if_disabled
set(@element["value"]) unless @element["name"].blank?
form.submit
end
end
class HiddenField < Field #:nodoc:
def self.xpath_search
".//input[@type = 'hidden']"
end
def to_query_string
if collection_name?
super
else
checkbox_with_same_name = form.field_named(name, CheckboxField)
if checkbox_with_same_name.to_query_string.blank?
super
else
nil
end
end
end
protected
def collection_name?
name =~ /\[\]/
end
end
class CheckboxField < Field #:nodoc:
def self.xpath_search
".//input[@type = 'checkbox']"
end
def to_query_string
return nil if @value.nil?
super
end
def check
raise_error_if_disabled
set(@element["value"] || "on")
end
def checked?
@element["checked"] == "checked"
end
def uncheck
raise_error_if_disabled
set(nil)
end
protected
def default_value
if @element["checked"] == "checked"
@element["value"] || "on"
else
nil
end
end
end
class PasswordField < Field #:nodoc:
def self.xpath_search
".//input[@type = 'password']"
end
end
class RadioField < Field #:nodoc:
def self.xpath_search
".//input[@type = 'radio']"
end
def to_query_string
return nil if @value.nil?
super
end
def choose
raise_error_if_disabled
other_options.each do |option|
option.set(nil)
end
set(@element["value"] || "on")
end
def checked?
@element["checked"] == "checked"
end
protected
def other_options
form.fields.select { |f| f.name == name }
end
def default_value
if @element["checked"] == "checked"
@element["value"] || "on"
else
nil
end
end
end
class TextareaField < Field #:nodoc:
def self.xpath_search
".//textarea"
end
protected
def default_value
@element.inner_html
end
end
class FileField < Field #:nodoc:
def self.xpath_search
".//input[@type = 'file']"
end
attr_accessor :content_type
def set(value, content_type = nil)
@original_value = @value
@content_type ||= content_type
super(value)
end
def digest_value
@value ? Digest::MD5.hexdigest(self.object_id.to_s) : ""
end
def to_query_string
@value.nil? ? set("") : set(digest_value)
super
end
def test_uploaded_file
return "" if @original_value.blank?
case Webrat.configuration.mode
when :rails
if content_type
ActionController::TestUploadedFile.new(@original_value, content_type)
else
ActionController::TestUploadedFile.new(@original_value)
end
when :rack, :merb
Rack::Test::UploadedFile.new(@original_value, content_type)
end
end
end
class TextField < Field #:nodoc:
def self.xpath_search
[".//input[@type = 'text']", ".//input[not(@type)]"]
end
end
class ResetField < Field #:nodoc:
def self.xpath_search
[".//input[@type = 'reset']"]
end
end
class SelectField < Field #:nodoc:
def self.xpath_search
[".//select[not(@multiple)]"]
end
def options
@options ||= SelectOption.load_all(@session, @element)
end
def unset(value)
@value = nil
end
protected
def default_value
selected_options = @element.xpath(".//option[@selected = 'selected']")
selected_options = @element.xpath(".//option[position() = 1]") if selected_options.empty?
selected_options.map do |option|
return "" if option.nil?
option["value"] || option.inner_html
end.uniq.first
end
end
class MultipleSelectField < Field #:nodoc:
def self.xpath_search
[".//select[@multiple='multiple']"]
end
def options
@options ||= SelectOption.load_all(@session, @element)
end
def set(value)
@value << value
end
def unset(value)
@value.delete(value)
end
protected
# Overwrite SelectField definition because we don't want to select the first option
# (mutliples don't select the first option unlike their non multiple versions)
def default_value
selected_options = @element.xpath(".//option[@selected = 'selected']")
selected_options.map do |option|
return "" if option.nil?
option["value"] || option.inner_html
end.uniq
end
end
end

View File

@ -1,127 +0,0 @@
require "webrat/core/elements/field"
require "webrat/core_extensions/blank"
require "webrat/core/elements/element"
require "webrat/core/locators/field_named_locator"
module Webrat
class Form < Element #:nodoc:
attr_reader :element
def self.xpath_search
[".//form"]
end
def fields
@fields ||= Field.load_all(@session, @element)
end
def submit
@session.request_page(form_action, form_method, params)
end
def field_named(name, *field_types)
Webrat::Locators::FieldNamedLocator.new(@session, dom, name, *field_types).locate
end
protected
def dom
@session.dom.xpath(path).first
end
def fields_by_type(field_types)
if field_types.any?
fields.select { |f| field_types.include?(f.class) }
else
fields
end
end
# iterate over all form fields to build a request querystring to get params from it,
# for file_field we made a work around to pass a digest as value to later replace it
# in params hash with the real file.
def params
query_string = []
replaces = {}
fields.each do |field|
next if field.to_query_string.nil?
replaces.merge!({field.digest_value => field.test_uploaded_file}) if field.is_a?(FileField)
query_string << field.to_query_string
end
query_params = self.class.query_string_to_params(query_string.join('&'))
query_params = self.class.replace_params_values(query_params, replaces)
self.class.unescape_params(query_params)
end
def form_method
@element["method"].blank? ? :get : @element["method"].downcase
end
def form_action
@element["action"].blank? ? @session.current_url : @element["action"]
end
def self.replace_param_value(params, oval, nval)
output = Hash.new
params.each do |key, value|
case value
when Hash
value = replace_param_value(value, oval, nval)
when Array
value = value.map { |o| o == oval ? nval : ( o.is_a?(Hash) ? replace_param_value(o, oval, nval) : o) }
when oval
value = nval
end
output[key] = value
end
output
end
def self.replace_params_values(params, values)
values.each do |key, value|
params = replace_param_value(params, key, value)
end
params
end
def self.unescape_params(params)
case params.class.name
when 'Hash', 'Mash'
params.each { |key,value| params[key] = unescape_params(value) }
params
when 'Array'
params.collect { |value| unescape_params(value) }
else
params.is_a?(String) ? CGI.unescapeHTML(params) : params
end
end
def self.query_string_to_params(query_string)
case Webrat.configuration.mode
when :rails
parse_rails_request_params(query_string)
when :merb
::Merb::Parse.query(query_string)
when :rack, :sinatra
Rack::Utils.parse_nested_query(query_string)
else
Hash[query_string.split('&').map {|query| [ query.split('=').first, query.split('=').last ]}]
end
end
def self.parse_rails_request_params(query_string)
if defined?(ActionController::AbstractRequest)
ActionController::AbstractRequest.parse_query_parameters(query_string)
elsif defined?(ActionController::UrlEncodedPairParser)
ActionController::UrlEncodedPairParser.parse_query_parameters(query_string)
else
Rack::Utils.parse_nested_query(query_string)
end
end
end
end

View File

@ -1,31 +0,0 @@
require "webrat/core/elements/element"
module Webrat
class Label < Element #:nodoc:
attr_reader :element
def self.xpath_search
[".//label"]
end
def for_id
@element["for"]
end
def field
Field.load(@session, field_element)
end
protected
def field_element
if for_id.blank?
@element.xpath(*Field.xpath_search_excluding_hidden).first
else
@session.current_dom.css("#" + for_id).first
end
end
end
end

View File

@ -1,48 +0,0 @@
require "webrat/core/elements/element"
module Webrat
class SelectOption < Element #:nodoc:
def self.xpath_search
[".//option"]
end
def choose
select.raise_error_if_disabled
select.set(value)
end
def unchoose
select.raise_error_if_disabled
select.unset(value)
end
def inner_text
@element.inner_text
end
protected
def select
SelectField.load(@session, select_element)
end
def select_element
parent = @element.parent
while parent.respond_to?(:parent)
return parent if parent.name == 'select'
parent = parent.parent
end
end
def value
@element["value"] || @element.inner_html
end
def label
@element.inner_html
end
end
end

332
lib/webrat/core/field.rb Normal file
View File

@ -0,0 +1,332 @@
require "cgi"
require "webrat/core_extensions/blank"
require "webrat/core_extensions/nil_to_param"
module Webrat
class Field #:nodoc:
def self.class_for_element(element)
if element.name == "input"
if %w[submit image].include?(element["type"])
field_class = "button"
else
field_class = element["type"] || "text" #default type; 'type' attribute is not mandatory
end
else
field_class = element.name
end
Webrat.const_get("#{field_class.capitalize}Field")
rescue NameError
raise "Invalid field element: #{element.inspect}"
end
def initialize(form, element)
@form = form
@element = element
@value = default_value
end
def label_text
return nil if labels.empty?
labels.first.text
end
def matches_id?(id)
@element["id"] == id.to_s
end
def matches_name?(name)
@element["name"] == name.to_s
end
def matches_label?(label_text)
return false if labels.empty?
labels.any? { |label| label.matches_text?(label_text) }
end
def matches_alt?(alt)
@element["alt"] =~ /^\W*#{Regexp.escape(alt.to_s)}/i
end
def disabled?
@element.attributes.has_key?("disabled") && @element["disabled"] != 'false'
end
def raise_error_if_disabled
raise "Cannot interact with disabled form element (#{self})" if disabled?
end
def to_param
return nil if disabled?
key_and_value = "#{name}=#{escaped_value}"
if defined?(CGIMethods)
CGIMethods.parse_query_parameters(key_and_value)
elsif defined?(ActionController::AbstractRequest)
ActionController::AbstractRequest.parse_query_parameters(key_and_value)
else
::Merb::Parse.query(key_and_value)
end
end
def set(value)
@value = value
end
def unset
@value = default_value
end
protected
def id
@element["id"]
end
def name
@element["name"]
end
def escaped_value
CGI.escape(@value.to_s)
end
def labels
@labels ||= label_elements.map { |element| Label.new(self, element) }
end
def label_elements
return @label_elements unless @label_elements.nil?
@label_elements = []
parent = @element.parent
while parent.respond_to?(:parent)
if parent.name == 'label'
@label_elements.push parent
break
end
parent = parent.parent
end
unless id.blank?
@label_elements += @form.element.search("label[@for='#{id}']")
end
@label_elements
end
def default_value
@element["value"]
end
def replace_param_value(params, oval, nval)
output = Hash.new
params.each do |key, value|
case value
when Hash
value = replace_param_value(value, oval, nval)
when Array
value = value.map { |o| o == oval ? nval : oval }
when oval
value = nval
end
output[key] = value
end
output
end
end
class ButtonField < Field #:nodoc:
def matches_text?(text)
@element.inner_html =~ /#{Regexp.escape(text.to_s)}/i
end
def matches_value?(value)
@element["value"] =~ /^\W*#{Regexp.escape(value.to_s)}/i || matches_text?(value) || matches_alt?(value)
end
def to_param
return nil if @value.nil?
super
end
def default_value
nil
end
def click
raise_error_if_disabled
set(@element["value"]) unless @element["name"].blank?
@form.submit
end
end
class HiddenField < Field #:nodoc:
def to_param
if collection_name?
super
else
checkbox_with_same_name = @form.field(name, CheckboxField)
if checkbox_with_same_name.to_param.blank?
super
else
nil
end
end
end
protected
def collection_name?
name =~ /\[\]/
end
end
class CheckboxField < Field #:nodoc:
def to_param
return nil if @value.nil?
super
end
def check
raise_error_if_disabled
set(@element["value"] || "on")
end
def checked?
@element["checked"] == "checked"
end
def uncheck
raise_error_if_disabled
set(nil)
end
protected
def default_value
if @element["checked"] == "checked"
@element["value"] || "on"
else
nil
end
end
end
class PasswordField < Field #:nodoc:
end
class RadioField < Field #:nodoc:
def to_param
return nil if @value.nil?
super
end
def choose
raise_error_if_disabled
other_options.each do |option|
option.set(nil)
end
set(@element["value"] || "on")
end
protected
def other_options
@form.fields.select { |f| f.name == name }
end
def default_value
if @element["checked"] == "checked"
@element["value"] || "on"
else
nil
end
end
end
class TextareaField < Field #:nodoc:
protected
def default_value
@element.inner_html
end
end
class FileField < Field #:nodoc:
attr_accessor :content_type
def set(value, content_type = nil)
super(value)
@content_type = content_type
end
def to_param
if @value.nil?
super
else
replace_param_value(super, @value, test_uploaded_file)
end
end
protected
def test_uploaded_file
if content_type
ActionController::TestUploadedFile.new(@value, content_type)
else
ActionController::TestUploadedFile.new(@value)
end
end
end
class TextField < Field #:nodoc:
end
class ResetField < Field #:nodoc:
end
class SelectField < Field #:nodoc:
def find_option(text)
options.detect { |o| o.matches_text?(text) }
end
protected
def default_value
selected_options = @element.search(".//option[@selected='selected']")
selected_options = @element.search(".//option[position() = 1]") if selected_options.empty?
selected_options.map do |option|
return "" if option.nil?
option["value"] || option.inner_html
end
end
def options
option_elements.map { |oe| SelectOption.new(self, oe) }
end
def option_elements
@element.search(".//option")
end
end
end

7
lib/webrat/core/flunk.rb Normal file
View File

@ -0,0 +1,7 @@
module Flunk
def flunk(message) #:nodoc:
raise message
end
end

130
lib/webrat/core/form.rb Normal file
View File

@ -0,0 +1,130 @@
require "webrat/core/field"
require "webrat/core_extensions/blank"
module Webrat
class Form #:nodoc:
attr_reader :element
def initialize(session, element)
@session = session
@element = element
@fields = nil
end
def field(locator, *field_types)
field_with_id(locator, *field_types) ||
field_named(locator, *field_types) ||
field_labeled(locator, *field_types) ||
nil
end
def find_select_option(option_text)
select_fields = fields_by_type([SelectField])
select_fields.each do |select_field|
result = select_field.find_option(option_text)
return result if result
end
nil
end
def find_button(value = nil)
return fields_by_type([ButtonField]).first if value.nil?
possible_buttons = fields_by_type([ButtonField])
possible_buttons.detect { |possible_button| possible_button.matches_id?(value) } ||
possible_buttons.detect { |possible_button| possible_button.matches_value?(value) }
end
def fields
return @fields if @fields
@fields = (@element.search(".//button", ".//input", ".//textarea", ".//select")).collect do |field_element|
Field.class_for_element(field_element).new(self, field_element)
end
end
def submit
@session.request_page(form_action, form_method, params)
end
def field_with_id(id, *field_types)
possible_fields = fields_by_type(field_types)
possible_fields.detect { |possible_field| possible_field.matches_id?(id) }
end
def field_named(name, *field_types)
possible_fields = fields_by_type(field_types)
possible_fields.detect { |possible_field| possible_field.matches_name?(name) }
end
def field_labeled(label, *field_types)
possible_fields = fields_by_type(field_types)
matching_fields = possible_fields.select do |possible_field|
possible_field.matches_label?(label)
end
matching_fields.min { |a, b| a.label_text.length <=> b.label_text.length }
end
protected
def fields_by_type(field_types)
if field_types.any?
fields.select { |f| field_types.include?(f.class) }
else
fields
end
end
def params
all_params = {}
fields.each do |field|
next if field.to_param.nil?
merge(all_params, field.to_param)
end
all_params
end
def form_method
@element["method"].blank? ? :get : @element["method"].downcase
end
def form_action
@element["action"].blank? ? @session.current_url : @element["action"]
end
HASH = [Hash, HashWithIndifferentAccess] rescue [Hash]
def merge(all_params, new_param)
new_param.each do |key, value|
case all_params[key]
when *HASH
merge_hash_values(all_params[key], value)
when Array
all_params[key] += value
else
all_params[key] = value
end
end
end
def merge_hash_values(a, b) # :nodoc:
a.keys.each do |k|
if b.has_key?(k)
case [a[k], b[k]].map{|value| value.class}
when [Hash, Hash]
a[k] = merge_hash_values(a[k], b[k])
b.delete(k)
when [Array, Array]
a[k] += b[k]
b.delete(k)
end
end
end
a.merge!(b)
end
end
end

18
lib/webrat/core/label.rb Normal file
View File

@ -0,0 +1,18 @@
module Webrat
class Label #:nodoc:
def initialize(field, element)
@field = field
@element = element
end
def matches_text?(label_text)
text =~ /^\W*#{Regexp.escape(label_text.to_s)}\b/i
end
def text
@element.inner_text
end
end
end

View File

@ -1,40 +1,50 @@
require "English"
require "webrat/core_extensions/blank" require "webrat/core_extensions/blank"
require "webrat/core/elements/element"
module Webrat module Webrat
class Link < Element #:nodoc: class Link #:nodoc:
def self.xpath_search def initialize(session, element)
[".//a[@href]"] @session = session
@element = element
end end
def click(options = {}) def click(options = {})
method = options[:method] || http_method method = options[:method] || http_method
return if href =~ /^#/ && method == :get return if href =~ /^#/ && method == :get
options[:javascript] = true if options[:javascript].nil? options[:javascript] = true if options[:javascript].nil?
if options[:javascript] if options[:javascript]
@session.request_page(absolute_href, method, data) @session.request_page(absolute_href, method, data)
else else
@session.request_page(absolute_href, :get, {}) @session.request_page(absolute_href, :get, {})
end end
end end
protected def matches_text?(link_text)
html = text.gsub('&#xA0;',' ')
def id
@element["id"] if link_text.is_a?(Regexp)
matcher = link_text
else
matcher = /#{Regexp.escape(link_text.to_s)}/i
end
html =~ matcher || title =~ matcher
end end
def text
@element.inner_html
end
protected
def data def data
authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token} authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token}
end end
def title def title
@element["title"] @element['title']
end end
def href def href
@ -44,23 +54,23 @@ module Webrat
def absolute_href def absolute_href
if href =~ /^\?/ if href =~ /^\?/
"#{@session.current_url}#{href}" "#{@session.current_url}#{href}"
elsif href !~ %r{^https?://} && (href !~ /^\//) elsif href !~ %r{^https?://www.example.com(/.*)} && (href !~ /^\//)
"#{@session.current_url}/#{href}" "#{@session.current_url}/#{href}"
else else
href href
end end
end end
def authenticity_token def authenticity_token
return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") && return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") &&
( onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/ || onclick =~ /s\.setAttribute\('value', '(.{44})'\);/ ) onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/
$LAST_MATCH_INFO.captures.first $LAST_MATCH_INFO.captures.first
end end
def onclick def onclick
@element["onclick"] @element["onclick"]
end end
def http_method def http_method
if !onclick.blank? && onclick.include?("f.submit()") if !onclick.blank? && onclick.include?("f.submit()")
http_method_from_js_form http_method_from_js_form
@ -82,10 +92,8 @@ module Webrat
:delete :delete
elsif onclick.include?("m.setAttribute('value', 'put')") elsif onclick.include?("m.setAttribute('value', 'put')")
:put :put
elsif onclick.include?("m.setAttribute('value', 'post')")
:post
else else
raise Webrat::WebratError.new("No HTTP method for _method param in #{onclick.inspect}") raise "No HTTP method for _method param in #{onclick.inspect}"
end end
end end

View File

@ -1,20 +1,92 @@
require "webrat/core/locators/area_locator" require "webrat/core_extensions/detect_mapped"
require "webrat/core/locators/button_locator"
require "webrat/core/locators/field_labeled_locator"
require "webrat/core/locators/label_locator"
require "webrat/core/locators/field_named_locator"
require "webrat/core/locators/field_by_id_locator"
require "webrat/core/locators/select_option_locator"
require "webrat/core/locators/link_locator"
require "webrat/core/locators/field_locator"
require "webrat/core/locators/form_locator"
module Webrat module Webrat
module Locators module Locators
def field_by_xpath(xpath) def field(*args)
Field.load(@session, dom.xpath(xpath).first) # This is the default locator strategy
find_field_with_id(*args) ||
find_field_named(*args) ||
field_labeled(*args) ||
flunk("Could not find field: #{args.inspect}")
end
def field_labeled(label, *field_types)
find_field_labeled(label, *field_types) ||
flunk("Could not find field labeled #{label.inspect}")
end end
def field_named(name, *field_types)
find_field_named(name, *field_types) ||
flunk("Could not find field named #{name.inspect}")
end
def field_with_id(id, *field_types)
find_field_with_id(id, *field_types) ||
flunk("Could not find field with id #{id.inspect}")
end
def find_field_labeled(label, *field_types) #:nodoc:
forms.detect_mapped do |form|
form.field_labeled(label, *field_types)
end
end
def find_field_named(name, *field_types) #:nodoc:
forms.detect_mapped do |form|
form.field_named(name, *field_types)
end
end
def find_field_with_id(id, *field_types) #:nodoc:
forms.detect_mapped do |form|
form.field_with_id(id, *field_types)
end
end
def find_select_option(option_text, id_or_name_or_label) #:nodoc:
if id_or_name_or_label
field = field(id_or_name_or_label, SelectField)
return field.find_option(option_text)
else
select_option = forms.detect_mapped do |form|
form.find_select_option(option_text)
end
return select_option if select_option
end
flunk("Could not find option #{option_text.inspect}")
end
def find_button(value) #:nodoc:
button = forms.detect_mapped do |form|
form.find_button(value)
end
if button
return button
else
flunk("Could not find button #{value.inspect}")
end
end
def find_area(area_name) #:nodoc:
areas.detect { |area| area.matches_text?(area_name) } ||
flunk("Could not find area with name #{area_name}")
end
def find_link(text) #:nodoc:
matching_links = links.select do |possible_link|
possible_link.matches_text?(text)
end
if matching_links.any?
matching_links.min { |a, b| a.text.length <=> b.text.length }
else
flunk("Could not find link with text #{text.inspect}")
end
end
end end
end end

View File

@ -1,38 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class AreaLocator < Locator # :nodoc:
def locate
Area.load(@session, area_element)
end
def area_element
area_elements.detect do |area_element|
area_element["title"] =~ matcher ||
area_element["id"] =~ matcher
end
end
def matcher
/#{Regexp.escape(@value.to_s)}/i
end
def area_elements
@dom.xpath(*Area.xpath_search)
end
def error_message
"Could not find area with name #{@value}"
end
end
def find_area(id_or_title) #:nodoc:
AreaLocator.new(@session, dom, id_or_title).locate!
end
end
end

View File

@ -1,54 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class ButtonLocator < Locator # :nodoc:
def locate
ButtonField.load(@session, button_element)
end
def button_element
button_elements.detect do |element|
@value.nil? ||
matches_id?(element) ||
matches_value?(element) ||
matches_html?(element) ||
matches_alt?(element)
end
end
def matches_id?(element)
(@value.is_a?(Regexp) && element["id"] =~ @value) ||
(!@value.is_a?(Regexp) && element["id"] == @value.to_s)
end
def matches_value?(element)
element["value"] =~ /^\W*#{Regexp.escape(@value.to_s)}/i
end
def matches_html?(element)
element.inner_html =~ /#{Regexp.escape(@value.to_s)}/i
end
def matches_alt?(element)
element["alt"] =~ /^\W*#{Regexp.escape(@value.to_s)}/i
end
def button_elements
@dom.xpath(*ButtonField.xpath_search)
end
def error_message
"Could not find button #{@value.inspect}"
end
end
def find_button(value) #:nodoc:
ButtonLocator.new(@session, dom, value).locate!
end
end
end

View File

@ -1,37 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class FieldByIdLocator < Locator # :nodoc:
def locate
Field.load(@session, field_element)
end
def field_element
field_elements.detect do |field_element|
if @value.is_a?(Regexp)
field_element["id"] =~ @value
else
field_element["id"] == @value.to_s
end
end
end
def field_elements
@dom.xpath(*Field.xpath_search)
end
def error_message
"Could not find field with id #{@value.inspect}"
end
end
def field_with_id(id, *field_types)
FieldByIdLocator.new(@session, dom, id, *field_types).locate!
end
end
end

View File

@ -1,56 +0,0 @@
require "webrat/core_extensions/detect_mapped"
require "webrat/core/locators/locator"
module Webrat
module Locators
class FieldLabeledLocator < Locator # :nodoc:
def locate
matching_labels.any? && matching_labels.detect_mapped { |label| label.field }
end
def matching_labels
matching_label_elements.sort_by do |label_element|
text(label_element).length
end.map do |label_element|
Label.load(@session, label_element)
end
end
def matching_label_elements
label_elements.select do |label_element|
text(label_element) =~ /^\W*#{Regexp.escape(@value.to_s)}(\b|\Z)/i
end
end
def label_elements
@dom.xpath(*Label.xpath_search)
end
def error_message
"Could not find field labeled #{@value.inspect}"
end
def text(element)
str = element.inner_text
str.gsub!("\n","")
str.strip!
str.squeeze!(" ")
str
end
end
# Locates a form field based on a <tt>label</tt> element in the HTML source.
# This can be useful in order to verify that a field is pre-filled with the
# correct value.
#
# Example:
# field_labeled("First name").value.should == "Bryan"
def field_labeled(label, *field_types)
FieldLabeledLocator.new(@session, dom, label, *field_types).locate!
end
end
end

View File

@ -1,25 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class FieldLocator < Locator # :nodoc:
def locate
FieldByIdLocator.new(@session, @dom, @value).locate ||
FieldNamedLocator.new(@session, @dom, @value, *@field_types).locate ||
FieldLabeledLocator.new(@session, @dom, @value, *@field_types).locate
end
def error_message
"Could not find field: #{@value.inspect}"
end
end
def field(*args) # :nodoc:
FieldLocator.new(@session, dom, *args).locate!
end
end
end

View File

@ -1,41 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class FieldNamedLocator < Locator # :nodoc:
def locate
Field.load(@session, field_element)
end
def field_element
field_elements.detect do |field_element|
field_element["name"] == @value.to_s
end
end
def field_elements
@dom.xpath(*xpath_searches)
end
def xpath_searches
if @field_types.any?
@field_types.map { |field_type| field_type.xpath_search }.flatten
else
Field.xpath_search
end
end
def error_message
"Could not find field named #{@value.inspect}"
end
end
def field_named(name, *field_types)
FieldNamedLocator.new(@session, dom, name, *field_types).locate!
end
end
end

View File

@ -1,19 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class FormLocator < Locator # :nodoc:
def locate
Form.load(@session, form_element)
end
def form_element
@dom.css("#" + @value).first || @dom.css(@value).first
end
end
end
end

View File

@ -1,34 +0,0 @@
require "webrat/core_extensions/detect_mapped"
require "webrat/core/locators/locator"
module Webrat
module Locators
class LabelLocator < Locator # :nodoc:
def locate
Label.load(@session, label_element)
end
def label_element
label_elements.detect do |label_element|
text(label_element) =~ /^\W*#{Regexp.escape(@value.to_s)}(\b|\Z)/i
end
end
def label_elements
@dom.xpath(*Label.xpath_search)
end
def text(label_element)
str = label_element.inner_text
str.gsub!("\n","")
str.strip!
str.squeeze!(" ")
str
end
end
end
end

View File

@ -1,74 +0,0 @@
require "webrat/core/locators/locator"
module Webrat
module Locators
class LinkLocator < Locator # :nodoc:
def locate
Link.load(@session, link_element)
end
def link_element
matching_links.min { |a, b| a.inner_text.length <=> b.inner_text.length }
end
def matching_links
@matching_links ||= link_elements.select do |link_element|
matches_text?(link_element) ||
matches_id?(link_element)
end
end
def matches_text?(link)
if @value.is_a?(Regexp)
matcher = @value
else
matcher = /#{Regexp.escape(@value.to_s)}/i
end
replace_nbsp(link.inner_text) =~ matcher ||
replace_nbsp_ref(link.inner_html) =~ matcher ||
link["title"] =~ matcher
end
def matches_id?(link)
if @value.is_a?(Regexp)
link["id"] =~ @value ? true : false
else
link["id"] == @value ? true : false
end
end
def link_elements
@dom.xpath(*Link.xpath_search)
end
def replace_nbsp(str)
if str.respond_to?(:valid_encoding?)
if str.valid_encoding?
str.gsub(/\xc2\xa0/u, ' ')
else
str.force_encoding('UTF-8').gsub(/\xc2\xa0/u, ' ')
end
else
str.gsub(/\xc2\xa0/u, ' ')
end
end
def replace_nbsp_ref(str)
str.gsub('&#xA0;',' ').gsub('&nbsp;', ' ')
end
def error_message
"Could not find link with text or title or id #{@value.inspect}"
end
end
def find_link(text_or_title_or_id) #:nodoc:
LinkLocator.new(@session, dom, text_or_title_or_id).locate!
end
end
end

View File

@ -1,20 +0,0 @@
module Webrat
module Locators
class Locator # :nodoc:
def initialize(session, dom, value, *field_types)
@session = session
@dom = dom
@value = value
@field_types = field_types
end
def locate!
locate || raise(NotFoundError.new(error_message))
end
end
end
end

View File

@ -1,59 +0,0 @@
require "webrat/core_extensions/detect_mapped"
require "webrat/core/locators/locator"
module Webrat
module Locators
class SelectOptionLocator < Locator # :nodoc:
def initialize(session, dom, option_text, id_or_name_or_label)
@session = session
@dom = dom
@option_text = option_text
@id_or_name_or_label = id_or_name_or_label
end
def locate
if @id_or_name_or_label
field = FieldLocator.new(@session, @dom, @id_or_name_or_label, SelectField).locate!
field.options.detect do |o|
if @option_text.is_a?(Regexp)
o.element.inner_text =~ @option_text
else
o.inner_text == @option_text.to_s
end
end
else
option_element = option_elements.detect do |o|
if @option_text.is_a?(Regexp)
o.inner_text =~ @option_text
else
o.inner_text == @option_text.to_s
end
end
SelectOption.load(@session, option_element)
end
end
def option_elements
@dom.xpath(*SelectOption.xpath_search)
end
def error_message
if @id_or_name_or_label
"The '#{@option_text}' option was not found in the #{@id_or_name_or_label.inspect} select box"
else
"Could not find option #{@option_text.inspect}"
end
end
end
def select_option(option_text, id_or_name_or_label = nil) #:nodoc:
SelectOptionLocator.new(@session, dom, option_text, id_or_name_or_label).locate!
end
end
end

View File

@ -1,23 +1,25 @@
require "logger"
module Webrat module Webrat
module Logging #:nodoc: module Logging #:nodoc:
def warn_log(message) # :nodoc:
return unless logger
logger.warn message
end
def debug_log(message) # :nodoc: def debug_log(message) # :nodoc:
return unless logger return unless logger
logger.debug message logger.debug message
end end
def logger # :nodoc: def logger # :nodoc:
case Webrat.configuration.mode if defined? RAILS_DEFAULT_LOGGER
when :rails RAILS_DEFAULT_LOGGER
defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : nil elsif defined? Merb
when :merb Merb.logger
::Merb.logger
else else
@logger ||= ::Logger.new("webrat.log") nil
end end
end end
end end
end end

View File

@ -1,39 +1,76 @@
module Webrat module Webrat
module Matchers module Matchers
class HasContent #:nodoc: class HasContent #:nodoc:
def initialize(content) def initialize(content)
@content = content @content = content
end end
def matches?(stringlike) def matches?(stringlike)
@document = Webrat::XML.document(stringlike) if defined?(Nokogiri::XML)
matches_nokogiri?(stringlike)
else
matches_rexml?(stringlike)
end
end
def matches_rexml?(stringlike)
@document = rexml_document(stringlike)
@element = @document.inner_text @element = @document.inner_text
case @content case @content
when String when String
@element.gsub(/\s+/, ' ').include?(@content) @element.include?(@content)
when Regexp when Regexp
@element.match(@content) @element.match(@content)
end end
end end
def matches_nokogiri?(stringlike)
@document = Webrat.nokogiri_document(stringlike)
@element = @document.inner_text
case @content
when String
@element.include?(@content)
when Regexp
@element.match(@content)
end
end
def rexml_document(stringlike)
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
case stringlike
when REXML::Document
stringlike.root
when REXML::Node
stringlike
when StringIO, String
begin
REXML::Document.new(stringlike.to_s).root
rescue REXML::ParseException => e
if e.message.include?("second root element")
REXML::Document.new("<fake-root-element>#{stringlike}</fake-root-element>").root
else
raise e
end
end
end
end
# ==== Returns # ==== Returns
# String:: The failure message. # String:: The failure message.
def failure_message def failure_message
"expected the following element's content to #{content_message}:\n#{squeeze_space(@element)}" "expected the following element's content to #{content_message}:\n#{@element}"
end end
# ==== Returns # ==== Returns
# String:: The failure message to be displayed in negative matches. # String:: The failure message to be displayed in negative matches.
def negative_failure_message def negative_failure_message
"expected the following element's content to not #{content_message}:\n#{squeeze_space(@element)}" "expected the following element's content to not #{content_message}:\n#{@element}"
end end
def squeeze_space(inner_text)
inner_text.gsub(/^\s*$/, "").squeeze("\n")
end
def content_message def content_message
case @content case @content
when String when String
@ -43,26 +80,15 @@ module Webrat
end end
end end
end end
# Matches the contents of an HTML document with # Matches the contents of an HTML document with
# whatever string is supplied # whatever string is supplied
#
# ---
# @api public
def contain(content) def contain(content)
HasContent.new(content) HasContent.new(content)
end end
# Asserts that the body of the response contain
# the supplied string or regexp
def assert_contain(content)
hc = HasContent.new(content)
assert hc.matches?(response_body), hc.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_not_contain(content)
hc = HasContent.new(content)
assert !hc.matches?(response_body), hc.negative_failure_message
end
end end
end end

View File

@ -1,48 +1,26 @@
require "webrat/core/matchers/have_xpath"
module Webrat module Webrat
module Matchers module Matchers
class HaveSelector < HaveXpath #:nodoc: class HaveSelector < HaveXpath #:nodoc:
# ==== Returns # ==== Returns
# String:: The failure message. # String:: The failure message.
def failure_message def failure_message
"expected following output to contain a #{tag_inspect} tag:\n#{@document}" "expected following text to match selector #{@expected}:\n#{@document}"
end end
# ==== Returns # ==== Returns
# String:: The failure message to be displayed in negative matches. # String:: The failure message to be displayed in negative matches.
def negative_failure_message def negative_failure_message
"expected following output to omit a #{tag_inspect}:\n#{@document}" "expected following text to not match selector #{@expected}:\n#{@document}"
end end
def tag_inspect
options = @options.dup
count = options.delete(:count)
content = options.delete(:content)
html = "<#{@expected}"
options.each do |k,v|
html << " #{k}='#{v}'"
end
if content
html << ">#{content}</#{@expected}>"
else
html << "/>"
end
html
end
def query def query
Nokogiri::CSS.parse(@expected.to_s).map do |ast| Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath }
ast.to_xpath
end.first
end end
end end
# Matches HTML content against a CSS 3 selector. # Matches HTML content against a CSS 3 selector.
# #
# ==== Parameters # ==== Parameters
@ -50,25 +28,12 @@ module Webrat
# #
# ==== Returns # ==== Returns
# HaveSelector:: A new have selector matcher. # HaveSelector:: A new have selector matcher.
def have_selector(name, attributes = {}, &block) # ---
HaveSelector.new(name, attributes, &block) # @api public
def have_selector(expected, &block)
HaveSelector.new(expected, &block)
end end
alias_method :match_selector, :have_selector alias_method :match_selector, :have_selector
# Asserts that the body of the response contains
# the supplied selector
def assert_have_selector(name, attributes = {}, &block)
matcher = HaveSelector.new(name, attributes, &block)
assert matcher.matches?(response_body), matcher.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_have_no_selector(name, attributes = {}, &block)
matcher = HaveSelector.new(name, attributes, &block)
assert !matcher.matches?(response_body), matcher.negative_failure_message
end
end end
end end

View File

@ -1,21 +1,58 @@
require "webrat/core/matchers/have_selector"
module Webrat module Webrat
module HaveTagMatcher module HaveTagMatcher
def have_tag(*args, &block) class HaveTag < ::Webrat::Matchers::HaveSelector #:nodoc:
have_selector(*args, &block)
# ==== Returns
# String:: The failure message.
def failure_message
"expected following output to contain a #{tag_inspect} tag:\n#{@document}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following output to omit a #{tag_inspect}:\n#{@document}"
end
def tag_inspect
options = @expected.last.dup
content = options.delete(:content)
html = "<#{@expected.first}"
options.each do |k,v|
html << " #{k}='#{v}'"
end
if content
html << ">#{content}</#{@expected.first}>"
else
html << "/>"
end
html
end
def query
options = @expected.last.dup
selector = @expected.first.to_s
selector << ":contains('#{options.delete(:content)}')" if options[:content]
options.each do |key, value|
selector << "[#{key}='#{value}']"
end
Nokogiri::CSS::Parser.parse(selector).map { |ast| ast.to_xpath }
end
end end
def have_tag(name, attributes = {}, &block)
HaveTag.new([name, attributes], &block)
end
alias_method :match_tag, :have_tag alias_method :match_tag, :have_tag
def assert_have_tag(*args, &block)
assert_have_selector(*args, &block)
end
def assert_have_no_tag(*args, &block)
assert_have_no_selector(*args, &block)
end
end end
end end

View File

@ -1,73 +1,74 @@
require "webrat/core/xml"
module Webrat module Webrat
module Matchers module Matchers
class HaveXpath #:nodoc: class HaveXpath #:nodoc:
def initialize(expected, options = {}, &block) def initialize(expected, &block)
@expected = expected @expected = expected
@options = options
@block = block @block = block
end end
def matches?(stringlike, &block) def matches?(stringlike)
@block ||= block if defined?(Nokogiri::XML)
matched = matches(stringlike) matches_nokogiri?(stringlike)
@block.call(matched) if @block
if @options[:count]
matched.size == @options[:count].to_i
else else
matched.any? matches_rexml?(stringlike)
end end
end end
def matches_rexml?(stringlike)
@query = query
def matches(stringlike) @document = rexml_document(stringlike)
nokogiri_matches(stringlike)
matched = @query.map do |q|
if @document.is_a?(Array)
@document.map { |d| REXML::XPath.match(d, q) }
else
REXML::XPath.match(@document, q)
end
end.flatten.compact
matched.any? && (!@block || @block.call(matched))
end end
def nokogiri_matches(stringlike) def matches_nokogiri?(stringlike)
if Nokogiri::XML::NodeSet === stringlike if Nokogiri::XML::NodeSet === stringlike
@query = query.gsub(%r'^//', './/') @query = query.map { |q| q.gsub(%r'//', './') }
else else
@query = query @query = query
end end
add_options_conditions_to(@query) @document = Webrat.nokogiri_document(stringlike)
matched = @document.xpath(*@query)
@document = Webrat::XML.document(stringlike) matched.any? && (!@block || @block.call(matched))
@document.xpath(*@query)
end end
def add_options_conditions_to(query) def rexml_document(stringlike)
add_attributes_conditions_to(query) stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
add_content_condition_to(query)
end case stringlike
when REXML::Document
def add_attributes_conditions_to(query) stringlike.root
attribute_conditions = [] when REXML::Node, Array
@query = query.map { |q| q.gsub(%r'//', './') }
@options.each do |key, value| stringlike
next if [:content, :count].include?(key) else
attribute_conditions << "@#{key} = #{xpath_escape(value)}" begin
end REXML::Document.new(stringlike.to_s).root
rescue REXML::ParseException => e
if attribute_conditions.any? if e.message.include?("second root element")
query << "[#{attribute_conditions.join(' and ')}]" REXML::Document.new("<fake-root-element>#{stringlike}</fake-root-element>").root
else
raise e
end
end
end end
end end
def add_content_condition_to(query)
if @options[:content]
query << "[contains(., #{xpath_escape(@options[:content])})]"
end
end
def query def query
@expected [@expected].flatten.compact
end end
# ==== Returns # ==== Returns
# String:: The failure message. # String:: The failure message.
def failure_message def failure_message
@ -78,26 +79,9 @@ module Webrat
# String:: The failure message to be displayed in negative matches. # String:: The failure message to be displayed in negative matches.
def negative_failure_message def negative_failure_message
"expected following text to not match xpath #{@expected}:\n#{@document}" "expected following text to not match xpath #{@expected}:\n#{@document}"
end end
protected
def xpath_escape(string)
if string.include?("'") && string.include?('"')
parts = string.split("'").map do |part|
"'#{part}'"
end
"concat(" + parts.join(", \"'\", ") + ")"
elsif string.include?("'")
"\"#{string}\""
else
"'#{string}'"
end
end
end end
# Matches HTML content against an XPath query # Matches HTML content against an XPath query
# #
# ==== Parameters # ==== Parameters
@ -105,20 +89,12 @@ module Webrat
# #
# ==== Returns # ==== Returns
# HaveXpath:: A new have xpath matcher. # HaveXpath:: A new have xpath matcher.
def have_xpath(expected, options = {}, &block) # ---
HaveXpath.new(expected, options, &block) # @api public
def have_xpath(expected, &block)
HaveXpath.new(expected, &block)
end end
alias_method :match_xpath, :have_xpath alias_method :match_xpath, :have_xpath
def assert_have_xpath(expected, options = {}, &block)
hs = HaveXpath.new(expected, options, &block)
assert hs.matches?(response_body), hs.failure_message
end
def assert_have_no_xpath(expected, options = {}, &block)
hs = HaveXpath.new(expected, options, &block)
assert !hs.matches?(response_body), hs.negative_failure_message
end
end end
end end

View File

@ -3,26 +3,15 @@ module Webrat
def self.delegate_to_session(*meths) def self.delegate_to_session(*meths)
meths.each do |meth| meths.each do |meth|
self.class_eval(<<-RUBY, __FILE__, __LINE__) self.class_eval <<-RUBY
def #{meth}(*args, &blk) def #{meth}(*args, &blk)
webrat_session.#{meth}(*args, &blk) @_webrat_session ||= ::Webrat::MerbSession.new
@_webrat_session.#{meth}(*args, &blk)
end end
RUBY RUBY
end end
end end
def webrat
webrat_session
end
def webrat_session
@_webrat_session ||= begin
session = Webrat.session_class.new
session.adapter = Webrat.adapter_class.new(self) if session.respond_to?(:adapter=)
session
end
end
# all of these methods delegate to the @session, which should # all of these methods delegate to the @session, which should
# be created transparently. # be created transparently.
# #
@ -34,31 +23,22 @@ module Webrat
:within, :within,
:header, :http_accept, :basic_auth, :header, :http_accept, :basic_auth,
:save_and_open_page, :save_and_open_page,
:fills_in, :fill_in, :fill_in,
:checks, :check, :check,
:unchecks, :uncheck, :uncheck,
:chooses, :choose, :choose,
:selects, :select, :select,
:unselects, :unselect, :attach_file,
:attaches_file, :attach_file, :cookies,
:response,
:current_page, :current_page,
:current_url, :current_url,
:clicks_link, :click_link, :click_link,
:clicks_area, :click_area, :click_area,
:clicks_button, :click_button, :click_button,
:reload, :reloads, :reload, :reloads,
:clicks_link_within, :click_link_within, :clicks_link_within,
:field_labeled, :field_labeled
:select_option,
:set_hidden_field, :submit_form,
:request_page, :current_dom,
:response_body,
:selects_date, :selects_time, :selects_datetime,
:select_date, :select_time, :select_datetime,
:field_by_xpath,
:field_with_id,
:selenium,
:simulate, :automate,
:field_named
end end
end end

View File

@ -1,18 +1,29 @@
module Webrat #:nodoc: module Webrat
module MIME #:nodoc: module MIME
MIME_TYPES = Rack::Mime::MIME_TYPES.dup.merge(
".multipart_form" => "multipart/form-data", def self.mime_type(string_or_symbol)
".url_encoded_form" => "application/x-www-form-urlencoded" if string_or_symbol.is_a?(String)
).freeze string_or_symbol
else
def mime_type(type) case string_or_symbol
return type if type.nil? || type.to_s.include?("/") when :text then "text/plain"
type = ".#{type}" unless type.to_s[0] == ?. when :html then "text/html"
MIME_TYPES.fetch(type) { |invalid_type| when :js then "text/javascript"
raise ArgumentError.new("Invalid Mime type: #{invalid_type}") when :css then "text/css"
} when :ics then "text/calendar"
when :csv then "text/csv"
when :xml then "application/xml"
when :rss then "application/rss+xml"
when :atom then "application/atom+xml"
when :yaml then "application/x-yaml"
when :multipart_form then "multipart/form-data"
when :url_encoded_form then "application/x-www-form-urlencoded"
when :json then "application/json"
else
raise ArgumentError.new("Invalid Mime type: #{string_or_symbol.inspect}")
end
end
end end
module_function :mime_type
end end
end end

View File

@ -0,0 +1,44 @@
require "webrat/core_extensions/meta_class"
module Webrat
def self.nokogiri_document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif StringIO === stringlike
Nokogiri::HTML(stringlike.string)
elsif stringlike.respond_to?(:body)
Nokogiri::HTML(stringlike.body.to_s)
else
Nokogiri::HTML(stringlike.to_s)
end
end
def self.define_dom_method(object, dom) #:nodoc:
object.meta_class.send(:define_method, :dom) do
dom
end
end
end
module Nokogiri
module CSS
class XPathVisitor
def visit_pseudo_class_text(node) #:nodoc:
"@type='text'"
end
def visit_pseudo_class_password(node) #:nodoc:
"@type='password'"
end
end
end
end

View File

@ -1,28 +0,0 @@
module Webrat
module SaveAndOpenPage
# Saves the page out to RAILS_ROOT/tmp/ and opens it in the default
# web browser if on OS X. Useful for debugging.
#
# Example:
# save_and_open_page
def save_and_open_page
return unless File.exist?(Webrat.configuration.saved_pages_dir)
filename = "#{Webrat.configuration.saved_pages_dir}/webrat-#{Time.now.to_i}.html"
File.open(filename, "w") do |f|
f.write response_body
end
open_in_browser(filename)
end
def open_in_browser(path) # :nodoc
require "launchy"
Launchy::Browser.run(path)
rescue LoadError
warn "Sorry, you need to install launchy to open pages: `gem install launchy`"
end
end
end

View File

@ -1,42 +1,32 @@
require "webrat/core/elements/form" require "nokogiri"
require "webrat/core/form"
require "webrat/core/locators" require "webrat/core/locators"
require "webrat/core_extensions/deprecate"
module Webrat module Webrat
# An HTML element (link, button, field, etc.) that Webrat expected was not found on the page
class NotFoundError < WebratError
end
class Scope class Scope
include Logging include Logging
include Flunk
include Locators include Locators
def self.from_page(session, response, response_body) #:nodoc: def self.from_page(session, response, response_body) #:nodoc:
new(session) do new(session) do
@response = response @response = response
@response_body = response_body @response_body = response_body
end end
end end
def self.from_scope(session, scope, selector) #:nodoc: def self.from_scope(session, scope, selector) #:nodoc:
new(session) do new(session) do
@scope = scope @scope = scope
@selector = selector @selector = selector
end end
end end
attr_reader :session
def initialize(session, &block) #:nodoc: def initialize(session, &block) #:nodoc:
@selector, @dom = nil
@session = session @session = session
instance_eval(&block) if block_given? instance_eval(&block) if block_given?
if @selector && scoped_dom.nil?
raise Webrat::NotFoundError.new("The scope was not found on the page: #{@selector.inspect}")
end
end end
# Verifies an input field or textarea exists on the current page, and stores a value for # Verifies an input field or textarea exists on the current page, and stores a value for
# it which will be sent when the form is submitted. # it which will be sent when the form is submitted.
# #
@ -53,18 +43,8 @@ module Webrat
field.set(options[:with]) field.set(options[:with])
end end
webrat_deprecate :fills_in, :fill_in alias_method :fills_in, :fill_in
# Verifies that a hidden field exists on the current page and sets
# the value to that given by the <tt>:to</tt> option.
#
# Example:
# set_hidden_field 'user_id', :to => 1
def set_hidden_field(field_locator, options = {})
field = locate_field(field_locator, HiddenField)
field.set(options[:to])
end
# Verifies that an input checkbox exists on the current page and marks it # Verifies that an input checkbox exists on the current page and marks it
# as checked, so that the value will be submitted with the form. # as checked, so that the value will be submitted with the form.
# #
@ -74,8 +54,8 @@ module Webrat
locate_field(field_locator, CheckboxField).check locate_field(field_locator, CheckboxField).check
end end
webrat_deprecate :checks, :check alias_method :checks, :check
# Verifies that an input checkbox exists on the current page and marks it # Verifies that an input checkbox exists on the current page and marks it
# as unchecked, so that the value will not be submitted with the form. # as unchecked, so that the value will not be submitted with the form.
# #
@ -85,8 +65,8 @@ module Webrat
locate_field(field_locator, CheckboxField).uncheck locate_field(field_locator, CheckboxField).uncheck
end end
webrat_deprecate :unchecks, :uncheck alias_method :unchecks, :uncheck
# Verifies that an input radio button exists on the current page and marks it # Verifies that an input radio button exists on the current page and marks it
# as checked, so that the value will be submitted with the form. # as checked, so that the value will be submitted with the form.
# #
@ -96,162 +76,46 @@ module Webrat
locate_field(field_locator, RadioField).choose locate_field(field_locator, RadioField).choose
end end
webrat_deprecate :chooses, :choose alias_method :chooses, :choose
# Verifies that a an option element exists on the current page with the specified # Verifies that a an option element exists on the current page with the specified
# text. You can optionally restrict the search to a specific select list by # text. You can optionally restrict the search to a specific select list by
# assigning <tt>options[:from]</tt> the value of the select list's name or # assigning <tt>options[:from]</tt> the value of the select list's name or
# a label. Stores the option's value to be sent when the form is submitted. # a label. Stores the option's value to be sent when the form is submitted.
# #
# Examples: # Examples:
# select "January" # selects "January"
# select "February", :from => "event_month" # selects "February", :from => "event_month"
# select "February", :from => "Event Month" # selects "February", :from => "Event Month"
def select(option_text, options = {}) def selects(option_text, options = {})
select_option(option_text, options[:from]).choose find_select_option(option_text, options[:from]).choose
end end
webrat_deprecate :selects, :select alias_method :select, :selects
# Verifies that a an option element exists on the current page with the specified
# text. You can optionally restrict the search to a specific select list by
# assigning <tt>options[:from]</tt> the value of the select list's name or
# a label. Remove the option's value before the form is submitted.
#
# Examples:
# unselect "January"
# unselect "February", :from => "event_month"
# unselect "February", :from => "Event Month"
def unselect(option_text, options={})
select_option(option_text, options[:from]).unchoose
end
webrat_deprecate :unselects, :unselect
DATE_TIME_SUFFIXES = {
:year => '1i',
:month => '2i',
:day => '3i',
:hour => '4i',
:minute => '5i'
}
# Verifies that date elements (year, month, day) exist on the current page
# with the specified values. You can optionally restrict the search to a specific
# date's elements by assigning <tt>options[:from]</tt> the value of the date's
# label. Selects all the date elements with date provided. The date provided may
# be a string or a Date/Time object.
#
# Rail's convention is used for detecting the date elements. All elements
# are assumed to have a shared prefix. You may also specify the prefix
# by assigning <tt>options[:id_prefix]</tt>.
#
# Examples:
# select_date "January 23, 2004"
# select_date "April 26, 1982", :from => "Birthday"
# select_date Date.parse("December 25, 2000"), :from => "Event"
# select_date "April 26, 1982", :id_prefix => 'birthday'
def select_date(date_to_select, options ={})
date = date_to_select.is_a?(Date) || date_to_select.is_a?(Time) ?
date_to_select : Date.parse(date_to_select)
id_prefix = locate_id_prefix(options) do
year_field = FieldByIdLocator.new(@session, dom, /(.*?)_#{DATE_TIME_SUFFIXES[:year]}$/).locate
raise NotFoundError.new("No date fields were found") unless year_field && year_field.id =~ /(.*?)_1i/
$1
end
select date.year, :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:year]}"
select date.strftime('%B'), :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:month]}"
select date.day, :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:day]}"
end
webrat_deprecate :selects_date, :select_date
# Verifies that time elements (hour, minute) exist on the current page
# with the specified values. You can optionally restrict the search to a specific
# time's elements by assigning <tt>options[:from]</tt> the value of the time's
# label. Selects all the time elements with date provided. The time provided may
# be a string or a Time object.
#
# Rail's convention is used for detecting the time elements. All elements are
# assumed to have a shared prefix. You may specify the prefix by assigning
# <tt>options[:id_prefix]</tt>.
#
# Note: Just like Rails' time_select helper this assumes the form is using
# 24 hour select boxes, and not 12 hours with AM/PM.
#
# Examples:
# select_time "9:30"
# select_date "3:30PM", :from => "Party Time"
# select_date Time.parse("10:00PM"), :from => "Event"
# select_date "10:30AM", :id_prefix => 'meeting'
def select_time(time_to_select, options ={})
time = time_to_select.is_a?(Time) ? time_to_select : Time.parse(time_to_select)
id_prefix = locate_id_prefix(options) do
hour_field = FieldByIdLocator.new(@session, dom, /(.*?)_#{DATE_TIME_SUFFIXES[:hour]}$/).locate
raise NotFoundError.new("No time fields were found") unless hour_field && hour_field.id =~ /(.*?)_4i/
$1
end
select time.hour.to_s.rjust(2,'0'), :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:hour]}"
select time.min.to_s.rjust(2,'0'), :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:minute]}"
end
webrat_deprecate :selects_time, :select_time
# Verifies and selects all the date and time elements on the current page.
# See #select_time and #select_date for more details and available options.
#
# Examples:
# select_datetime "January 23, 2004 10:30AM"
# select_datetime "April 26, 1982 7:00PM", :from => "Birthday"
# select_datetime Time.parse("December 25, 2000 15:30"), :from => "Event"
# select_datetime "April 26, 1982 5:50PM", :id_prefix => 'birthday'
def select_datetime(time_to_select, options ={})
time = time_to_select.is_a?(Time) ? time_to_select : Time.parse(time_to_select)
options[:id_prefix] ||= (options[:from] ? FieldByIdLocator.new(@session, dom, options[:from]).locate : nil)
select_date time, options
select_time time, options
end
webrat_deprecate :selects_datetime, :select_datetime
# Verifies that an input file field exists on the current page and sets # Verifies that an input file field exists on the current page and sets
# its value to the given +file+, so that the file will be uploaded # its value to the given +file+, so that the file will be uploaded
# along with the form. An optional <tt>content_type</tt> may be given. # along with the form. An optional <tt>content_type</tt> may be given.
# #
# Example: # Example:
# attach_file "Resume", "/path/to/the/resume.txt" # attaches_file "Resume", "/path/to/the/resume.txt"
# attach_file "Photo", "/path/to/the/image.png", "image/png" # attaches_file "Photo", "/path/to/the/image.png", "image/png"
def attach_file(field_locator, path, content_type = nil) def attach_file(field_locator, path, content_type = nil)
locate_field(field_locator, FileField).set(path, content_type) locate_field(field_locator, FileField).set(path, content_type)
end end
webrat_deprecate :attaches_file, :attach_file alias_method :attaches_file, :attach_file
# Issues a request for the URL pointed to by an <tt>area</tt> tag
# on the current page, follows any redirects, and verifies the
# final page load was successful.
#
# The area used is the first area whose title or id contains the
# given +area_name+ (case is ignored).
#
# Example:
# click_area 'Australia'
def click_area(area_name) def click_area(area_name)
find_area(area_name).click find_area(area_name).click
end end
webrat_deprecate :clicks_area, :click_area alias_method :clicks_area, :click_area
# Issues a request for the URL pointed to by a link on the current page, # Issues a request for the URL pointed to by a link on the current page,
# follows any redirects, and verifies the final page load was successful. # follows any redirects, and verifies the final page load was successful.
# #
# click_link has very basic support for detecting Rails-generated # click_link has very basic support for detecting Rails-generated
# JavaScript onclick handlers for PUT, POST and DELETE links, as well as # JavaScript onclick handlers for PUT, POST and DELETE links, as well as
# CSRF authenticity tokens if they are present. # CSRF authenticity tokens if they are present.
# #
@ -259,25 +123,19 @@ module Webrat
# #
# Passing a :method in the options hash overrides the HTTP method used # Passing a :method in the options hash overrides the HTTP method used
# for making the link request # for making the link request
# #
# It will try to find links by (in order of precedence):
# innerHTML, with simple &nbsp; handling
# title
# id
#
# innerHTML and title are matchable by text subtring or Regexp
# id is matchable by full text equality or Regexp
#
# Example: # Example:
# click_link "Sign up" # click_link "Sign up"
#
# click_link "Sign up", :javascript => false # click_link "Sign up", :javascript => false
#
# click_link "Sign up", :method => :put # click_link "Sign up", :method => :put
def click_link(text_or_title_or_id, options = {}) def click_link(link_text, options = {})
find_link(text_or_title_or_id).click(options) find_link(link_text).click(options)
end end
webrat_deprecate :clicks_link, :click_link alias_method :clicks_link, :click_link
# Verifies that a submit button exists for the form, then submits the form, follows # Verifies that a submit button exists for the form, then submits the form, follows
# any redirects, and verifies the final page was successful. # any redirects, and verifies the final page was successful.
# #
@ -291,51 +149,33 @@ module Webrat
find_button(value).click find_button(value).click
end end
webrat_deprecate :clicks_button, :click_button alias_method :clicks_button, :click_button
# Submit the form with the given id.
#
# Note that +click_button+ is usually preferrable for simulating
# form submissions, as you may specify part of the button text
# rather than the form id.
#
# Example:
# submit_form 'login'
def submit_form(id)
FormLocator.new(@session, dom, id).locate.submit
end
def dom # :nodoc: def dom # :nodoc:
return @dom if @dom return @dom if @dom
if @selector if @selector
@dom = scoped_dom @dom = scoped_dom
else else
@dom = page_dom @dom = page_dom
end end
return @dom return @dom
end end
protected protected
def page_dom #:nodoc: def page_dom #:nodoc:
return @response.dom if @response.respond_to?(:dom) return @response.dom if @response.respond_to?(:dom)
dom = Webrat.nokogiri_document(@response_body)
if @session.xml_content_type? Webrat.define_dom_method(@response, dom)
dom = Webrat::XML.xml_document(@response_body)
else
dom = Webrat::XML.html_document(@response_body)
end
Webrat::XML.define_dom_method(@response, dom)
return dom return dom
end end
def scoped_dom def scoped_dom #:nodoc:
@scope.dom.css(@selector).first Webrat.nokogiri_document(@scope.dom.search(@selector).first.to_html)
end end
def locate_field(field_locator, *field_types) #:nodoc: def locate_field(field_locator, *field_types) #:nodoc:
if field_locator.is_a?(Field) if field_locator.is_a?(Field)
field_locator field_locator
@ -343,24 +183,26 @@ module Webrat
field(field_locator, *field_types) field(field_locator, *field_types)
end end
end end
def locate_id_prefix(options, &location_strategy) #:nodoc: def areas #:nodoc:
return options[:id_prefix] if options[:id_prefix] dom.search("area").map do |element|
Area.new(@session, element)
if options[:from]
if (label = LabelLocator.new(@session, dom, options[:from]).locate)
label.for_id
else
raise NotFoundError.new("Could not find the label with text #{options[:from]}")
end
else
yield
end end
end end
def forms #:nodoc: def links #:nodoc:
@forms ||= Form.load_all(@session, dom) dom.search("a[@href]").map do |link_element|
Link.new(@session, link_element)
end
end end
def forms #:nodoc:
return @forms if @forms
@forms = dom.search("form").map do |form_element|
Form.new(@session, form_element)
end
end
end end
end end

View File

@ -0,0 +1,29 @@
module Webrat
class SelectOption #:nodoc:
def initialize(select, element)
@select = select
@element = element
end
def matches_text?(text)
if text.is_a?(Regexp)
@element.inner_html =~ text
else
@element.inner_html == text.to_s
end
end
def choose
@select.raise_error_if_disabled
@select.set(value)
end
protected
def value
@element["value"] || @element.inner_html
end
end
end

View File

@ -2,96 +2,59 @@ require "forwardable"
require "ostruct" require "ostruct"
require "webrat/core/mime" require "webrat/core/mime"
require "webrat/core/save_and_open_page"
module Webrat module Webrat
# A page load or form submission returned an unsuccessful response code (500-599)
class PageLoadError < WebratError
end
class InfiniteRedirectError < WebratError
end
def self.session_class
if Webrat.configuration.mode == :selenium
SeleniumSession
else
Session
end
end
def self.adapter_class
case Webrat.configuration.mode
when :rails
RailsAdapter
when :merb
MerbAdapter
when :rack
RackAdapter
when :rack_test
warn("The :rack_test mode is deprecated. Please use :rack instead")
require "webrat/rack"
RackAdapter
when :sinatra
warn("The :sinatra mode is deprecated. Please use :rack instead")
SinatraAdapter
when :mechanize
MechanizeAdapter
else
raise WebratError.new(<<-STR)
Unknown Webrat mode: #{Webrat.configuration.mode.inspect}
Please ensure you have a Webrat configuration block that specifies a mode
in your test_helper.rb, spec_helper.rb, or env.rb (for Cucumber).
This configure block supercedes the need to require "webrat/<framework>".
For example:
Webrat.configure do |config|
config.mode = :rails
end
STR
end
end
class Session class Session
extend Forwardable extend Forwardable
include Logging include Logging
include SaveAndOpenPage include Flunk
attr_accessor :adapter
attr_reader :current_url attr_reader :current_url
attr_reader :elements
def initialize #:nodoc:
def_delegators :@adapter, :response, :response_code, :response_body, :response_headers,
:response_body=, :response_code=,
:get, :post, :put, :delete
def initialize(adapter = nil)
@adapter = adapter
@http_method = :get @http_method = :get
@data = {} @data = {}
@default_headers = {} @default_headers = {}
@custom_headers = {} @custom_headers = {}
@current_url = nil
reset
end end
# Saves the page out to RAILS_ROOT/tmp/ and opens it in the default
# web browser if on OS X. Useful for debugging.
#
# Example:
# save_and_open_page
def save_and_open_page
return unless File.exist?(saved_page_dir)
filename = "#{saved_page_dir}/webrat-#{Time.now.to_i}.html"
File.open(filename, "w") do |f|
f.write rewrite_css_and_image_references(response_body)
end
open_in_browser(filename)
end
def current_dom #:nodoc: def current_dom #:nodoc:
current_scope.dom current_scope.dom
end end
# For backwards compatibility -- removing in 1.0 # For backwards compatibility -- removing in 1.0
def current_page #:nodoc: def current_page #:nodoc:
warn "current_page is deprecated and will be going away in the next release. Use current_url instead."
page = OpenStruct.new page = OpenStruct.new
page.url = @current_url page.url = @current_url
page.http_method = @http_method page.http_method = @http_method
page.data = @data page.data = @data
page page
end end
def doc_root #:nodoc:
nil
end
def saved_page_dir #:nodoc:
File.expand_path(".")
end
def header(key, value) def header(key, value)
@custom_headers[key] = value @custom_headers[key] = value
@ -100,9 +63,9 @@ For example:
def http_accept(mime_type) def http_accept(mime_type)
header('Accept', Webrat::MIME.mime_type(mime_type)) header('Accept', Webrat::MIME.mime_type(mime_type))
end end
def basic_auth(user, pass) def basic_auth(user, pass)
encoded_login = ["#{user}:#{pass}"].pack("m*").gsub(/\n/, '') encoded_login = ["#{user}:#{pass}"].pack("m*")
header('HTTP_AUTHORIZATION', "Basic #{encoded_login}") header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
end end
@ -115,82 +78,50 @@ For example:
h['HTTP_REFERER'] = @current_url if @current_url h['HTTP_REFERER'] = @current_url if @current_url
debug_log "REQUESTING PAGE: #{http_method.to_s.upcase} #{url} with #{data.inspect} and HTTP headers #{h.inspect}" debug_log "REQUESTING PAGE: #{http_method.to_s.upcase} #{url} with #{data.inspect} and HTTP headers #{h.inspect}"
if h.empty?
send "#{http_method}", url, data || {}
else
send "#{http_method}", url, data || {}, h
end
process_request(http_method, url, data, h) save_and_open_page if exception_caught?
flunk("Page load was not successful (Code: #{response_code.inspect}):\n#{formatted_error}") unless success_code?
save_and_open_page if exception_caught? && Webrat.configuration.open_error_files?
raise PageLoadError.new("Page load was not successful (Code: #{response_code.inspect}):\n#{formatted_error}") unless success_code? @_scopes = nil
@_page_scope = nil
reset
@current_url = url @current_url = url
@http_method = http_method @http_method = http_method
@data = data @data = data
if internal_redirect?
check_for_infinite_redirects
request_page(response_location, :get, {})
end
return response return response
end end
def check_for_infinite_redirects
if current_url == response_location
@_identical_redirect_count ||= 0
@_identical_redirect_count += 1
end
if infinite_redirect_limit_exceeded?
raise InfiniteRedirectError.new("#{Webrat.configuration.infinite_redirect_limit} redirects to the same URL (#{current_url.inspect})")
end
end
def infinite_redirect_limit_exceeded?
Webrat.configuration.infinite_redirect_limit &&
(@_identical_redirect_count || 0) > Webrat.configuration.infinite_redirect_limit
end
def success_code? #:nodoc: def success_code? #:nodoc:
(200..499).include?(response_code) (200..499).include?(response_code)
end end
def redirect? #:nodoc:
[301, 302, 303, 307].include?(response_code)
end
def internal_redirect?
return false unless redirect?
#should keep internal_redirects if the subdomain changes
current_host_domain = current_host.split('.')[-2..-1].join('.') rescue current_host
response_location_host_domain = response_location_host.split('.')[-2..-1].join('.') rescue response_location_host
current_host_domain == response_location_host_domain
end
#easy helper to pull out where we were redirected to
def redirected_to
redirect? ? response_location : nil
end
def exception_caught? #:nodoc: def exception_caught? #:nodoc:
response_body =~ /Exception caught/ response_body =~ /Exception caught/
end end
def current_scope #:nodoc: def current_scope #:nodoc:
scopes.last || page_scope scopes.last || page_scope
end end
# Reloads the last page requested. Note that this will resubmit forms # Reloads the last page requested. Note that this will resubmit forms
# and their data. # and their data.
def reload #
# Example:
# reloads
def reloads
request_page(@current_url, @http_method, @data) request_page(@current_url, @http_method, @data)
end end
webrat_deprecate :reloads, :reload alias_method :reload, :reloads
# Works like click_link, but only looks for the link text within a given selector # Works like click_link, but only looks for the link text within a given selector
# #
# Example: # Example:
# click_link_within "#user_12", "Vote" # click_link_within "#user_12", "Vote"
def click_link_within(selector, link_text) def click_link_within(selector, link_text)
@ -199,15 +130,15 @@ For example:
end end
end end
webrat_deprecate :clicks_link_within, :click_link_within alias_method :clicks_link_within, :click_link_within
def within(selector) def within(selector)
scopes.push(Scope.from_scope(self, current_scope, selector)) scopes.push(Scope.from_scope(self, current_scope, selector))
ret = yield(current_scope) ret = yield(current_scope)
scopes.pop scopes.pop
return ret return ret
end end
# Issues a GET request for a page, follows any redirects, and verifies the final page # Issues a GET request for a page, follows any redirects, and verifies the final page
# load was successful. # load was successful.
# #
@ -216,8 +147,17 @@ For example:
def visit(url = nil, http_method = :get, data = {}) def visit(url = nil, http_method = :get, data = {})
request_page(url, http_method, data) request_page(url, http_method, data)
end end
webrat_deprecate :visits, :visit alias_method :visits, :visit
def open_in_browser(path) #:nodoc
`open #{path}`
end
def rewrite_css_and_image_references(response_html) #:nodoc
return response_html unless doc_root
response_html.gsub(/"\/(stylesheets|images)/, doc_root + '/\1')
end
# Subclasses can override this to show error messages without html # Subclasses can override this to show error messages without html
def formatted_error #:nodoc: def formatted_error #:nodoc:
@ -231,73 +171,18 @@ For example:
def page_scope #:nodoc: def page_scope #:nodoc:
@_page_scope ||= Scope.from_page(self, response, response_body) @_page_scope ||= Scope.from_page(self, response, response_body)
end end
def dom
page_scope.dom
end
def xml_content_type?
false
end
def simulate
return if Webrat.configuration.mode == :selenium
yield
end
def automate
return unless Webrat.configuration.mode == :selenium
yield
end
def_delegators :current_scope, :fill_in, :fills_in def_delegators :current_scope, :fill_in, :fills_in
def_delegators :current_scope, :set_hidden_field
def_delegators :current_scope, :submit_form
def_delegators :current_scope, :check, :checks def_delegators :current_scope, :check, :checks
def_delegators :current_scope, :uncheck, :unchecks def_delegators :current_scope, :uncheck, :unchecks
def_delegators :current_scope, :choose, :chooses def_delegators :current_scope, :choose, :chooses
def_delegators :current_scope, :select, :selects def_delegators :current_scope, :select, :selects
def_delegators :current_scope, :unselect, :unselects
def_delegators :current_scope, :select_datetime, :selects_datetime
def_delegators :current_scope, :select_date, :selects_date
def_delegators :current_scope, :select_time, :selects_time
def_delegators :current_scope, :attach_file, :attaches_file def_delegators :current_scope, :attach_file, :attaches_file
def_delegators :current_scope, :click_area, :clicks_area def_delegators :current_scope, :click_area, :clicks_area
def_delegators :current_scope, :click_link, :clicks_link def_delegators :current_scope, :click_link, :clicks_link
def_delegators :current_scope, :click_button, :clicks_button def_delegators :current_scope, :click_button, :clicks_button
def_delegators :current_scope, :should_see
def_delegators :current_scope, :should_not_see
def_delegators :current_scope, :field_labeled def_delegators :current_scope, :field_labeled
def_delegators :current_scope, :field_by_xpath
def_delegators :current_scope, :field_with_id
def_delegators :current_scope, :select_option
def_delegators :current_scope, :field_named
private
def process_request(http_method, url, data, headers)
if headers.empty?
send "#{http_method}", url, data || {}
else
send "#{http_method}", url, data || {}, headers
end
end
def response_location
response_headers['Location']
end
def current_host
URI.parse(current_url).host || @custom_headers["Host"] || "www.example.com"
end
def response_location_host
URI.parse(response_location).host || "www.example.com"
end
def reset
@elements = {}
@_scopes = nil
@_page_scope = nil
end
end end
end end

View File

@ -1,72 +0,0 @@
require "webrat/core_extensions/meta_class"
module Webrat #:nodoc:
module XML #:nodoc:
def self.document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif stringlike.respond_to?(:body)
Nokogiri::HTML(stringlike.body.to_s)
else
Nokogiri::HTML(stringlike.to_s)
end
end
def self.html_document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif stringlike.respond_to?(:body)
Nokogiri::HTML(stringlike.body.to_s)
else
Nokogiri::HTML(stringlike.to_s)
end
end
def self.xml_document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif stringlike.respond_to?(:body)
Nokogiri::XML(stringlike.body.to_s)
else
Nokogiri::XML(stringlike.to_s)
end
end
def self.define_dom_method(object, dom) #:nodoc:
object.meta_class.send(:define_method, :dom) do
dom
end
end
end
end
module Nokogiri #:nodoc:
module CSS #:nodoc:
class XPathVisitor #:nodoc:
def visit_pseudo_class_text(node) #:nodoc:
"@type='text'"
end
def visit_pseudo_class_password(node) #:nodoc:
"@type='password'"
end
end
end
end

View File

@ -12,7 +12,7 @@ class Object #:nodoc:
def blank? def blank?
respond_to?(:empty?) ? empty? : !self respond_to?(:empty?) ? empty? : !self
end end
# An object is present if it's not blank. # An object is present if it's not blank.
def present? def present?
!blank? !blank?

View File

@ -1,8 +1,8 @@
class Module #:nodoc: class Module #:nodoc:
def webrat_deprecate(old_method_name, new_method_name) def deprecate(old_method_name, new_method_name)
define_method old_method_name do |*args| define_method old_method_name do |*args|
warn "#{old_method_name} is deprecated. Use #{new_method_name} instead." warn "#{old_method_name} is deprecated. Use #{new_method_name} instead."
__send__(new_method_name, *args) __send__(new_method_name, *args)
end end
end end
end end

View File

@ -1,12 +1,12 @@
class Array #:nodoc: class Array #:nodoc:
def detect_mapped def detect_mapped
each do |element| each do |element|
result = yield element result = yield element
return result if result return result if result
end end
return nil return nil
end end
end end

View File

@ -0,0 +1,131 @@
# This class has dubious semantics and we only have it so that
# people can write params[:key] instead of params['key']
# and they get the same value for both keys.
class HashWithIndifferentAccess < Hash #:nodoc:
def initialize(constructor = {})
if constructor.is_a?(Hash)
super()
update(constructor)
else
super(constructor)
end
end
def default(key = nil)
if key.is_a?(Symbol) && include?(key = key.to_s)
self[key]
else
super
end
end
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
alias_method :regular_update, :update unless method_defined?(:regular_update)
#
# Assigns a new value to the hash.
#
# Example:
#
# hash = HashWithIndifferentAccess.new
# hash[:key] = "value"
#
def []=(key, value)
regular_writer(convert_key(key), convert_value(value))
end
#
# Updates the instantized hash with values from the second.
#
# Example:
#
# >> hash_1 = HashWithIndifferentAccess.new
# => {}
#
# >> hash_1[:key] = "value"
# => "value"
#
# >> hash_2 = HashWithIndifferentAccess.new
# => {}
#
# >> hash_2[:key] = "New Value!"
# => "New Value!"
#
# >> hash_1.update(hash_2)
# => {"key"=>"New Value!"}
#
def update(other_hash)
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
self
end
alias_method :merge!, :update
# Checks the hash for a key matching the argument passed in
def key?(key)
super(convert_key(key))
end
alias_method :include?, :key?
alias_method :has_key?, :key?
alias_method :member?, :key?
# Fetches the value for the specified key, same as doing hash[key]
def fetch(key, *extras)
super(convert_key(key), *extras)
end
# Returns an array of the values at the specified indicies.
def values_at(*indices)
indices.collect {|key| self[convert_key(key)]}
end
# Returns an exact copy of the hash.
def dup
HashWithIndifferentAccess.new(self)
end
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
# Does not overwrite the existing hash.
def merge(hash)
self.dup.update(hash)
end
# Removes a specified key from the hash.
def delete(key)
super(convert_key(key))
end
def stringify_keys!; self end
def symbolize_keys!; self end
def to_options!; self end
# Convert to a Hash with String keys.
def to_hash
Hash.new(default).merge(self)
end
protected
def convert_key(key)
key.kind_of?(Symbol) ? key.to_s : key
end
def convert_value(value)
case value
when Hash
value.with_indifferent_access
when Array
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
else
value
end
end
end
class Hash #:nodoc:
def with_indifferent_access
hash = HashWithIndifferentAccess.new(self)
hash.default = self.default
hash
end
end

View File

@ -3,4 +3,4 @@ class ::Object #:nodoc:
class << self; self end class << self; self end
end end
end end

View File

@ -1,5 +1,5 @@
class NilClass #:nodoc: class NilClass #:nodoc:
def to_query_string def to_param
nil nil
end end
end end

View File

@ -1,27 +0,0 @@
class TCPSocket
def self.wait_for_service_with_timeout(options)
start_time = Time.now
until listening_service?(options)
verbose_wait
if options[:timeout] && (Time.now > start_time + options[:timeout])
raise SocketError.new("Socket did not open within #{options[:timeout]} seconds")
end
end
end
def self.wait_for_service_termination_with_timeout(options)
start_time = Time.now
while listening_service?(options)
verbose_wait
if options[:timeout] && (Time.now > start_time + options[:timeout])
raise SocketError.new("Socket did not terminate within #{options[:timeout]} seconds")
end
end
end
end

View File

@ -1,10 +0,0 @@
module Merb #:nodoc:
module Test #:nodoc:
module RequestHelper #:nodoc:
def request(uri, env = {})
@_webrat_session ||= Webrat::MerbAdapter.new
@_webrat_session.response = @_webrat_session.request(uri, env)
end
end
end
end

View File

@ -1,25 +0,0 @@
require "action_controller"
require "action_controller/integration"
module ActionController #:nodoc:
IntegrationTest.class_eval do
include Webrat::Methods
include Webrat::Matchers
# The Rails version of within supports passing in a model and Webrat
# will apply a scope based on Rails' dom_id for that model.
#
# Example:
# within User.last do
# click_link "Delete"
# end
def within(selector_or_object, &block)
if selector_or_object.is_a?(String)
super
else
super('#' + RecordIdentifier.dom_id(selector_or_object), &block)
end
end
end
end

View File

@ -1,11 +0,0 @@
# Supports using the matchers in controller, helper, and view specs if you're
# using rspec-rails. Just add a require statement to spec/spec_helper.rb or env.rb:
#
# require 'webrat/integrations/rspec-rails'
#
require "nokogiri"
require "webrat/core/matchers"
Spec::Runner.configure do |config|
config.include(Webrat::Matchers, :type => [:controller, :helper, :view])
end

View File

@ -1,11 +0,0 @@
require "webrat/selenium"
if defined?(ActionController::IntegrationTest)
module ActionController #:nodoc:
IntegrationTest.class_eval do
include Webrat::Methods
include Webrat::Selenium::Methods
include Webrat::Selenium::Matchers
end
end
end

28
lib/webrat/mechanize.rb Normal file
View File

@ -0,0 +1,28 @@
require "mechanize"
module Webrat
class MechanizeSession < Session #:nodoc:
def initialize(mechanize = WWW::Mechanize.new)
super()
@mechanize = mechanize
end
def get(url, data)
@mechanize_page = @mechanize.get(url, data)
end
def post(url, data)
@mechanize_page = @mechanize.post(url, data)
end
def response_body
@mechanize_page.content
end
def response_code
@mechanize_page.code.to_i
end
end
end

View File

@ -1,9 +1,75 @@
require "webrat" require "webrat/core"
# This is a temporary hack to support backwards compatibility require "cgi"
# with Merb 1.0.8 until it's updated to use the new Webrat.configure gem "extlib"
# syntax require "extlib"
require "merb-core"
Webrat.configure do |config| HashWithIndifferentAccess = Mash
config.mode = :merb
module Webrat
class MerbSession < Session #:nodoc:
include Merb::Test::MakeRequest
attr_accessor :response
def get(url, data, headers = nil)
do_request(url, data, headers, "GET")
end
def post(url, data, headers = nil)
do_request(url, data, headers, "POST")
end
def put(url, data, headers = nil)
do_request(url, data, headers, "PUT")
end
def delete(url, data, headers = nil)
do_request(url, data, headers, "DELETE")
end
def response_body
@response.body.to_s
end
def response_code
@response.status
end
def do_request(url, data, headers, method)
@response = request(url,
:params => (data && data.any?) ? data : nil,
:headers => headers,
:method => method)
follow_redirect
end
def follow_redirect
self.get(@response.headers['Location'], nil, @response.headers) if @response.status == 302
end
end
end end
module Merb
module Test
module RequestHelper #:nodoc:
def request(uri, env = {})
@_webrat_session ||= Webrat::MerbSession.new
@_webrat_session.response = @_webrat_session.request(uri, env)
end
def follow_redirect
@_webrat_session.follow_redirect
end
end
end
end
class Merb::Test::RspecStory #:nodoc:
def browser
@browser ||= Webrat::MerbSession.new
end
end

24
lib/webrat/rack.rb Normal file
View File

@ -0,0 +1,24 @@
require 'webrat'
class CGIMethods #:nodoc:
def self.parse_query_parameters(params)
hash = {}
params.split('&').each do |p|
pair = p.split('=')
hash[pair[0]] = pair[1]
end
hash
end
end
module Webrat
class RackSession < Session #:nodoc:
def response_body
@response.body
end
def response_code
@response.status
end
end
end

102
lib/webrat/rails.rb Normal file
View File

@ -0,0 +1,102 @@
require "webrat"
module Webrat
class RailsSession < Session #:nodoc:
def initialize(integration_session)
super()
@integration_session = integration_session
end
def doc_root
File.expand_path(File.join(RAILS_ROOT, 'public'))
end
def saved_page_dir
File.expand_path(File.join(RAILS_ROOT, "tmp"))
end
def get(url, data, headers = nil)
do_request(:get, url, data, headers)
end
def post(url, data, headers = nil)
do_request(:post, url, data, headers)
end
def put(url, data, headers = nil)
do_request(:put, url, data, headers)
end
def delete(url, data, headers = nil)
do_request(:delete, url, data, headers)
end
def response_body
response.body
end
def response_code
response.code.to_i
end
protected
def do_request(http_method, url, data, headers) #:nodoc:
update_protocol(url)
@integration_session.request_via_redirect(http_method, remove_protocol(url), data, headers)
end
def remove_protocol(href) #:nodoc:
if href =~ %r{^https?://www.example.com(/.*)}
$LAST_MATCH_INFO.captures.first
else
href
end
end
def update_protocol(href) #:nodoc:
if href =~ /^https:/
@integration_session.https!(true)
elsif href =~ /^http:/
@integration_session.https!(false)
end
end
def response #:nodoc:
@integration_session.response
end
end
end
module ActionController
module Integration
class Session #:nodoc:
unless instance_methods.include?("put_via_redirect")
require "webrat/rails/redirect_actions"
include Webrat::RedirectActions
end
def respond_to?(name)
super || webrat_session.respond_to?(name)
end
def method_missing(name, *args, &block)
if webrat_session.respond_to?(name)
webrat_session.send(name, *args, &block)
else
super
end
end
protected
def webrat_session
@webrat_session ||= Webrat::RailsSession.new(self)
end
end
end
end

View File

@ -0,0 +1,18 @@
# For Rails before http://dev.rubyonrails.org/ticket/10497 was committed
module Webrat
module RedirectActions #:nodoc:
def put_via_redirect(path, parameters = {}, headers = {})
put path, parameters, headers
follow_redirect! while redirect?
status
end
def delete_via_redirect(path, parameters = {}, headers = {})
delete path, parameters, headers
follow_redirect! while redirect?
status
end
end
end

View File

@ -1,2 +0,0 @@
warn("Requiring 'webrat/rspec-rails' is deprecated. Please require 'webrat/integrations/rspec-rails' instead")
require "webrat/integrations/rspec-rails"

View File

@ -1,70 +1,3 @@
require "webrat" require "selenium"
require "selenium/client"
require "webrat/selenium/silence_stream"
require "webrat/selenium/selenium_session" require "webrat/selenium/selenium_session"
require "webrat/selenium/matchers"
require "webrat/core_extensions/tcp_socket"
module Webrat
# To use Webrat's Selenium support, you'll need the selenium-client gem installed.
# Activate it with (for example, in your <tt>env.rb</tt>):
#
# require "webrat"
#
# Webrat.configure do |config|
# config.mode = :selenium
# end
#
# == Dropping down to the selenium-client API
#
# If you ever need to do something with Selenium not provided in the Webrat API,
# you can always drop down to the selenium-client API using the <tt>selenium</tt> method.
# For example:
#
# When "I drag the photo to the left" do
# selenium.dragdrop("id=photo_123", "+350, 0")
# end
#
# == Choosing the underlying framework to test
#
# Webrat assumes you're using rails by default but it can also work with sinatra
# and merb. To take advantage of this you can use the configuration block to
# set the application_framework variable.
# require "webrat"
#
# Webrat.configure do |config|
# config.mode = :selenium
# config.application_port = 4567
# config.application_framework = :sinatra # could also be :merb
# end
#
# == Auto-starting of the appserver and java server
#
# Webrat will automatically start the Selenium Java server process and an instance
# of Mongrel when a test is run. The Mongrel will run in the "selenium" environment
# instead of "test", so ensure you've got that defined, and will run on port
# Webrat.configuration.application_port.
#
# == Waiting
#
# In order to make writing Selenium tests as easy as possible, Webrat will automatically
# wait for the correct elements to exist on the page when trying to manipulate them
# with methods like <tt>fill_in</tt>, etc. In general, this means you should be able to write
# your Webrat::Selenium tests ignoring the concurrency issues that can plague in-browser
# testing, so long as you're using the Webrat API.
module Selenium
module Methods
def response
webrat_session.response
end
def wait_for(*args, &block)
webrat_session.wait_for(*args, &block)
end
def save_and_open_screengrab
webrat_session.save_and_open_screengrab
end
end
end
end

View File

@ -1,40 +0,0 @@
module Webrat
module Selenium
class ApplicationServerFactory
def self.app_server_instance
case Webrat.configuration.application_framework
when :sinatra
require "webrat/selenium/application_servers/sinatra"
return Webrat::Selenium::ApplicationServers::Sinatra.new
when :merb
require "webrat/selenium/application_servers/merb"
return Webrat::Selenium::ApplicationServers::Merb.new
when :rails
require "webrat/selenium/application_servers/rails"
return Webrat::Selenium::ApplicationServers::Rails.new
when :external
require "webrat/selenium/application_servers/external"
return Webrat::Selenium::ApplicationServers::External.new
else
raise WebratError.new(<<-STR)
Unknown Webrat application_framework: #{Webrat.configuration.application_framework.inspect}
Please ensure you have a Webrat configuration block that specifies an application_framework
in your test_helper.rb, spec_helper.rb, or env.rb (for Cucumber).
For example:
Webrat.configure do |config|
# ...
config.application_framework = :rails
end
STR
end
end
end
end
end

View File

@ -1,5 +0,0 @@
require "webrat/selenium/application_servers/base"
require "webrat/selenium/application_servers/sinatra"
require "webrat/selenium/application_servers/merb"
require "webrat/selenium/application_servers/rails"
require "webrat/selenium/application_servers/external"

View File

@ -1,46 +0,0 @@
require "webrat/selenium/silence_stream"
module Webrat
module Selenium
module ApplicationServers
class Base
include Webrat::Selenium::SilenceStream
def boot
start
wait
stop_at_exit
end
def stop_at_exit
at_exit do
stop
end
end
def wait
$stderr.print "==> Waiting for #{Webrat.configuration.application_framework} application server on port #{Webrat.configuration.application_port}... "
wait_for_socket
$stderr.print "Ready!\n"
end
def wait_for_socket
silence_stream(STDOUT) do
TCPSocket.wait_for_service_with_timeout \
:host => "0.0.0.0",
:port => Webrat.configuration.application_port.to_i,
:timeout => 30 # seconds
end
rescue SocketError
fail
end
def prepare_pid_file(file_path, pid_file_name)
FileUtils.mkdir_p File.expand_path(file_path)
File.expand_path("#{file_path}/#{pid_file_name}")
end
end
end
end
end

View File

@ -1,26 +0,0 @@
require "webrat/selenium/application_servers/base"
module Webrat
module Selenium
module ApplicationServers
class External < Webrat::Selenium::ApplicationServers::Base
def start
warn "Webrat Ignoring Start Of Application Server Due to External Mode"
end
def stop
end
def fail
end
def pid_file
end
def wait
end
end
end
end
end

View File

@ -1,50 +0,0 @@
require "webrat/selenium/application_servers/base"
module Webrat
module Selenium
module ApplicationServers
class Merb < Webrat::Selenium::ApplicationServers::Base
def start
system start_command
end
def stop
silence_stream(STDOUT) do
pid = File.read(pid_file)
system("kill -9 #{pid}")
FileUtils.rm_f pid_file
end
end
def fail
$stderr.puts
$stderr.puts
$stderr.puts "==> Failed to boot the Merb application server... exiting!"
$stderr.puts
$stderr.puts "Verify you can start a Merb server on port #{Webrat.configuration.application_port} with the following command:"
$stderr.puts
$stderr.puts " #{start_command}"
exit
end
def pid_file
"log/merb.#{Webrat.configuration.application_port}.pid"
end
def start_command
"#{merb_command} -d -p #{Webrat.configuration.application_port} -e #{Webrat.configuration.application_environment}"
end
def merb_command
if File.exist?('bin/merb')
merb_cmd = 'bin/merb'
else
merb_cmd = 'merb'
end
end
end
end
end
end

View File

@ -1,44 +0,0 @@
require "webrat/selenium/application_servers/base"
module Webrat
module Selenium
module ApplicationServers
class Rails < Webrat::Selenium::ApplicationServers::Base
def start
system start_command
end
def stop
silence_stream(STDOUT) do
system stop_command
end
end
def fail
$stderr.puts
$stderr.puts
$stderr.puts "==> Failed to boot the Rails application server... exiting!"
$stderr.puts
$stderr.puts "Verify you can start a Rails server on port #{Webrat.configuration.application_port} with the following command:"
$stderr.puts
$stderr.puts " #{start_command}"
exit
end
def pid_file
prepare_pid_file("#{RAILS_ROOT}/tmp/pids", "mongrel_selenium.pid")
end
def start_command
"mongrel_rails start -d --chdir='#{RAILS_ROOT}' --port=#{Webrat.configuration.application_port} --environment=#{Webrat.configuration.application_environment} --pid #{pid_file} &"
end
def stop_command
"mongrel_rails stop -c #{RAILS_ROOT} --pid #{pid_file}"
end
end
end
end
end

View File

@ -1,37 +0,0 @@
require "webrat/selenium/application_servers/base"
module Webrat
module Selenium
module ApplicationServers
class Sinatra < Webrat::Selenium::ApplicationServers::Base
def start
fork do
File.open('rack.pid', 'w') { |fp| fp.write Process.pid }
exec 'rackup', File.expand_path(Dir.pwd + '/config.ru'), '-p', Webrat.configuration.application_port.to_s
end
end
def stop
silence_stream(STDOUT) do
pid = File.read(pid_file)
system("kill -9 #{pid}")
FileUtils.rm_f pid_file
end
end
def fail
$stderr.puts
$stderr.puts
$stderr.puts "==> Failed to boot the Sinatra application server... exiting!"
exit
end
def pid_file
prepare_pid_file(Dir.pwd, 'rack.pid')
end
end
end
end
end

View File

@ -1,19 +1,12 @@
if (locator == '*') { if (locator == '*') {
return selenium.browserbot.locationStrategies['xpath'].call(this, "//input[@type='submit']", inDocument, inWindow) return selenium.browserbot.locationStrategies['xpath'].call(this, "//input[@type='submit']", inDocument, inWindow)
} }
var buttons = inDocument.getElementsByTagName('button');
var inputs = inDocument.getElementsByTagName('input'); var inputs = inDocument.getElementsByTagName('input');
var result = $A(inputs).concat($A(buttons)).find(function(candidate){ return $A(inputs).find(function(candidate){
var type = candidate.getAttribute('type'); inputType = candidate.getAttribute('type');
if (type == 'submit' || type == 'image' || type == 'button') { if (inputType == 'submit' || inputType == 'image') {
var matches_id = PatternMatcher.matches(locator, candidate.id); var buttonText = $F(candidate);
var matches_value = PatternMatcher.matches(locator, candidate.value); return (PatternMatcher.matches(locator, buttonText));
var matches_html = PatternMatcher.matches(locator, candidate.innerHTML); }
var matches_alt = PatternMatcher.matches(locator, candidate.alt); return false;
if (matches_id || matches_value || matches_html || matches_alt) {
return true;
}
}
return false;
}); });
return result;

View File

@ -1,48 +1,16 @@
// Credit to: http://simonwillison.net/2006/Jan/20/escape/
RegExp.escape = function(text) {
if (!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
};
var allLabels = inDocument.getElementsByTagName("label"); var allLabels = inDocument.getElementsByTagName("label");
var regExp = new RegExp('^\\W*' + RegExp.escape(locator) + '(\\b|$)', 'i');
var candidateLabels = $A(allLabels).select(function(candidateLabel){ var candidateLabels = $A(allLabels).select(function(candidateLabel){
var regExp = new RegExp('^' + locator + '\\b', 'i');
var labelText = getText(candidateLabel).strip(); var labelText = getText(candidateLabel).strip();
return (labelText.search(regExp) >= 0); return (labelText.search(regExp) >= 0);
}); });
if (candidateLabels.length == 0) { if (candidateLabels.length == 0) {
return null; return null;
} }
candidateLabels = candidateLabels.sortBy(function(s) { return s.length * -1; }); //reverse length sort
//reverse length sort
candidateLabels = candidateLabels.sortBy(function(s) {
return s.length * -1;
});
var locatedLabel = candidateLabels.first(); var locatedLabel = candidateLabels.first();
var labelFor = null; var labelFor = locatedLabel.getAttribute('for');
if (locatedLabel.getAttribute('for')) {
labelFor = locatedLabel.getAttribute('for');
} else if (locatedLabel.attributes['for']) { // IE
labelFor = locatedLabel.attributes['for'].nodeValue;
}
if ((labelFor == null) && (locatedLabel.hasChildNodes())) { if ((labelFor == null) && (locatedLabel.hasChildNodes())) {
return locatedLabel.getElementsByTagName('button')[0] return locatedLabel.firstChild; //TODO: should find the first form field, not just any node
|| locatedLabel.getElementsByTagName('input')[0]
|| locatedLabel.getElementsByTagName('textarea')[0]
|| locatedLabel.getElementsByTagName('select')[0];
} }
return selenium.browserbot.locationStrategies['id'].call(this, labelFor, inDocument, inWindow); return selenium.browserbot.locationStrategies['id'].call(this, labelFor, inDocument, inWindow);

View File

@ -1,5 +1,4 @@
var locationStrategies = selenium.browserbot.locationStrategies; var locationStrategies = selenium.browserbot.locationStrategies;
return locationStrategies['id'].call(this, locator, inDocument, inWindow) return locationStrategies['id'].call(this, locator, inDocument, inWindow)
|| locationStrategies['name'].call(this, locator, inDocument, inWindow) || locationStrategies['name'].call(this, locator, inDocument, inWindow)
|| locationStrategies['label'].call(this, locator, inDocument, inWindow) || locationStrategies['label'].call(this, locator, inDocument, inWindow)

View File

@ -1,32 +1,9 @@
var links = inDocument.getElementsByTagName('a'); var links = inDocument.getElementsByTagName('a');
var candidateLinks = $A(links).select(function(candidateLink) { var candidateLinks = $A(links).select(function(candidateLink) {
var textMatched = false; return PatternMatcher.matches(locator, getText(candidateLink));
var titleMatched = false;
var idMatched = false;
if (getText(candidateLink).toLowerCase().indexOf(locator.toLowerCase()) != -1) {
textMatched = true;
}
if (candidateLink.title.toLowerCase().indexOf(locator.toLowerCase()) != -1) {
titleMatched = true;
}
if (candidateLink.id.toLowerCase().indexOf(locator.toLowerCase()) != -1) {
idMatched = true;
}
return textMatched || idMatched || titleMatched;
}); });
if (candidateLinks.length == 0) { if (candidateLinks.length == 0) {
return null; return null;
} }
candidateLinks = candidateLinks.sortBy(function(s) { return s.length * -1; }); //reverse length sort
//reverse length sort
candidateLinks = candidateLinks.sortBy(function(s) {
return s.length * -1;
});
return candidateLinks.first(); return candidateLinks.first();

View File

@ -1,4 +0,0 @@
require "webrat/selenium/matchers/have_xpath"
require "webrat/selenium/matchers/have_selector"
# require "webrat/selenium/matchers/have_tag"
require "webrat/selenium/matchers/have_content"

View File

@ -1,78 +0,0 @@
module Webrat
module Selenium
module Matchers
class HasContent #:nodoc:
def initialize(content)
@content = content
end
def matches?(response)
response.session.wait_for do
response.selenium.is_text_present(text_finder)
end
rescue Webrat::TimeoutError => e
@error_message = e.message
false
end
def does_not_match?(response)
response.session.wait_for do
!response.selenium.is_text_present(text_finder)
end
rescue Webrat::TimeoutError => e
@error_message = e.message
false
end
# ==== Returns
# String:: The failure message.
def failure_message
"expected the response to #{content_message}:\n#{@error_message}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected the response to not #{content_message}"
end
def content_message
case @content
when String
"include \"#{@content}\""
when Regexp
"match #{@content.inspect}"
end
end
def text_finder
if @content.is_a?(Regexp)
"regexp:#{@content.source}"
else
@content
end
end
end
# Matches the contents of an HTML document with
# whatever string is supplied
def contain(content)
HasContent.new(content)
end
# Asserts that the body of the response contain
# the supplied string or regexp
def assert_contain(content)
hc = HasContent.new(content)
assert hc.matches?(response), hc.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_not_contain(content)
hc = HasContent.new(content)
assert !hc.matches?(response), hc.negative_failure_message
end
end
end
end

View File

@ -1,57 +0,0 @@
module Webrat
module Selenium
module Matchers
class HaveSelector
def initialize(expected)
@expected = expected
end
def matches?(response)
response.session.wait_for do
response.selenium.is_element_present("css=#{@expected}")
end
rescue Webrat::TimeoutError
false
end
def does_not_match?(response)
response.session.wait_for do
!response.selenium.is_element_present("css=#{@expected}")
end
rescue Webrat::TimeoutError
false
end
# ==== Returns
# String:: The failure message.
def failure_message
"expected following text to match selector #{@expected}:\n#{@document}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following text to not match selector #{@expected}:\n#{@document}"
end
end
def have_selector(content)
HaveSelector.new(content)
end
# Asserts that the body of the response contains
# the supplied selector
def assert_have_selector(expected)
hs = HaveSelector.new(expected)
assert hs.matches?(response), hs.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_have_no_selector(expected)
hs = HaveSelector.new(expected)
assert !hs.matches?(response), hs.negative_failure_message
end
end
end
end

View File

@ -1,72 +0,0 @@
module Webrat
module Selenium
module Matchers
class HaveTag < HaveSelector #:nodoc:
# ==== Returns
# String:: The failure message.
def failure_message
"expected following output to contain a #{tag_inspect} tag:\n#{@document}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following output to omit a #{tag_inspect}:\n#{@document}"
end
def tag_inspect
options = @expected.last.dup
content = options.delete(:content)
html = "<#{@expected.first}"
options.each do |k,v|
html << " #{k}='#{v}'"
end
if content
html << ">#{content}</#{@expected.first}>"
else
html << "/>"
end
html
end
def query
options = @expected.last.dup
selector = @expected.first.to_s
selector << ":contains('#{options.delete(:content)}')" if options[:content]
options.each do |key, value|
selector << "[#{key}='#{value}']"
end
Nokogiri::CSS.parse(selector).map { |ast| ast.to_xpath }
end
end
def have_tag(name, attributes = {}, &block)
HaveTag.new([name, attributes], &block)
end
alias_method :match_tag, :have_tag
# Asserts that the body of the response contains
# the supplied tag with the associated selectors
def assert_have_tag(name, attributes = {})
ht = HaveTag.new([name, attributes])
assert ht.matches?(response), ht.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_have_no_tag(name, attributes = {})
ht = HaveTag.new([name, attributes])
assert !ht.matches?(response), ht.negative_failure_message
end
end
end
end

View File

@ -1,53 +0,0 @@
module Webrat
module Selenium
module Matchers
class HaveXpath
def initialize(expected)
@expected = expected
end
def matches?(response)
response.session.wait_for do
response.selenium.is_element_present("xpath=#{@expected}")
end
rescue Webrat::TimeoutError
false
end
def does_not_match?(response)
response.session.wait_for do
!response.selenium.is_element_present("xpath=#{@expected}")
end
rescue Webrat::TimeoutError
false
end
# ==== Returns
# String:: The failure message.
def failure_message
"expected following text to match xpath #{@expected}:\n#{@document}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following text to not match xpath #{@expected}:\n#{@document}"
end
end
def have_xpath(xpath)
HaveXpath.new(xpath)
end
def assert_have_xpath(expected)
hs = HaveXpath.new(expected)
assert hs.matches?(response), hs.failure_message
end
def assert_have_no_xpath(expected)
hs = HaveXpath.new(expected)
assert !hs.matches?(response), hs.negative_failure_message
end
end
end
end

View File

@ -1,89 +0,0 @@
module Webrat
module Selenium
class SeleniumRCServer
include Webrat::Selenium::SilenceStream
def self.boot
new.boot
end
def boot
return if selenium_grid?
start
wait
stop_at_exit
end
def start
silence_stream(STDOUT) do
remote_control.start :background => true
end
end
def stop_at_exit
at_exit do
stop
end
end
def remote_control
return @remote_control if @remote_control
server_options = { :timeout => Webrat.configuration.selenium_browser_startup_timeout }
server_options[:firefox_profile] = Webrat.configuration.selenium_firefox_profile if Webrat.configuration.selenium_firefox_profile
@remote_control = ::Selenium::RemoteControl::RemoteControl.new("0.0.0.0",
Webrat.configuration.selenium_server_port,
server_options)
@remote_control.jar_file = jar_path
return @remote_control
end
def jar_path
File.expand_path(__FILE__ + "../../../../../vendor/selenium-server.jar")
end
def selenium_grid?
Webrat.configuration.selenium_server_address
end
def wait
$stderr.print "==> Waiting for Selenium RC server on port #{Webrat.configuration.selenium_server_port}... "
wait_for_socket
$stderr.print "Ready!\n"
rescue SocketError
fail
end
def wait_for_socket
silence_stream(STDOUT) do
TCPSocket.wait_for_service_with_timeout \
:host => (Webrat.configuration.selenium_server_address || "0.0.0.0"),
:port => Webrat.configuration.selenium_server_port,
:timeout => 45 # seconds
end
end
def fail
$stderr.puts
$stderr.puts
$stderr.puts "==> Failed to boot the Selenium RC server... exiting!"
exit
end
def stop
silence_stream(STDOUT) do
::Selenium::RemoteControl::RemoteControl.new("0.0.0.0",
Webrat.configuration.selenium_server_port,
:timeout => 5).stop
end
end
end
end
end

View File

@ -1,272 +1,137 @@
require "webrat/core/save_and_open_page"
require "webrat/selenium/selenium_rc_server"
require "webrat/selenium/application_server_factory"
require "webrat/selenium/application_servers/base"
begin
require "selenium"
rescue LoadError => e
e.message << " (You may need to install the selenium-rc gem)"
raise e
end
module Webrat module Webrat
class TimeoutError < WebratError
end
class SeleniumResponse
attr_reader :body
attr_reader :session
def initialize(session, body)
@session = session
@body = body
end
def selenium
session.selenium
end
end
class SeleniumSession class SeleniumSession
include Webrat::SaveAndOpenPage
include Webrat::Selenium::SilenceStream def initialize(selenium_driver) #:nodoc:
@selenium = selenium_driver
def initialize(*args) # :nodoc: extend_selenium
define_location_strategies
end end
def simulate
end
def automate
yield
end
def visit(url) def visit(url)
selenium.open(url) @selenium.open(url)
end end
webrat_deprecate :visits, :visit alias_method :visits, :visit
def fill_in(field_identifier, options) def fill_in(field_identifier, options)
locator = "webrat=#{field_identifier}" locator = "webrat=#{Regexp.escape(field_identifier)}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 @selenium.type(locator, "#{options[:with]}")
selenium.type(locator, "#{options[:with]}")
end end
webrat_deprecate :fills_in, :fill_in alias_method :fills_in, :fill_in
def response
SeleniumResponse.new(self, response_body)
end
def response_body #:nodoc: def response_body #:nodoc:
selenium.get_html_source @selenium.get_html_source
end end
def current_url
selenium.location
end
def click_button(button_text_or_regexp = nil, options = {}) def click_button(button_text_or_regexp = nil, options = {})
if button_text_or_regexp.is_a?(Hash) && options == {} if button_text_or_regexp.is_a?(Hash) && options == {}
pattern, options = nil, button_text_or_regexp pattern, options = nil, button_text_or_regexp
elsif button_text_or_regexp else
pattern = adjust_if_regexp(button_text_or_regexp) pattern = adjust_if_regexp(button_text_or_regexp)
end end
pattern ||= '*' pattern ||= '*'
locator = "button=#{pattern}" @selenium.click("button=#{pattern}")
wait_for_result(options[:wait])
selenium.wait_for_element locator, :timeout_in_seconds => 5
selenium.click locator
end end
webrat_deprecate :clicks_button, :click_button alias_method :clicks_button, :click_button
def click_link(link_text_or_regexp, options = {}) def click_link(link_text_or_regexp, options = {})
if link_text_or_regexp.is_a?(Regexp) pattern = adjust_if_regexp(link_text_or_regexp)
pattern = "evalregex:#{link_text_or_regexp.inspect}" @selenium.click("webratlink=#{pattern}")
else wait_for_result(options[:wait])
pattern = link_text_or_regexp.to_s
end
locator = "webratlink=#{pattern}"
selenium.wait_for_element locator, :timeout_in_seconds => 5
selenium.click locator
end end
webrat_deprecate :clicks_link, :click_link alias_method :clicks_link, :click_link
def click_link_within(selector, link_text, options = {}) def click_link_within(selector, link_text, options = {})
locator = "webratlinkwithin=#{selector}|#{link_text}" @selenium.click("webratlinkwithin=#{selector}|#{link_text}")
selenium.wait_for_element locator, :timeout_in_seconds => 5 wait_for_result(options[:wait])
selenium.click locator end
alias_method :clicks_link_within, :click_link_within
def wait_for_result(wait_type)
if wait_type == :ajax
wait_for_ajax
elsif wait_type == :effects
wait_for_effects
else
wait_for_page_to_load
end
end end
webrat_deprecate :clicks_link_within, :click_link_within def wait_for_page_to_load(timeout = 15000)
@selenium.wait_for_page_to_load(timeout)
end
def select(option_text, options = {}) def wait_for_ajax(timeout = 15000)
@selenium.wait_for_condition "Ajax.activeRequestCount == 0", timeout
end
def wait_for_effects(timeout = 15000)
@selenium.wait_for_condition "window.Effect.Queue.size() == 0", timeout
end
def wait_for_ajax_and_effects
wait_for_ajax
wait_for_effects
end
def selects(option_text, options = {})
id_or_name_or_label = options[:from] id_or_name_or_label = options[:from]
if id_or_name_or_label if id_or_name_or_label
select_locator = "webrat=#{id_or_name_or_label}" select_locator = "webrat=#{id_or_name_or_label}"
else else
select_locator = "webratselectwithoption=#{option_text}" select_locator = "webratselectwithoption=#{option_text}"
end end
@selenium.select(select_locator, option_text)
selenium.wait_for_element select_locator, :timeout_in_seconds => 5
selenium.select(select_locator, option_text)
end end
webrat_deprecate :selects, :select
def choose(label_text) def choose(label_text)
locator = "webrat=#{label_text}" @selenium.click("webrat=#{label_text}")
selenium.wait_for_element locator, :timeout_in_seconds => 5
selenium.click locator
end end
webrat_deprecate :chooses, :choose alias_method :chooses, :choose
def check(label_text) def check(label_text)
locator = "webrat=#{label_text}" @selenium.check("webrat=#{label_text}")
selenium.wait_for_element locator, :timeout_in_seconds => 5
selenium.click locator
end end
alias_method :uncheck, :check
alias_method :checks, :check
webrat_deprecate :checks, :check
def is_ordered(*args) #:nodoc:
def fire_event(field_identifier, event) @selenium.is_ordered(*args)
locator = "webrat=#{Regexp.escape(field_identifier)}"
selenium.fire_event(locator, "#{event}")
end end
def key_down(field_identifier, key_code) def dragdrop(*args) #:nodoc:
locator = "webrat=#{Regexp.escape(field_identifier)}" @selenium.dragdrop(*args)
selenium.key_down(locator, key_code)
end end
def key_up(field_identifier, key_code) protected
locator = "webrat=#{Regexp.escape(field_identifier)}"
selenium.key_up(locator, key_code)
end
def wait_for(params={})
timeout = params[:timeout] || 5
message = params[:message] || "Timeout exceeded"
begin_time = Time.now
while (Time.now - begin_time) < timeout
value = nil
begin
value = yield
rescue Exception => e
unless is_ignorable_wait_for_exception?(e)
raise e
end
end
return value if value
sleep 0.25
end
error_message = "#{message} (after #{timeout} sec)"
if $browser && Webrat.configuration.selenium_verbose_output
error_message += <<-EOS
HTML of the page was:
#{selenium.get_html_source}"
EOS
end
raise Webrat::TimeoutError.new(error_message)
true
end
def selenium
return $browser if $browser
setup
$browser
end
webrat_deprecate :browser, :selenium
def save_and_open_screengrab
return unless File.exist?(Webrat.configuration.saved_pages_dir)
filename = "#{Webrat.configuration.saved_pages_dir}/webrat-#{Time.now.to_i}.png"
if $browser.chrome_backend?
$browser.capture_entire_page_screenshot(filename, '')
else
$browser.capture_screenshot(filename)
end
open_in_browser(filename)
end
protected
def is_ignorable_wait_for_exception?(exception) #:nodoc:
if defined?(::Spec::Expectations::ExpectationNotMetError)
return true if exception.class == ::Spec::Expectations::ExpectationNotMetError
end
return true if [::Selenium::CommandError, Webrat::WebratError].include?(exception.class)
return false
end
def setup #:nodoc:
Webrat::Selenium::SeleniumRCServer.boot
Webrat::Selenium::ApplicationServerFactory.app_server_instance.boot
create_browser
$browser.start
extend_selenium
define_location_strategies
$browser.window_maximize
end
def create_browser
$browser = ::Selenium::Client::Driver.new(Webrat.configuration.selenium_server_address || "localhost",
Webrat.configuration.selenium_server_port, Webrat.configuration.selenium_browser_key, "http://#{Webrat.configuration.application_address}:#{Webrat.configuration.application_port_for_selenium}")
$browser.set_speed(0) unless Webrat.configuration.selenium_server_address
at_exit do
silence_stream(STDOUT) do
$browser.stop
end
end
end
def adjust_if_regexp(text_or_regexp) #:nodoc: def adjust_if_regexp(text_or_regexp) #:nodoc:
if text_or_regexp.is_a?(Regexp) if text_or_regexp.is_a?(Regexp)
"evalregex:#{text_or_regexp.inspect}" "evalregex:#{text_or_regexp.inspect}"
else else
"evalregex:/#{text_or_regexp}/" text_or_regexp
end end
end end
def extend_selenium #:nodoc: def extend_selenium #:nodoc:
extensions_file = File.join(File.dirname(__FILE__), "selenium_extensions.js") extensions_file = File.join(File.dirname(__FILE__), "selenium_extensions.js")
extenions_js = File.read(extensions_file) extenions_js = File.read(extensions_file)
selenium.get_eval(extenions_js) @selenium.get_eval(extenions_js)
end end
def define_location_strategies #:nodoc: def define_location_strategies #:nodoc:
Dir[File.join(File.dirname(__FILE__), "location_strategy_javascript", "*.js")].sort.each do |file| Dir[File.join(File.dirname(__FILE__), "location_strategy_javascript", "*.js")].sort.each do |file|
strategy_js = File.read(file) strategy_js = File.read(file)
strategy_name = File.basename(file, '.js') strategy_name = File.basename(file, '.js')
selenium.add_location_strategy(strategy_name, strategy_js) @selenium.add_location_strategy(strategy_name, strategy_js)
end end
end end
end end
end end

View File

@ -1,18 +0,0 @@
module Webrat
module Selenium
module SilenceStream
# active_support already defines silence_stream, no need to do that again if it's already present.
# http://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/kernel/reporting.rb
unless Kernel.respond_to?(:silence_stream)
def silence_stream(stream)
old_stream = stream.dup
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
stream.sync = true
yield
ensure
stream.reopen(old_stream)
end
end
end
end
end

19
lib/webrat/sinatra.rb Normal file
View File

@ -0,0 +1,19 @@
require 'webrat/rack'
require 'sinatra'
require 'sinatra/test/methods'
module Webrat
class SinatraSession < RackSession #:nodoc:
include Sinatra::Test::Methods
%w(get head post put delete).each do |verb|
define_method(verb) do |*args| # (path, data, headers = nil)
path, data, headers = *args
params = data.merge({:env => headers || {}})
self.__send__("#{verb}_it", path, params)
follow! while @response.redirect?
end
end
end
end

View File

@ -0,0 +1,23 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "Basic Auth HTTP headers" do
before do
@session = Webrat::TestSession.new
@session.basic_auth('user', 'secret')
end
it "should be present in visit" do
@session.should_receive(:get).with("/", {}, {'HTTP_AUTHORIZATION' => "Basic dXNlcjpzZWNyZXQ=\n"})
@session.visit("/")
end
it "should be present in form submits" do
@session.response_body = <<-EOS
<form method="post" action="/form1">
<input type="submit" />
</form>
EOS
@session.should_receive(:post).with("/form1", {}, {'HTTP_AUTHORIZATION' => "Basic dXNlcjpzZWNyZXQ=\n"})
@session.click_button
end
end

136
spec/api/check_spec.rb Normal file
View File

@ -0,0 +1,136 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "check" do
before do
@session = Webrat::TestSession.new
end
it "should fail if no checkbox found" do
@session.response_body = <<-EOS
<form method="post" action="/login">
</form>
EOS
lambda { @session.check "remember_me" }.should raise_error
end
it "should fail if input is not a checkbox" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="text" name="remember_me" />
</form>
EOS
lambda { @session.check "remember_me" }.should raise_error
end
it "should check rails style checkboxes" do
@session.response_body = <<-EOS
<form method="get" action="/login">
<input id="user_tos" name="user[tos]" type="checkbox" value="1" />
<input name="user[tos]" type="hidden" value="0" />
<label for="user_tos">TOS</label>
<input type="submit" />
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "1"})
@session.check "TOS"
@session.click_button
end
it "should result in the value on being posted if not specified" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="checkbox" name="remember_me" />
<input type="submit" />
</form>
EOS
@session.should_receive(:post).with("/login", "remember_me" => "on")
@session.check "remember_me"
@session.click_button
end
it "should fail if the checkbox is disabled" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="checkbox" name="remember_me" disabled="disabled" />
<input type="submit" />
</form>
EOS
lambda { @session.check "remember_me" }.should raise_error
end
it "should result in a custom value being posted" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="checkbox" name="remember_me" value="yes" />
<input type="submit" />
</form>
EOS
@session.should_receive(:post).with("/login", "remember_me" => "yes")
@session.check "remember_me"
@session.click_button
end
end
describe "uncheck" do
before do
@session = Webrat::TestSession.new
end
it "should fail if no checkbox found" do
@session.response_body = <<-EOS
<form method="post" action="/login">
</form>
EOS
lambda { @session.uncheck "remember_me" }.should raise_error
end
it "should fail if input is not a checkbox" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="text" name="remember_me" />
</form>
EOS
lambda { @session.uncheck "remember_me" }.should raise_error
end
it "should fail if the checkbox is disabled" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="checkbox" name="remember_me" checked="checked" disabled="disabled" />
<input type="submit" />
</form>
EOS
lambda { @session.uncheck "remember_me" }.should raise_error
end
it "should uncheck rails style checkboxes" do
@session.response_body = <<-EOS
<form method="get" action="/login">
<input id="user_tos" name="user[tos]" type="checkbox" value="1" checked="checked" />
<input name="user[tos]" type="hidden" value="0" />
<label for="user_tos">TOS</label>
<input type="submit" />
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "0"})
@session.check "TOS"
@session.uncheck "TOS"
@session.click_button
end
it "should result in value not being posted" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="checkbox" name="remember_me" value="yes" checked="checked" />
<input type="submit" />
</form>
EOS
@session.should_receive(:post).with("/login", {})
@session.uncheck "remember_me"
@session.click_button
end
end

View File

@ -1,32 +1,31 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "choose" do describe "choose" do
before do
@session = Webrat::TestSession.new
end
it "should fail if no radio buttons found" do it "should fail if no radio buttons found" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
</form> </form>
</html> EOS
HTML
lambda { @session.choose "first option" }.should raise_error
lambda { choose "first option" }.should raise_error(Webrat::NotFoundError)
end end
it "should fail if input is not a radio button" do it "should fail if input is not a radio button" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="text" name="first_option" /> <input type="text" name="first_option" />
</form> </form>
</html> EOS
HTML
lambda { @session.choose "first_option" }.should raise_error
lambda { choose "first_option" }.should raise_error(Webrat::NotFoundError)
end end
it "should check rails style radio buttons" do it "should check rails style radio buttons" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_gender_male" name="user[gender]" type="radio" value="M" /> <input id="user_gender_male" name="user[gender]" type="radio" value="M" />
<label for="user_gender_male">Male</label> <label for="user_gender_male">Male</label>
@ -34,16 +33,14 @@ describe "choose" do
<label for="user_gender_female">Female</label> <label for="user_gender_female">Female</label>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"gender" => "M"})
webrat_session.should_receive(:get).with("/login", "user" => {"gender" => "M"}) @session.choose "Male"
choose "Male" @session.click_button
click_button
end end
it "should only submit last chosen value" do it "should only submit last chosen value" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_gender_male" name="user[gender]" type="radio" value="M" /> <input id="user_gender_male" name="user[gender]" type="radio" value="M" />
<label for="user_gender_male">Male</label> <label for="user_gender_male">Male</label>
@ -51,57 +48,49 @@ describe "choose" do
<label for="user_gender_female">Female</label> <label for="user_gender_female">Female</label>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"gender" => "M"})
webrat_session.should_receive(:get).with("/login", "user" => {"gender" => "M"}) @session.choose "Female"
choose "Female" @session.choose "Male"
choose "Male" @session.click_button
click_button
end end
it "should fail if the radio button is disabled" do it "should fail if the radio button is disabled" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="radio" name="first_option" disabled="disabled" /> <input type="radio" name="first_option" disabled="disabled" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML
lambda { @session.choose "first_option" }.should raise_error
lambda { choose "first_option" }.should raise_error(Webrat::DisabledFieldError)
end end
it "should result in the value on being posted if not specified" do it "should result in the value on being posted if not specified" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="radio" name="first_option" /> <input type="radio" name="first_option" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login", "first_option" => "on")
webrat_session.should_receive(:post).with("/login", "first_option" => "on") @session.choose "first_option"
choose "first_option" @session.click_button
click_button
end end
it "should result in the value on being posted if not specified and checked by default" do it "should result in the value on being posted if not specified and checked by default" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="radio" name="first_option" checked="checked"/> <input type="radio" name="first_option" checked="checked"/>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login", "first_option" => "on")
webrat_session.should_receive(:post).with("/login", "first_option" => "on") @session.click_button
click_button
end end
it "should result in the value of the selected radio button being posted when a subsequent one is checked by default" do it "should result in the value of the selected radio button being posted when a subsequent one is checked by default" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input id="user_gender_male" name="user[gender]" type="radio" value="M" /> <input id="user_gender_male" name="user[gender]" type="radio" value="M" />
<label for="user_gender_male">Male</label> <label for="user_gender_male">Male</label>
@ -109,10 +98,9 @@ describe "choose" do
<label for="user_gender_female">Female</label> <label for="user_gender_female">Female</label>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login", "user" => {"gender" => "M"})
webrat_session.should_receive(:post).with("/login", "user" => {"gender" => "M"}) @session.choose "Male"
choose "Male" @session.click_button
click_button
end end
end end

View File

@ -1,106 +1,93 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "click_area" do describe "click_area" do
before do
@session = Webrat::TestSession.new
end
it "should use get by default" do it "should use get by default" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.should_receive(:get).with("/page", {})
webrat_session.should_receive(:get).with("/page", {}) @session.click_area "Berlin"
click_area "Berlin"
end end
it "should assert valid response" do it "should assert valid response" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.response_code = 501
webrat_session.response_code = 501 lambda { @session.click_area "Berlin" }.should raise_error
lambda { click_area "Berlin" }.should raise_error(Webrat::PageLoadError)
end end
[200, 300, 400, 499].each do |status| [200, 300, 400, 499].each do |status|
it "should consider the #{status} status code as success" do it "should consider the #{status} status code as success" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.response_code = status
webrat_session.stub!(:redirect? => false) lambda { @session.click_area "Berlin" }.should_not raise_error
webrat_session.response_code = status
lambda { click_area "Berlin" }.should_not raise_error
end end
end end
it "should fail if the area doesn't exist" do it "should fail if the area doesn't exist" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML
lambda { lambda {
click_area "Missing area" @session.click_area "Missing area"
}.should raise_error(Webrat::NotFoundError) }.should raise_error
end end
it "should not be case sensitive" do it "should not be case sensitive" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.should_receive(:get).with("/page", {})
webrat_session.should_receive(:get).with("/page", {}) @session.click_area "berlin"
click_area "berlin"
end end
it "should follow relative links" do it "should follow relative links" do
webrat_session.stub!(:current_url => "/page") @session.stub!(:current_url).and_return("/page")
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="sub" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="sub" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.should_receive(:get).with("/page/sub", {})
webrat_session.should_receive(:get).with("/page/sub", {}) @session.click_area "Berlin"
click_area "Berlin"
end end
it "should follow fully qualified local links" do it "should follow fully qualified local links" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="http://www.example.com/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="http://www.example.com/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.should_receive(:get).with("http://www.example.com/page", {})
webrat_session.should_receive(:get).with("http://www.example.com/page", {}) @session.click_area "Berlin"
click_area "Berlin"
end end
it "should follow query parameters" do it "should follow query parameters" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<map name="map_de" id="map_de"> <map name="map_de" id="map_de">
<area href="/page?foo=bar" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" /> <area href="/page?foo=bar" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map> </map>
</html> EOS
HTML @session.should_receive(:get).with("/page?foo=bar", {})
webrat_session.should_receive(:get).with("/page?foo=bar", {}) @session.click_area "Berlin"
click_area "Berlin"
end end
end end

View File

@ -1,244 +1,210 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper") require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "click_button" do describe "click_button" do
it "should fail if no buttons" do before do
with_html <<-HTML @session = Webrat::TestSession.new
<html>
<form method="get" action="/login"></form>
</html>
HTML
lambda { click_button }.should raise_error(Webrat::NotFoundError)
end end
it "should fail if no buttons" do
@session.response_body = <<-EOS
<form method="get" action="/login"></form>
EOS
lambda { @session.click_button }.should raise_error
end
it "should fail if input is not a submit button" do it "should fail if input is not a submit button" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input type="reset" /> <input type="reset" />
</form> </form>
</html> EOS
HTML
lambda { click_button }.should raise_error(Webrat::NotFoundError) lambda { @session.click_button }.should raise_error
end end
it "should fail if button is disabled" do it "should fail if button is disabled" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input type="submit" disabled="disabled" /> <input type="submit" disabled="disabled" />
</form> </form>
</html> EOS
HTML
lambda { click_button }.should raise_error(Webrat::DisabledFieldError) lambda { @session.click_button }.should raise_error
end end
it "should default to get method" do it "should default to get method" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form action="/login"> <form action="/login">
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get)
webrat_session.should_receive(:get) @session.click_button
click_button
end end
it "should assert valid response" do it "should assert valid response" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form action="/login"> <form action="/login">
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.response_code = 501
webrat_session.response_code = 501 lambda { @session.click_button }.should raise_error
lambda { click_button }.should raise_error(Webrat::PageLoadError)
end end
[200, 300, 400, 499].each do |status| [200, 300, 400, 499].each do |status|
it "should consider the #{status} status code as success" do it "should consider the #{status} status code as success" do
with_html <<-HTML @session.response_body = <<-EOS
<html> <form action="/login">
<form action="/login"> <input type="submit" />
<input type="submit" /> </form>
</form> EOS
</html> @session.response_code = status
HTML lambda { @session.click_button }.should_not raise_error
webrat_session.stub!(:redirect? => false)
webrat_session.response_code = status
lambda { click_button }.should_not raise_error
end end
end end
it "should submit the first form by default" do it "should submit the first form by default" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/form1"> <form method="get" action="/form1">
<input type="submit" /> <input type="submit" />
</form> </form>
<form method="get" action="/form2"> <form method="get" action="/form2">
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/form1", {})
webrat_session.should_receive(:get).with("/form1", {}) @session.click_button
click_button
end end
it "should not explode on file fields" do it "should not explode on file fields" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/form1"> <form method="get" action="/form1">
<input type="file" /> <input type="file" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.click_button
click_button
end end
it "should submit the form with the specified button" do it "should submit the form with the specified button" do
with_html <<-HTML @session.response_body = <<-EOS
<html> <form method="get" action="/form1">
<form method="get" action="/form1"> <input type="submit" />
<input type="submit" /> </form>
</form> <form method="get" action="/form2">
<form method="get" action="/form2"> <input type="submit" value="Form2" />
<input type="submit" value="Form2" /> </form>
</form> EOS
</html> @session.should_receive(:get).with("/form2", {})
HTML @session.click_button "Form2"
webrat_session.should_receive(:get).with("/form2", {})
click_button "Form2"
end end
it "should use action from form" do it "should use action from form" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", {})
webrat_session.should_receive(:get).with("/login", {}) @session.click_button
click_button
end end
it "should use method from form" do it "should use method from form" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post)
webrat_session.should_receive(:post) @session.click_button
click_button
end end
it "should send button as param if it has a name" do it "should send button as param if it has a name" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="submit" name="cancel" value="Cancel" /> <input type="submit" name="cancel" value="Cancel" />
<input type="submit" name="login" value="Login" /> <input type="submit" name="login" value="Login" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login", "login" => "Login")
webrat_session.should_receive(:post).with("/login", "login" => "Login") @session.click_button("Login")
click_button("Login")
end end
it "should not send button as param if it has no name" do it "should not send button as param if it has no name" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="submit" name="cancel" value="Cancel" /> <input type="submit" name="cancel" value="Cancel" />
<input type="submit" value="Login" /> <input type="submit" value="Login" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login", {})
webrat_session.should_receive(:post).with("/login", {}) @session.click_button("Login")
click_button("Login")
end end
it "should send default password field values" do it "should send default password field values" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_password" name="user[password]" value="mypass" type="password" /> <input id="user_password" name="user[password]" value="mypass" type="password" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"password" => "mypass"})
webrat_session.should_receive(:get).with("/login", "user" => {"password" => "mypass"}) @session.click_button
click_button end
end
it "should send default hidden field values" do it "should send default hidden field values" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_email" name="user[email]" value="test@example.com" type="hidden" /> <input id="user_email" name="user[email]" value="test@example.com" type="hidden" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"email" => "test@example.com"})
webrat_session.should_receive(:get).with("/login", "user" => {"email" => "test@example.com"}) @session.click_button
click_button
end end
it "should send default text field values" do it "should send default text field values" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_email" name="user[email]" value="test@example.com" type="text" /> <input id="user_email" name="user[email]" value="test@example.com" type="text" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"email" => "test@example.com"})
webrat_session.should_receive(:get).with("/login", "user" => {"email" => "test@example.com"}) @session.click_button
click_button
end end
it "should not send disabled field values" do it "should not send disabled field values" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input disabled="disabled" id="user_email" name="user[email]" value="test@example.com" type="text" /> <input disabled id="user_email" name="user[email]" value="test@example.com" type="text" />
<input disabled="disabled" id="user_gender_male" name="user[gender]" type="radio" value="M" /> <input disabled id="user_gender_male" name="user[gender]" type="radio" value="M" />
<label for="user_gender_male">Male</label> <label for="user_gender_male">Male</label>
<input disabled="disabled" id="user_gender_female" name="user[gender]" type="radio" value="F" checked="checked" /> <input disabled id="user_gender_female" name="user[gender]" type="radio" value="F" checked="checked" />
<label for="user_gender_female">Female</label> <label for="user_gender_female">Female</label>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", {})
webrat_session.should_receive(:get).with("/login", {}) @session.click_button
click_button
end end
it "should send default checked fields" do it "should send default checked fields" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_tos" name="user[tos]" value="1" type="checkbox" checked="checked" /> <input id="user_tos" name="user[tos]" value="1" type="checkbox" checked="checked" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"tos" => "1"})
webrat_session.should_receive(:get).with("/login", "user" => {"tos" => "1"}) @session.click_button
click_button
end end
it "should send default radio options" do it "should send default radio options" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_gender_male" name="user[gender]" type="radio" value="M" /> <input id="user_gender_male" name="user[gender]" type="radio" value="M" />
<label for="user_gender_male">Male</label> <label for="user_gender_male">Male</label>
@ -246,43 +212,37 @@ describe "click_button" do
<label for="user_gender_female">Female</label> <label for="user_gender_female">Female</label>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"gender" => "F"})
webrat_session.should_receive(:get).with("/login", "user" => {"gender" => "F"}) @session.click_button
click_button
end end
it "should send correct data for rails style unchecked fields" do it "should send correct data for rails style unchecked fields" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_tos" name="user[tos]" type="checkbox" value="1" /> <input id="user_tos" name="user[tos]" type="checkbox" value="1" />
<input name="user[tos]" type="hidden" value="0" /> TOS <input name="user[tos]" type="hidden" value="0" /> TOS
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"tos" => "0"})
webrat_session.should_receive(:get).with("/login", "user" => {"tos" => "0"}) @session.click_button
click_button
end end
it "should send correct data for rails style checked fields" do it "should send correct data for rails style checked fields" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_tos" name="user[tos]" type="checkbox" value="1" checked="checked" /> <input id="user_tos" name="user[tos]" type="checkbox" value="1" checked="checked" />
<input name="user[tos]" type="hidden" value="0" /> TOS <input name="user[tos]" type="hidden" value="0" /> TOS
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"tos" => "1"})
webrat_session.should_receive(:get).with("/login", "user" => {"tos" => "1"}) @session.click_button
click_button
end end
it "should send default collection fields" do it "should send default collection fields" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="checkbox" name="options[]" value="burger" checked="checked" /> <input type="checkbox" name="options[]" value="burger" checked="checked" />
<input type="radio" name="options[]" value="fries" checked="checked" /> <input type="radio" name="options[]" value="fries" checked="checked" />
@ -296,56 +256,37 @@ describe "click_button" do
<input type="hidden" name="response[choices][][selected]" value="two" /> <input type="hidden" name="response[choices][][selected]" value="two" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login",
webrat_session.should_receive(:post).with("/login",
"options" => ["burger", "fries", "soda", "soda", "dessert"], "options" => ["burger", "fries", "soda", "soda", "dessert"],
"response" => { "choices" => [{"selected" => "one"}, {"selected" => "two"}, {"selected" => "two"}]}) "response" => { "choices" => [{"selected" => "one"}, {"selected" => "two"}, {"selected" => "two"}]})
click_button @session.click_button
end end
it "should not send default unchecked fields" do it "should not send default unchecked fields" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_tos" name="user[tos]" value="1" type="checkbox" /> <input id="user_tos" name="user[tos]" value="1" type="checkbox" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", {})
webrat_session.should_receive(:get).with("/login", {}) @session.click_button
click_button
end end
it "should send default textarea values" do it "should send default textarea values" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/posts"> <form method="post" action="/posts">
<textarea name="post[body]">Post body here!</textarea> <textarea name="post[body]">Post body here!</textarea>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/posts", "post" => {"body" => "Post body here!"})
webrat_session.should_receive(:post).with("/posts", "post" => {"body" => "Post body here!"}) @session.click_button
click_button
end end
it "should properly handle HTML entities in textarea default values" do
with_html <<-HTML
<html>
<form method="post" action="/posts">
<textarea name="post[body]">Peanut butter &amp; jelly</textarea>
<input type="submit" />
</form>
</html>
HTML
webrat_session.should_receive(:post).with("/posts", "post" => {"body" => "Peanut butter & jelly"})
click_button
end
it "should send default selected option value from select" do it "should send default selected option value from select" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<select name="month"> <select name="month">
<option value="1">January</option> <option value="1">January</option>
@ -353,15 +294,13 @@ describe "click_button" do
</select> </select>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "month" => "2")
webrat_session.should_receive(:get).with("/login", "month" => "2") @session.click_button
click_button
end end
it "should send default selected option inner html from select when no value attribute" do it "should send default selected option inner html from select when no value attribute" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<select name="month"> <select name="month">
<option>January</option> <option>January</option>
@ -369,15 +308,13 @@ describe "click_button" do
</select> </select>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "month" => "February")
webrat_session.should_receive(:get).with("/login", "month" => "February") @session.click_button
click_button
end end
it "should send first select option value when no option selected" do it "should send first select option value when no option selected" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<select name="month"> <select name="month">
<option value="1">January</option> <option value="1">January</option>
@ -385,110 +322,83 @@ describe "click_button" do
</select> </select>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "month" => "1")
webrat_session.should_receive(:get).with("/login", "month" => "1") @session.click_button
click_button
end end
it "should handle nested properties" do it "should handle nested properties" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="post" action="/login"> <form method="post" action="/login">
<input type="text" id="contestant_scores_12" name="contestant[scores][1]" value="2"/> <input type="text" id="contestant_scores_12" name="contestant[scores][1]" value="2"/>
<input type="text" id="contestant_scores_13" name="contestant[scores][3]" value="4"/> <input type="text" id="contestant_scores_13" name="contestant[scores][3]" value="4"/>
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:post).with("/login", "contestant" => {"scores" => {'1' => '2', '3' => '4'}})
webrat_session.should_receive(:post).with("/login", "contestant" => {"scores" => {'1' => '2', '3' => '4'}}) @session.click_button
click_button
end end
it "should send default empty text field values" do it "should send default empty text field values" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_email" name="user[email]" value="" type="text" /> <input id="user_email" name="user[email]" value="" type="text" />
<input type="submit" /> <input type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"email" => ""})
webrat_session.should_receive(:get).with("/login", "user" => {"email" => ""}) @session.click_button
click_button
end end
it "should recognize button tags" do it "should recognize button tags" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_email" name="user[email]" value="" type="text" /> <input id="user_email" name="user[email]" value="" type="text" />
<button type="submit" /> <button type="submit" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"email" => ""})
webrat_session.should_receive(:get).with("/login", "user" => {"email" => ""}) @session.click_button
click_button
end
it "should recognize input tags with the type button" do
with_html <<-HTML
<html>
<form action="/">
<input type="button" />
</form>
</html>
HTML
webrat_session.should_receive(:get)
click_button
end end
it "should recognize image button tags" do it "should recognize image button tags" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form action="/"> <form action="/">
<input type="image" /> <input type="image" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get)
webrat_session.should_receive(:get) @session.click_button
click_button
end end
it "should find buttons by their IDs" do it "should find buttons by their IDs" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form action="/"> <form action="/">
<input type="submit" id="my_button" /> <input type="submit" id="my_button" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get)
webrat_session.should_receive(:get) @session.click_button "my_button"
click_button "my_button"
end end
it "should find image buttons by their alt text" do it "should find image buttons by their alt text" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form action="/"> <form action="/">
<input type="image" alt="Go" /> <input type="image" alt="Go" />
</form> </form>
</html> EOS
HTML @session.should_receive(:get)
webrat_session.should_receive(:get) @session.click_button "Go"
click_button "Go"
end end
it "should recognize button tags by content" do it "should recognize button tags by content" do
with_html <<-HTML @session.response_body = <<-EOS
<html>
<form method="get" action="/login"> <form method="get" action="/login">
<input id="user_email" name="user[email]" value="" type="text" /> <input id="user_email" name="user[email]" value="" type="text" />
<button type="submit">Login</button> <button type="submit">Login</button>
</form> </form>
</html> EOS
HTML @session.should_receive(:get).with("/login", "user" => {"email" => ""})
webrat_session.should_receive(:get).with("/login", "user" => {"email" => ""}) @session.click_button "Login"
click_button "Login"
end end
end end

288
spec/api/click_link_spec.rb Normal file
View File

@ -0,0 +1,288 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "click_link" do
before do
@session = Webrat::TestSession.new
end
it "should use get by default" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_link "Link text"
end
it "should click get links" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_link "Link text", :method => :get
end
it "should click delete links" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:delete).with("/page", {})
@session.click_link "Link text", :method => :delete
end
it "should click post links" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:post).with("/page", {})
@session.click_link "Link text", :method => :post
end
it "should click put links" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:put).with("/page", {})
@session.click_link "Link text", :method => :put
end
it "should click links by regexp" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_link /link [a-z]/i
end
it "should click rails javascript links with authenticity tokens" do
@session.response_body = <<-EOS
<a href="/posts" onclick="var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
var s = document.createElement('input');
s.setAttribute('type', 'hidden');
s.setAttribute('name', 'authenticity_token');
s.setAttribute('value', 'aa79cb354597a60a3786e7e291ed4f74d77d3a62');
f.appendChild(s);
f.submit();
return false;">Posts</a>
EOS
@session.should_receive(:post).with("/posts", "authenticity_token" => "aa79cb354597a60a3786e7e291ed4f74d77d3a62")
@session.click_link "Posts"
end
it "should click rails javascript delete links" do
@session.response_body = <<-EOS
<a href="/posts/1" onclick="var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
var m = document.createElement('input');
m.setAttribute('type', 'hidden');
m.setAttribute('name', '_method');
m.setAttribute('value', 'delete');
f.appendChild(m);
f.submit();
return false;">Delete</a>
EOS
@session.should_receive(:delete).with("/posts/1", {})
@session.click_link "Delete"
end
it "should click rails javascript post links" do
@session.response_body = <<-EOS
<a href="/posts" onclick="var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
f.submit();
return false;">Posts</a>
EOS
@session.should_receive(:post).with("/posts", {})
@session.click_link "Posts"
end
it "should click rails javascript post links without javascript" do
@session.response_body = <<-EOS
<a href="/posts" onclick="var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
f.submit();
return false;">Posts</a>
EOS
@session.should_receive(:get).with("/posts", {})
@session.click_link "Posts", :javascript => false
end
it "should click rails javascript put links" do
@session.response_body = <<-EOS
<a href="/posts" onclick="var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
var m = document.createElement('input');
m.setAttribute('type', 'hidden');
m.setAttribute('name', '_method');
m.setAttribute('value', 'put');
f.appendChild(m);
f.submit();
return false;">Put</a></h2>
EOS
@session.should_receive(:put).with("/posts", {})
@session.click_link "Put"
end
it "should fail if the javascript link doesn't have a value for the _method input" do
@session.response_body = <<-EOS
<a href="/posts/1" onclick="var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = this.href;
var m = document.createElement('input');
m.setAttribute('type', 'hidden');
m.setAttribute('name', '_method');
f.appendChild(m);
f.submit();
return false;">Link</a>
EOS
lambda {
@session.click_link "Link"
}.should raise_error
end
it "should assert valid response" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.response_code = 501
lambda { @session.click_link "Link text" }.should raise_error
end
[200, 300, 400, 499].each do |status|
it "should consider the #{status} status code as success" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.response_code = status
lambda { @session.click_link "Link text" }.should_not raise_error
end
end
it "should fail is the link doesn't exist" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
lambda {
@session.click_link "Missing link"
}.should raise_error
end
it "should not be case sensitive" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_link "LINK TEXT"
end
it "should match link substrings" do
@session.response_body = <<-EOS
<a href="/page">This is some cool link text, isn't it?</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_link "Link text"
end
it "should work with elements in the link" do
@session.response_body = <<-EOS
<a href="/page"><span>Link text</span></a>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_link "Link text"
end
it "should match the first matching link" do
@session.response_body = <<-EOS
<a href="/page1">Link text</a>
<a href="/page2">Link text</a>
EOS
@session.should_receive(:get).with("/page1", {})
@session.click_link "Link text"
end
it "should choose the shortest link text match" do
@session.response_body = <<-EOS
<a href="/page1">Linkerama</a>
<a href="/page2">Link</a>
EOS
@session.should_receive(:get).with("/page2", {})
@session.click_link "Link"
end
it "should treat non-breaking spaces as spaces" do
@session.response_body = <<-EOS
<a href="/page1">This&nbsp;is&nbsp;a&nbsp;link</a>
EOS
@session.should_receive(:get).with("/page1", {})
@session.click_link "This is a link"
end
it "should click link within a selector" do
@session.response_body = <<-EOS
<a href="/page1">Link</a>
<div id="container">
<a href="/page2">Link</a>
</div>
EOS
@session.should_receive(:get).with("/page2", {})
@session.click_link_within "#container", "Link"
end
it "should not make request when link is local anchor" do
@session.response_body = <<-EOS
<a href="#section-1">Jump to Section 1</a>
EOS
# Don't know why @session.should_receive(:get).never doesn't work here
@session.should_receive(:send).with('get_via_redirect', '#section-1', {}).never
@session.click_link "Jump to Section 1"
end
it "should follow relative links" do
@session.stub!(:current_url).and_return("/page")
@session.response_body = <<-EOS
<a href="sub">Jump to sub page</a>
EOS
@session.should_receive(:get).with("/page/sub", {})
@session.click_link "Jump to sub page"
end
it "should follow fully qualified local links" do
@session.response_body = <<-EOS
<a href="http://www.example.com/page/sub">Jump to sub page</a>
EOS
@session.should_receive(:get).with("http://www.example.com/page/sub", {})
@session.click_link "Jump to sub page"
end
it "should follow query parameters" do
@session.stub!(:current_url).and_return("/page")
@session.response_body = <<-EOS
<a href="?foo=bar">Jump to foo bar</a>
EOS
@session.should_receive(:get).with("/page?foo=bar", {})
@session.click_link "Jump to foo bar"
end
end

View File

@ -1,58 +1,54 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper") require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "field_labeled" do describe "field_labeled" do
class << self class << self
def using_this_html html def using_this_html html
before(:each) do before(:each) do
with_html(html) @session = Webrat::TestSession.new
@session.response_body = html
end end
end end
def field_labeled(label) def field_labeled label
@label = label @label = label
yield yield
end end
def should_return_a type, opts def should_return_a type, opts
it "should return a textfield" do it "should return a textfield" do
field_labeled(opts[:for]).should be_an_instance_of(type) @session.field_labeled(opts[:for]).should be_an_instance_of(type)
end end
end end
def with_an_id_of id, opts def with_an_id_of id, opts
it "should return an element with the correct id" do it "should return an element with the correct id" do
field_labeled(opts[:for]).should match_id(id) @session.field_labeled(opts[:for]).should match_id(id)
end end
end end
def should_raise_error_matching regexp, opts def should_raise_error_matching regexp, opts
it "should raise with wrong label" do it "should raise with wrong label" do
lambda { lambda {
field_labeled(opts[:for]) @session.field_labeled(opts[:for])
}.should raise_error(regexp) }.should raise_error(regexp)
end end
end end
end end
def match_id(id) def match_id(id)
simple_matcher "element with id #{id.inspect}" do |element, matcher| simple_matcher "element with id #{id.inspect}" do |element, matcher|
if id.is_a?(Regexp) element.matches_id? id
element.id =~ id
else
element.id == id.to_s
end
end end
end end
describe "finding a text field" do describe "finding a text field" do
using_this_html <<-HTML using_this_html <<-HTML
<html>
<form> <form>
<label for="element_42">The Label</label> <label for="element_42">The Label</label>
<input type="text" id="element_42"> <input type="text" id="element_42">
</form> </form>
</html>
HTML HTML
should_return_a Webrat::TextField, :for => "The Label" should_return_a Webrat::TextField, :for => "The Label"
@ -62,12 +58,10 @@ describe "field_labeled" do
describe "finding a hidden field" do describe "finding a hidden field" do
using_this_html <<-HTML using_this_html <<-HTML
<html>
<form> <form>
<label for="element_42">The Label</label> <label for="element_42">The Label</label>
<input type="hidden" id="element_42"> <input type="hidden" id="element_42">
</form> </form>
</html>
HTML HTML
should_return_a Webrat::HiddenField, :for => "The Label" should_return_a Webrat::HiddenField, :for => "The Label"
@ -77,12 +71,10 @@ describe "field_labeled" do
describe "finding a checkbox" do describe "finding a checkbox" do
using_this_html <<-HTML using_this_html <<-HTML
<html>
<form> <form>
<label for="element_42">The Label</label> <label for="element_42">The Label</label>
<input type="checkbox" id="element_42"> <input type="checkbox" id="element_42">
</form> </form>
</html>
HTML HTML
should_return_a Webrat::CheckboxField, :for => "The Label" should_return_a Webrat::CheckboxField, :for => "The Label"
@ -92,12 +84,10 @@ describe "field_labeled" do
describe "finding a radio button" do describe "finding a radio button" do
using_this_html <<-HTML using_this_html <<-HTML
<html>
<form> <form>
<label for="element_42">The Label</label> <label for="element_42">The Label</label>
<input type="radio" id="element_42"> <input type="radio" id="element_42">
</form> </form>
</html>
HTML HTML
should_return_a Webrat::RadioField, :for => "The Label" should_return_a Webrat::RadioField, :for => "The Label"
@ -108,65 +98,14 @@ describe "field_labeled" do
describe "finding a text area" do describe "finding a text area" do
using_this_html <<-HTML using_this_html <<-HTML
<html>
<form> <form>
<label for="element_42">The Label</label> <label for="element_42">The Label</label>
<textarea id="element_42"></textarea> <textarea id="element_42"></textarea>
</form> </form>
</html>
HTML HTML
should_return_a Webrat::TextareaField, :for => "The Label" should_return_a Webrat::TextareaField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label" with_an_id_of "element_42", :for => "The Label"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label" should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end end
describe "finding a field with it's label containing newlines" do
using_this_html <<-HTML
<html>
<form>
<label for="element_42">
A label with
<a>a link on it's own line</a>
</label>
<input type="text" id="element_42">
</form>
</html>
HTML
should_return_a Webrat::TextField, :for => "A label with a link on it's own line"
with_an_id_of "element_42", :for => "A label with a link on it's own line"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end
describe "finding a field when labels without fields also match" do
using_this_html <<-HTML
<html>
<label>The Label</label>
<form>
<label for="element_42">The Label</label>
<input type="text" id="element_42">
</form>
</html>
HTML
should_return_a Webrat::TextField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label"
end
describe "finding a field whose label ends with an non word character" do
using_this_html <<-HTML
<html>
<form>
<label for="element_42">License #</label>
<input type="text" id="element_42">
</form>
</html>
HTML
should_return_a Webrat::TextField, :for => "License #"
with_an_id_of "element_42", :for => "License #"
should_raise_error_matching /Could not find .* "Other License #"/, :for => "Other License #"
end
end end

Some files were not shown because too many files have changed in this diff Show More