Merge branch 'brynary/master'

This commit is contained in:
Ben Mabey 2008-11-13 15:05:56 -07:00
commit b8a87e3143
82 changed files with 2677 additions and 862 deletions

3
.gitignore vendored
View File

@ -1,5 +1,6 @@
coverage
.DS_Store
pkg
doc
ri
email.txt
email.txt

View File

@ -1,38 +1,82 @@
== Trunk
* Minor enhancements
* Raise Webrat::PageLoadError when a failure occurs so that application exceptions can be more accurately tested (Ryan Briones)
== 0.3.2 / 2008-11-08
* Minor enhancements
* 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)
== 0.3.1 / 2008-11-07
* Minor enhancements
* Use @_webrat_session instance variable instead of @session for Merb integration to avoid collisions
== 0.3.0 / 2008-11-07
* Major enhancements
* Added #within for manipulating the current page within a selector scope
* Add support for simulating SSL requests (Luke Melia)
* Add support for file fields via #attaches_file method (Patch from Kyle Hargraves)
* Support relative links, including href="?foo=bar" (Patch from Kyle Hargraves)
* Separated Rails-specific code from the Webrat core to make it easier to use Webrat with other environments
* Alias visits as visit, clicks_link as click_link, etc. for better readability
* Added Merb support (Gwyn Morfey, Jeremy Burks, Rob Kaufman, Yehuda Katz)
* Added experimental Selenium support (Luke Melia)
* Add have_selector, have_xpath, have_tag and contain matchers from Merb
* Switch from Hpricot to Nokogiri for XML parsing (thanks, Aaron Patterson)
* Minor enhancements
* Add support for redirect_to :back by sending HTTP_REFERER headers (Patch from Hendrik Volkmer)
* 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) (Kyle Hargraves)
* Add support for simulating SSL requests (Luke Melia)
* Added #basic_auth(user, pass) to support HTTP Basic Auth (Aslak Hellesøy)
* Added support for Sinatra and Rack (Aslak Hellesøy)
* Rename visits to visit, fills_in to fill_in, etc.
* Add #field_labeled for looking up form fields by label (David Chelimsky)
* Add #field_named and #field_with_id locators
* Don't depend on hoe anymore
* Return responses after sending requests
* Allow clicking links and buttons by a regular expression in Selenium (Luke Melia)
* Allow clicking links by a regular expression
* Add #http_accept for including MIME type HTTP "Accept" 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 (David Leal)
* Add Webrat.root method for cross-framework support (Krzysztof Zylawy)
* Add support for clicking areas of an image map (Alex Lang)
* Support relative links, including href="?foo=bar" (Kyle Hargraves)
* Separated Rails-specific code from the Webrat core to make it easier to use Webrat with other environments
* 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)
* Don't send disabled form elements to the server (Nicholas A. Evans)
* Display response body when the page load is not successful (David Leal)
* CGI escape form field values (Miha Filej)
* Add support for redirect_to :back by sending HTTP_REFERER headers (Hendrik Volkmer)
* Expose current DOM (as an Hpricot object) as #current_dom
* Add support for disabling JavaScript when clicking a link to enable testing of both JS
and non-JS implementations (Luke Melia and Bryan Helmkamp)
* Support &nbsp's as spaces in matching link text (Patch from Luke Melia)
* Support button elements (Patch from Nick Sieger)
* Support matching select options by regexp (Patch from Kyle Hargraves)
* Support &nbsp's as spaces in matching link text (Luke Melia)
* Support button elements (Nick Sieger)
* Support matching select options by regexp (Kyle Hargraves)
* save_and_open_page rewrites css and image references to provide a friendlier debugging experience (Luke Melia)
* Added support for matching alt attributes in fields (primarly for clicks_button) (Patch from Aaron Quint)
* Support '&' in submitted values (Patch from Kyle Hargraves)
* Support clicking links by title (Patch from Dan Barry)
* Added missing spec for clicking image buttons (Patch from Tim Harper)
* Added support for matching alt attributes in fields (primarly for clicks_button) (Aaron Quint)
* Support '&' in submitted values (Kyle Hargraves)
* Support clicking links by title (Dan Barry)
* Added missing spec for clicking image buttons (Tim Harper)
* Switched tests to specs, and from Mocha to RSpec's mocking library
* Add support to click_button for IDs (Gwyn Morfey)
* Miscellaneous core refactorings (Jan Suchal)
* Bug fixes
* Don't open blank pages in the browser (Patch from Kyle Hargraves)
* Support radio buttons with multiple labels (Patch from Dan Barry)
* Fix load order bug on some platforms (Patch from Ismael Celis)
* Fix bug with empty select list option (Patch from Kyle Hargraves)
* Fix initialization of WWW::Mechanize (Derek Kastner)
* Don't open blank pages in the browser (Kyle Hargraves)
* Support radio buttons with multiple labels (Dan Barry)
* Fix load order bug on some platforms (Ismael Celis)
* Fix bug with empty select list option (Kyle Hargraves)
* Fix regression of not sending default values in password fields
* 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 incorrect field value (Luke Melia)
== 0.2.0 / 2008-04-04
@ -42,29 +86,29 @@
* Add radio button support via #chooses method
* Add basic support for Rails-generated JavaScript link tags
* Add support for checkboxes (Patches from Kyle Hargraves and Jarkko Laine)
* Add support for textarea fields (Patch from Sacha Schlegel)
* Add support for textarea fields (Sacha Schlegel)
* 8 Minor enhancements
* Added reloads method to reload the page (Patch from Kamal Fariz Mahyuddi)
* Prevent making a request if clicking on local anchor link (Patch from 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)
* Added clicks_link_within(selector, link_text), allowing restricting link search
to within a given css selector (Patch from Luke Melia)
* Allow specifying the input name/label when doing a select (Patch from David Chelimsky)
* Raise a specific exception if the developer tries to manipulate form elements before loading a page (Patch from James Deville)
* Add support for alternate POST, PUT and DELETE link clicking (Patch from Kyle Hargraves)
* Change clicks_link to find the shortest matching link (Patch from Luke Melia)
to within a given css selector (Luke Melia)
* Allow specifying the input name/label when doing a select (David Chelimsky)
* Raise a specific exception if the developer tries to manipulate form elements before loading a page (James Deville)
* Add support for alternate POST, PUT and DELETE link clicking (Kyle Hargraves)
* Change clicks_link to find the shortest matching link (Luke Melia)
* Improve matching for labels in potentially ambiguous cases
* 7 Bug fixes
* Fix incorrect serializing of collection inputs, i.e. name contains [] (Patch from Kamal Fariz Mahyuddi)
* Serialize empty text field values just like browsers (Patch from Kamal Fariz Mahyuddi)
* Quick fix to avoid @dom not initialized warnings (Patch from Kamal Fariz Mahyuddi)
* Docfix: bad reference to #select method in README (Patch from Luke Melia)
* Fix incorrect serializing of collection inputs, i.e. name contains [] (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)
* Docfix: bad reference to #select method in README (Luke Melia)
* Ensure Rails-style checkboxes work properly (checkboxes followed by a hidden input with the same name)
* Fix Edge Rails (a.k.a. 2.0 RC) compatibility (Patch from David Chelimsky)
* Support param hashes nested more than one level (Patch from David Chelimsky)
* Fix Edge Rails (a.k.a. 2.0 RC) compatibility (David Chelimsky)
* Support param hashes nested more than one level (David Chelimsky)
== 0.1.0 / 2007-11-28

View File

@ -1,41 +0,0 @@
.gitignore
History.txt
MIT-LICENSE.txt
Manifest.txt
README.txt
Rakefile
TODO.txt
coverage/index.html
coverage/lib-webrat-field_rb.html
coverage/lib-webrat-form_rb.html
coverage/lib-webrat-label_rb.html
coverage/lib-webrat-link_rb.html
coverage/lib-webrat-logging_rb.html
coverage/lib-webrat-page_rb.html
coverage/lib-webrat-redirect_actions_rb.html
coverage/lib-webrat-select_option_rb.html
coverage/lib-webrat_rb.html
init.rb
install.rb
lib/webrat.rb
lib/webrat/field.rb
lib/webrat/form.rb
lib/webrat/label.rb
lib/webrat/link.rb
lib/webrat/logging.rb
lib/webrat/page.rb
lib/webrat/redirect_actions.rb
lib/webrat/select_option.rb
spec/attaches_file_spec.rb
spec/checks_spec.rb
spec/chooses_spec.rb
spec/clicks_button_spec.rb
spec/clicks_link_spec.rb
spec/fills_in_spec.rb
spec/rcov.opts
spec/reloads_spec.rb
spec/save_and_open_page_spec.rb
spec/selects_spec.rb
spec/spec.opts
spec/spec_helper.rb
spec/visits_spec.rb

View File

@ -1,11 +1,9 @@
Webrat
======
=== Webrat
- [Code on GitHub](http://github.com/brynary/webrat)
- [Tickets on Lighthouse](http://webrat.lighthouseapp.com/)
Description
-----------
=== Description
Webrat (_Ruby Acceptance Testing for Web applications_)
lets you quickly write robust and thorough acceptance tests for a Ruby
@ -23,15 +21,14 @@ run your tests much faster and more frequently.
Initial development was sponsored by [EastMedia](http://www.eastmedia.com).
Synopsis
--------
=== Synopsis
def test_sign_up
visits "/"
clicks_link "Sign up"
fills_in "Email", :with => "good@example.com"
selects "Free account"
clicks_button "Register"
visit "/"
click_link "Sign up"
fill_in "Email", :with => "good@example.com"
select "Free account"
click_button "Register"
...
end
@ -62,8 +59,14 @@ tests to break unnecessarily as your application evolves:
A test written with Webrat can handle these changes to these without any modifications.
Install
-------
=== 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:
@ -75,23 +78,13 @@ In your stories/helper.rb:
You could also unpack the gem into vendor/plugins.
Requirements
------------
- Rails >= 1.2.6
- Hpricot >= 0.6
- Rails integration tests in Test::Unit _or_
- RSpec stories (using an RSpec version >= revision 2997)
Authors
-------
=== 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
-------
=== License
Copyright (c) 2007 Bryan Helmkamp, Seth Fitzsimmons.
See MIT-LICENSE.txt in this directory.

View File

@ -1,44 +1,53 @@
require 'rubygems'
require 'hoe'
require "rake/gempackagetask"
require 'rake/rdoctask'
require "rake/clean"
require 'spec'
require 'spec/rake/spectask'
require 'spec/rake/verify_rcov'
require './lib/webrat.rb'
Hoe.new('webrat', Webrat::VERSION) do |p|
p.rubyforge_name = 'webrat'
p.summary = 'Ruby Acceptance Testing for Web applications'
p.developer "Bryan Helmkamp", "bryan@brynary.com"
p.developer "Seth Fitzsimmons", "seth@mojodna.net"
p.description = p.paragraphs_of('README.txt', 4..6).join("\n\n")
p.url = p.paragraphs_of('README.txt', 1).first.split("\n").first.strip
p.changes = p.paragraphs_of('History.txt', 0..3).join("\n\n")
##############################################################################
# Package && release
##############################################################################
spec = Gem::Specification.new do |s|
s.name = "webrat"
s.version = Webrat::VERSION
s.platform = Gem::Platform::RUBY
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/**/*"]
p.extra_deps << ["hpricot", ">= 0.6"]
p.remote_rdoc_dir = '' # Release to root
# rdoc
s.has_rdoc = true
s.extra_rdoc_files = %w(README.txt MIT-LICENSE.txt)
# Dependencies
s.add_dependency "nokogiri", ">= 1.0.3"
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
Rake::TaskManager.class_eval do
def remove_task(task_name)
@tasks.delete(task_name.to_s)
end
end
def remove_task(task_name)
Rake.application.remove_task(task_name)
end
remove_task "test"
remove_task "test_deps"
desc "Run all specs in spec directory"
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']
@ -54,12 +63,27 @@ Spec::Rake::SpecTask.new(:rcov) do |t|
end
end
require 'spec/rake/verify_rcov'
RCov::VerifyTask.new(:verify_rcov => :rcov) do |t|
t.threshold = 97.1 # Make sure you have rcov 0.7 or higher!
t.threshold = 96.2 # Make sure you have rcov 0.7 or higher!
end
remove_task "default"
task :default do
Rake::Task["verify_rcov"].invoke
end
desc 'Install the package as a gem.'
task :install_gem => [:clean, :package] do
gem = Dir['pkg/*.gem'].first
sh "sudo gem install --local #{gem}"
end
Rake::RDocTask.new(:docs) do |rd|
rd.main = "README.txt"
rd.rdoc_dir = 'doc'
files = spec.files.grep(/^(lib|bin|ext)|txt$/)
files -= ["TODO.txt"]
files -= files.grep(/\.js$/)
rd.rdoc_files = files.uniq
title = "webrat-#{Webrat::VERSION} Documentation"
rd.options << "-t #{title}"
end

View File

@ -1,5 +1,10 @@
Restore SSL support for Rails (See 73d3b72108254c0f1ad00e63f8e712115cc8ca7c)
Full support for multiple forms on a page
Track the current form based on the location of the last manipulated input, use this as a default for clicks_button
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
Support for a hash mapping page names to page URLs
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

View File

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

View File

@ -1,9 +1,34 @@
require "rubygems"
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
module Webrat
VERSION = '0.2.1'
VERSION = '0.3.2'
def self.root #:nodoc:
defined?(RAILS_ROOT) ? RAILS_ROOT : Merb.root
end
class WebratError < StandardError
end
end
require "rubygems"
require "active_support"
# We need Nokogiri's CSS to XPath support, even if using REXML
require "nokogiri/css"
require File.dirname(__FILE__) + "/webrat/core"
require File.dirname(__FILE__) + "/webrat/rails" if defined?(RAILS_ENV)
# Require nokogiri and fall back on rexml
begin
require "nokogiri"
require "webrat/core/nokogiri"
rescue LoadError => e
require "rexml/document"
warn("Standard REXML library is slow. Please consider installing nokogiri.\nUse \"sudo gem install nokogiri\"")
end
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,3 +1,12 @@
Dir[File.join(File.dirname(__FILE__), "core", "*.rb")].sort.each do |file|
require File.expand_path(file)
end
require "webrat/core/nokogiri"
require "webrat/core/logging"
require "webrat/core/flunk"
require "webrat/core/form"
require "webrat/core/scope"
require "webrat/core/link"
require "webrat/core/area"
require "webrat/core/label"
require "webrat/core/select_option"
require "webrat/core/session"
require "webrat/core/methods"
require "webrat/core/matchers"

44
lib/webrat/core/area.rb Normal file
View File

@ -0,0 +1,44 @@
module Webrat
class Area #:nodoc:
def initialize(session, element)
@session = session
@element = element
end
def click(method = nil, options = {})
@session.request_page(absolute_href, :get, {})
end
def matches_text?(id_or_title)
matcher = /#{Regexp.escape(id_or_title.to_s)}/i
title =~ matcher || id =~ matcher
end
protected
def href
@element["href"]
end
def title
@element["title"]
end
def id
@element["id"]
end
def absolute_href
if href =~ /^\?/
"#{@session.current_url}#{href}"
elsif href !~ %r{^https?://[\w|.]+(/.*)} && (href !~ /^\//)
"#{@session.current_url}/#{href}"
else
href
end
end
end
end

View File

@ -1,20 +1,23 @@
require "cgi"
require "webrat/core_extensions/blank"
require "webrat/core_extensions/nil_to_param"
module Webrat
class Field
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"
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}"
raise "Invalid field element: #{element.inspect}"
end
def initialize(form, element)
@ -45,10 +48,27 @@ module Webrat
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
value = @value.to_s.gsub('&', '%26')
param_parser.parse_query_parameters("#{name}=#{value}")
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)
@ -69,6 +89,10 @@ module Webrat
@element["name"]
end
def escaped_value
CGI.escape(@value.to_s)
end
def labels
@labels ||= label_elements.map { |element| Label.new(self, element) }
end
@ -87,7 +111,7 @@ module Webrat
end
unless id.blank?
@label_elements += @form.element / "label[@for=#{id}]"
@label_elements += @form.element.search("label[@for='#{id}']")
end
@label_elements
@ -97,16 +121,6 @@ module Webrat
@element["value"]
end
def param_parser
if defined?(CGIMethods)
CGIMethods
else
require "action_controller"
require "action_controller/integration"
ActionController::AbstractRequest
end
end
def replace_param_value(params, oval, nval)
output = Hash.new
params.each do |key, value|
@ -124,12 +138,12 @@ module Webrat
end
end
class ButtonField < Field
class ButtonField < Field #:nodoc:
def matches_text?(text)
@element.innerHTML =~ /#{Regexp.escape(text.to_s)}/i
@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
@ -144,21 +158,22 @@ module Webrat
end
def click
raise_error_if_disabled
set(@element["value"]) unless @element["name"].blank?
@form.submit
end
end
class HiddenField < Field
class HiddenField < Field #:nodoc:
def to_param
if collection_name?
super
else
checkbox_with_same_name = @form.find_field(name, CheckboxField)
checkbox_with_same_name = @form.field(name, CheckboxField)
if checkbox_with_same_name.to_param.nil?
if checkbox_with_same_name.to_param.blank?
super
else
nil
@ -174,7 +189,7 @@ module Webrat
end
class CheckboxField < Field
class CheckboxField < Field #:nodoc:
def to_param
return nil if @value.nil?
@ -182,10 +197,16 @@ module Webrat
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
@ -201,10 +222,10 @@ module Webrat
end
class PasswordField < Field
class PasswordField < Field #:nodoc:
end
class RadioField < Field
class RadioField < Field #:nodoc:
def to_param
return nil if @value.nil?
@ -212,6 +233,7 @@ module Webrat
end
def choose
raise_error_if_disabled
other_options.each do |option|
option.set(nil)
end
@ -235,7 +257,7 @@ module Webrat
end
class TextareaField < Field
class TextareaField < Field #:nodoc:
protected
@ -245,7 +267,7 @@ module Webrat
end
class FileField < Field
class FileField < Field #:nodoc:
attr_accessor :content_type
@ -274,13 +296,13 @@ module Webrat
end
class TextField < Field
class TextField < Field #:nodoc:
end
class ResetField < Field
class ResetField < Field #:nodoc:
end
class SelectField < Field
class SelectField < Field #:nodoc:
def find_option(text)
options.detect { |o| o.matches_text?(text) }
@ -289,11 +311,12 @@ module Webrat
protected
def default_value
selected_options = @element / "option[@selected='selected']"
selected_options = @element / "option:first" if selected_options.empty?
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.innerHTML
option["value"] || option.inner_html
end
end
@ -302,7 +325,7 @@ module Webrat
end
def option_elements
(@element / "option")
@element.search(".//option")
end
end

View File

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

View File

@ -1,5 +1,8 @@
require "webrat/core/field"
require "webrat/core_extensions/blank"
module Webrat
class Form
class Form #:nodoc:
attr_reader :element
def initialize(session, element)
@ -8,12 +11,10 @@ module Webrat
@fields = nil
end
def find_field(id_or_name_or_label, *field_types)
possible_fields = fields_by_type(field_types)
find_field_by_id(possible_fields, id_or_name_or_label) ||
find_field_by_name(possible_fields, id_or_name_or_label) ||
find_field_by_label(possible_fields, id_or_name_or_label) ||
def field(locator, *field_types)
field_with_id(locator, *field_types) ||
field_named(locator, *field_types) ||
field_labeled(locator, *field_types) ||
nil
end
@ -29,63 +30,50 @@ module Webrat
end
def find_button(value = nil)
return fields_by_type([ButtonField]).first if value.nil?
return fields_by_type([ButtonField]).first if value.nil?
possible_buttons = fields_by_type([ButtonField])
possible_buttons.each do |possible_button|
return possible_button if possible_button.matches_value?(value)
end
nil
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 / "button, input, textarea, select").each do |field_element|
@fields << Field.class_for_element(field_element).new(self, field_element)
@fields = (@element.search(".//button", ".//input", ".//textarea", ".//select")).collect do |field_element|
Field.class_for_element(field_element).new(self, field_element)
end
@fields
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 find_field_by_id(possible_fields, id)
possible_fields.each do |possible_field|
return possible_field if possible_field.matches_id?(id)
end
nil
end
def find_field_by_name(possible_fields, name)
possible_fields.each do |possible_field|
return possible_field if possible_field.matches_name?(name)
end
nil
end
def find_field_by_label(possible_fields, label)
matching_fields = []
possible_fields.each do |possible_field|
matching_fields << possible_field if possible_field.matches_label?(label)
end
matching_fields.sort_by { |f| f.label_text.length }.first
end
def fields_by_type(field_types)
fields.select { |f| field_types.include?(f.class) }
if field_types.any?
fields.select { |f| field_types.include?(f.class) }
else
fields
end
end
def params
@ -106,11 +94,13 @@ module Webrat
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, HashWithIndifferentAccess
when *HASH
merge_hash_values(all_params[key], value)
when Array
all_params[key] += value
@ -123,7 +113,7 @@ module Webrat
def merge_hash_values(a, b) # :nodoc:
a.keys.each do |k|
if b.has_key?(k)
case [a[k], b[k]].map(&:class)
case [a[k], b[k]].map{|value| value.class}
when [Hash, Hash]
a[k] = merge_hash_values(a[k], b[k])
b.delete(k)

View File

@ -1,5 +1,5 @@
module Webrat
class Label
class Label #:nodoc:
def initialize(field, element)
@field = field
@ -11,7 +11,7 @@ module Webrat
end
def text
@element.innerText
@element.inner_text
end
end

View File

@ -1,13 +1,15 @@
require "webrat/core_extensions/blank"
module Webrat
class Link
class Link #:nodoc:
def initialize(session, element)
@session = session
@element = element
end
def click(method = nil, options = {})
method ||= http_method
def click(options = {})
method = options[:method] || http_method
return if href =~ /^#/ && method == :get
options[:javascript] = true if options[:javascript].nil?
@ -20,13 +22,19 @@ module Webrat
end
def matches_text?(link_text)
html = text.gsub('&nbsp;',' ')
matcher = /#{Regexp.escape(link_text.to_s)}/i
html = text.gsub('&#xA0;',' ')
if link_text.is_a?(Regexp)
matcher = link_text
else
matcher = /#{Regexp.escape(link_text.to_s)}/i
end
html =~ matcher || title =~ matcher
end
def text
@element.innerHTML
@element.inner_html
end
protected

View File

@ -0,0 +1,92 @@
require "webrat/core_extensions/detect_mapped"
module Webrat
module Locators
def field(*args)
# 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
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

View File

@ -1,14 +1,21 @@
module Webrat
module Logging
module Logging #:nodoc:
def warn_log(message) # :nodoc:
return unless logger
logger.warn message
end
def debug_log(message) # :nodoc:
return unless logger
logger.debug(message)
logger.debug message
end
def logger # :nodoc:
if defined? RAILS_DEFAULT_LOGGER
RAILS_DEFAULT_LOGGER
elsif defined? Merb
Merb.logger
else
nil
end

View File

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

View File

@ -0,0 +1,94 @@
module Webrat
module Matchers
class HasContent #:nodoc:
def initialize(content)
@content = content
end
def matches?(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
case @content
when String
@element.include?(@content)
when Regexp
@element.match(@content)
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
# String:: The failure message.
def failure_message
"expected the following element's content to #{content_message}:\n#{@element}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected the following element's content to not #{content_message}:\n#{@element}"
end
def content_message
case @content
when String
"include \"#{@content}\""
when Regexp
"match #{@content.inspect}"
end
end
end
# Matches the contents of an HTML document with
# whatever string is supplied
#
# ---
# @api public
def contain(content)
HasContent.new(content)
end
end
end

View File

@ -0,0 +1,39 @@
module Webrat
module Matchers
class HaveSelector < HaveXpath #:nodoc:
# ==== 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
def query
Nokogiri::CSS::Parser.parse(*super).map { |ast| ast.to_xpath }
end
end
# Matches HTML content against a CSS 3 selector.
#
# ==== Parameters
# expected<String>:: The CSS selector to look for.
#
# ==== Returns
# HaveSelector:: A new have selector matcher.
# ---
# @api public
def have_selector(expected, &block)
HaveSelector.new(expected, &block)
end
alias_method :match_selector, :have_selector
end
end

View File

@ -0,0 +1,58 @@
module Webrat
module HaveTagMatcher
class HaveTag < ::Webrat::Matchers::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::Parser.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
end
end

View File

@ -0,0 +1,85 @@
require "webrat/core/nokogiri"
require "webrat/core/rexml"
module Webrat
module Matchers
class HaveXpath #:nodoc:
def initialize(expected, &block)
@expected = expected
@block = block
end
def matches?(stringlike)
if defined?(Nokogiri::XML)
matches_nokogiri?(stringlike)
else
matches_rexml?(stringlike)
end
end
def matches_rexml?(stringlike)
if REXML::Node === stringlike || Array === stringlike
@query = query.map { |q| q.gsub(%r'//', './') }
else
@query = query
end
@document = Webrat.rexml_document(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
def matches_nokogiri?(stringlike)
if Nokogiri::XML::NodeSet === stringlike
@query = query.map { |q| q.gsub(%r'//', './') }
else
@query = query
end
@document = Webrat.nokogiri_document(stringlike)
matched = @document.xpath(*@query)
matched.any? && (!@block || @block.call(matched))
end
def query
[@expected].flatten.compact
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
# Matches HTML content against an XPath query
#
# ==== Parameters
# expected<String>:: The XPath query to look for.
#
# ==== Returns
# HaveXpath:: A new have xpath matcher.
# ---
# @api public
def have_xpath(expected, &block)
HaveXpath.new(expected, &block)
end
alias_method :match_xpath, :have_xpath
end
end

View File

@ -0,0 +1,44 @@
module Webrat
module Methods #:nodoc:
def self.delegate_to_session(*meths)
meths.each do |meth|
self.class_eval <<-RUBY
def #{meth}(*args, &blk)
@_webrat_session ||= ::Webrat::MerbSession.new
@_webrat_session.#{meth}(*args, &blk)
end
RUBY
end
end
# all of these methods delegate to the @session, which should
# be created transparently.
#
# Note that when using Webrat, #request also uses @session, so
# that #request and webrat native functions behave interchangably
delegate_to_session \
:visits, :visit,
:within,
:header, :http_accept, :basic_auth,
:save_and_open_page,
:fill_in,
:check,
:uncheck,
:choose,
:select,
:attach_file,
:cookies,
:response,
:current_page,
:current_url,
:click_link,
:click_area,
:click_button,
:reload, :reloads,
:clicks_link_within,
:field_labeled
end
end

29
lib/webrat/core/mime.rb Normal file
View File

@ -0,0 +1,29 @@
module Webrat
module MIME
def self.mime_type(string_or_symbol)
if string_or_symbol.is_a?(String)
string_or_symbol
else
case string_or_symbol
when :text then "text/plain"
when :html then "text/html"
when :js then "text/javascript"
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
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

24
lib/webrat/core/rexml.rb Normal file
View File

@ -0,0 +1,24 @@
module Webrat
def self.rexml_document(stringlike)
stringlike = stringlike.body.to_s if stringlike.respond_to?(:body)
case stringlike
when REXML::Document
stringlike.root
when REXML::Node, Array
stringlike
else
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
end

View File

@ -1,64 +1,81 @@
require "hpricot"
require "webrat/core/form"
require "webrat/core/locators"
module Webrat
class Scope
include Logging
include Flunk
include Locators
def initialize(session, html, selector = nil)
@session = session
@html = html
@selector = selector
def self.from_page(session, response, response_body) #:nodoc:
new(session) do
@response = response
@response_body = response_body
end
end
def self.from_scope(session, scope, selector) #:nodoc:
new(session) do
@scope = scope
@selector = selector
end
end
def initialize(session, &block) #:nodoc:
@session = session
instance_eval(&block) if block_given?
end
# 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.
#
# Examples:
# fills_in "Email", :with => "user@example.com"
# fills_in "user[email]", :with => "user@example.com"
# fill_in "Email", :with => "user@example.com"
# fill_in "user[email]", :with => "user@example.com"
#
# The field value is required, and must be specified in <tt>options[:with]</tt>.
# <tt>field</tt> can be either the value of a name attribute (i.e. <tt>user[email]</tt>)
# or the text inside a <tt><label></tt> element that points at the <tt><input></tt> field.
def fills_in(id_or_name_or_label, options = {})
find_field(id_or_name_or_label, TextField, TextareaField, PasswordField).set(options[:with])
def fill_in(field_locator, options = {})
field = locate_field(field_locator, TextField, TextareaField, PasswordField)
field.raise_error_if_disabled
field.set(options[:with])
end
alias_method :fill_in, :fills_in
alias_method :fills_in, :fill_in
# 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.
#
# Example:
# checks 'Remember Me'
def checks(id_or_name_or_label)
find_field(id_or_name_or_label, CheckboxField).check
# check 'Remember Me'
def check(field_locator)
locate_field(field_locator, CheckboxField).check
end
alias_method :check, :checks
alias_method :checks, :check
# 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.
#
# Example:
# unchecks 'Remember Me'
def unchecks(id_or_name_or_label)
find_field(id_or_name_or_label, CheckboxField).uncheck
# uncheck 'Remember Me'
def uncheck(field_locator)
locate_field(field_locator, CheckboxField).uncheck
end
alias_method :uncheck, :unchecks
alias_method :unchecks, :uncheck
# 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.
#
# Example:
# chooses 'First Option'
def chooses(label)
find_field(label, RadioField).choose
# choose 'First Option'
def choose(field_locator)
locate_field(field_locator, RadioField).choose
end
alias_method :choose, :chooses
alias_method :chooses, :choose
# 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
@ -82,93 +99,64 @@ module Webrat
# Example:
# attaches_file "Resume", "/path/to/the/resume.txt"
# attaches_file "Photo", "/path/to/the/image.png", "image/png"
def attaches_file(id_or_name_or_label, path, content_type = nil)
find_field(id_or_name_or_label, FileField).set(path, content_type)
def attach_file(field_locator, path, content_type = nil)
locate_field(field_locator, FileField).set(path, content_type)
end
alias_method :attach_file, :attaches_file
alias_method :attaches_file, :attach_file
def click_area(area_name)
find_area(area_name).click
end
alias_method :clicks_area, :click_area
# 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.
#
# clicks_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
# CSRF authenticity tokens if they are present.
#
# Javascript imitation can be disabled by passing the option :javascript => false
#
# Passing a :method in the options hash overrides the HTTP method used
# for making the link request
#
# Example:
# clicks_link "Sign up"
# click_link "Sign up"
#
# clicks_link "Sign up", :javascript => false
def clicks_link(link_text, options = {})
find_link(link_text).click(nil, options)
end
alias_method :click_link, :clicks_link
# Works like clicks_link, but forces a GET request
# click_link "Sign up", :javascript => false
#
# Example:
# clicks_get_link "Log out"
def clicks_get_link(link_text)
find_link(link_text).click(:get)
# click_link "Sign up", :method => :put
def click_link(link_text, options = {})
find_link(link_text).click(options)
end
alias_method :click_get_link, :clicks_get_link
# Works like clicks_link, but issues a DELETE request instead of a GET
#
# Example:
# clicks_delete_link "Log out"
def clicks_delete_link(link_text)
find_link(link_text).click(:delete)
end
alias_method :click_delete_link, :clicks_delete_link
# Works like clicks_link, but issues a POST request instead of a GET
#
# Example:
# clicks_post_link "Vote"
def clicks_post_link(link_text)
find_link(link_text).click(:post)
end
alias_method :click_post_link, :clicks_post_link
# Works like clicks_link, but issues a PUT request instead of a GET
#
# Example:
# clicks_put_link "Update profile"
def clicks_put_link(link_text)
find_link(link_text).click(:put)
end
alias_method :click_put_link, :clicks_put_link
alias_method :clicks_link, :click_link
# Verifies that a submit button exists for the form, then submits the form, follows
# any redirects, and verifies the final page was successful.
#
# Example:
# clicks_button "Login"
# clicks_button
# click_button "Login"
# click_button
#
# The URL and HTTP method for the form submission are automatically read from the
# <tt>action</tt> and <tt>method</tt> attributes of the <tt><form></tt> element.
def clicks_button(value = nil)
def click_button(value = nil)
find_button(value).click
end
alias_method :click_button, :clicks_button
alias_method :clicks_button, :click_button
def dom # :nodoc:
return @dom if defined?(@dom) && @dom
@dom = Hpricot(@html)
return @dom if @dom
if @selector
html = (@dom / @selector).first.to_html
@dom = Hpricot(html)
@dom = scoped_dom
else
@dom = page_dom
end
return @dom
@ -176,62 +164,41 @@ module Webrat
protected
def find_select_option(option_text, id_or_name_or_label)
if id_or_name_or_label
field = find_field(id_or_name_or_label, SelectField)
return field.find_option(option_text)
def page_dom #:nodoc:
return @response.dom if @response.respond_to?(:dom)
dom = Webrat.nokogiri_document(@response_body)
Webrat.define_dom_method(@response, dom)
return dom
end
def scoped_dom #:nodoc:
Webrat.nokogiri_document(@scope.dom.search(@selector).first.to_html)
end
def locate_field(field_locator, *field_types) #:nodoc:
if field_locator.is_a?(Field)
field_locator
else
forms.each do |form|
result = form.find_select_option(option_text)
return result if result
end
end
flunk("Could not find option #{option_text.inspect}")
end
def find_button(value)
forms.each do |form|
button = form.find_button(value)
return button if button
end
flunk("Could not find button #{value.inspect}")
end
def find_link(text, selector = nil)
matching_links = []
links_within(selector).each do |possible_link|
matching_links << possible_link if possible_link.matches_text?(text)
end
if matching_links.any?
matching_links.sort_by { |l| l.text.length }.first
else
flunk("Could not find link with text #{text.inspect}")
field(field_locator, *field_types)
end
end
def find_field(id_or_name_or_label, *field_types)
forms.each do |form|
result = form.find_field(id_or_name_or_label, *field_types)
return result if result
def areas #:nodoc:
dom.search("area").map do |element|
Area.new(@session, element)
end
flunk("Could not find #{field_types.inspect}: #{id_or_name_or_label.inspect}")
end
def links_within(selector)
(dom / selector / "a[@href]").map do |link_element|
def links #:nodoc:
dom.search("a[@href]").map do |link_element|
Link.new(@session, link_element)
end
end
def forms
def forms #:nodoc:
return @forms if @forms
@forms = (dom / "form").map do |form_element|
@forms = dom.search("form").map do |form_element|
Form.new(@session, form_element)
end
end

View File

@ -1,5 +1,5 @@
module Webrat
class SelectOption
class SelectOption #:nodoc:
def initialize(select, element)
@select = select
@ -8,20 +8,21 @@ module Webrat
def matches_text?(text)
if text.is_a?(Regexp)
@element.innerHTML =~ text
@element.inner_html =~ text
else
@element.innerHTML == text.to_s
@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.innerHTML
@element["value"] || @element.inner_html
end
end

View File

@ -1,17 +1,23 @@
require "forwardable"
require "ostruct"
require "webrat/core/mime"
module Webrat
class PageLoadError < WebratError
end
class Session
extend Forwardable
include Logging
include Flunk
attr_reader :current_url
def initialize
@http_method = :get
@data = {}
def initialize #:nodoc:
@http_method = :get
@data = {}
@default_headers = {}
@custom_headers = {}
end
# Saves the page out to RAILS_ROOT/tmp/ and opens it in the default
@ -31,12 +37,12 @@ module Webrat
open_in_browser(filename)
end
def current_dom
def current_dom #:nodoc:
current_scope.dom
end
# For backwards compatibility -- removing in 1.0
def current_page
def current_page #:nodoc:
page = OpenStruct.new
page.url = @current_url
page.http_method = @http_method
@ -44,41 +50,64 @@ module Webrat
page
end
def doc_root
def doc_root #:nodoc:
nil
end
def saved_page_dir
def saved_page_dir #:nodoc:
File.expand_path(".")
end
def header(key, value)
@custom_headers[key] = value
end
def http_accept(mime_type)
header('Accept', Webrat::MIME.mime_type(mime_type))
end
def request_page(url, http_method, data)
debug_log "REQUESTING PAGE: #{http_method.to_s.upcase} #{url} with #{data.inspect}"
if @current_url
send "#{http_method}", url, data || {}, {"HTTP_REFERER" => @current_url}
else
def basic_auth(user, pass)
encoded_login = ["#{user}:#{pass}"].pack("m*")
header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
end
def headers #:nodoc:
@default_headers.dup.merge(@custom_headers.dup)
end
def request_page(url, http_method, data) #:nodoc:
h = headers
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}"
if h.empty?
send "#{http_method}", url, data || {}
else
send "#{http_method}", url, data || {}, h
end
save_and_open_page if exception_caught?
flunk("Page load was not successful (Code: #{response_code.inspect})") unless success_code?
raise PageLoadError.new("Page load was not successful (Code: #{response_code.inspect}):\n#{formatted_error}") unless success_code?
@scope = nil
@_scopes = nil
@_page_scope = nil
@current_url = url
@http_method = http_method
@data = data
return response
end
def success_code?
(200..299).include?(response_code)
def success_code? #:nodoc:
(200..499).include?(response_code)
end
def exception_caught?
def exception_caught? #:nodoc:
response_body =~ /Exception caught/
end
def current_scope
@scope ||= Scope.new(self, response_body)
def current_scope #:nodoc:
scopes.last || page_scope
end
# Reloads the last page requested. Note that this will resubmit forms
@ -93,49 +122,69 @@ module Webrat
alias_method :reload, :reloads
# Works like clicks_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:
# clicks_link_within "#user_12", "Vote"
def clicks_link_within(selector, link_text)
within(selector) do |scope|
scope.clicks_link(link_text)
# click_link_within "#user_12", "Vote"
def click_link_within(selector, link_text)
within(selector) do
click_link(link_text)
end
end
alias_method :click_link_within, :clicks_link_within
alias_method :clicks_link_within, :click_link_within
def within(selector)
yield Scope.new(self, response_body, selector)
scopes.push(Scope.from_scope(self, current_scope, selector))
ret = yield(current_scope)
scopes.pop
return ret
end
def visits(url = nil, http_method = :get, data = {})
# Issues a GET request for a page, follows any redirects, and verifies the final page
# load was successful.
#
# Example:
# visit "/"
def visit(url = nil, http_method = :get, data = {})
request_page(url, http_method, data)
end
alias_method :visit, :visits
alias_method :visits, :visit
def open_in_browser(path) # :nodoc
def open_in_browser(path) #:nodoc
`open #{path}`
end
def rewrite_css_and_image_references(response_html) # :nodoc
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
def formatted_error #:nodoc:
response_body
end
def scopes #:nodoc:
@_scopes ||= []
end
def page_scope #:nodoc:
@_page_scope ||= Scope.from_page(self, response, response_body)
end
def_delegators :current_scope, :fill_in, :fills_in
def_delegators :current_scope, :check, :checks
def_delegators :current_scope, :uncheck, :unchecks
def_delegators :current_scope, :choose, :chooses
def_delegators :current_scope, :select, :selects
def_delegators :current_scope, :attach_file, :attaches_file
def_delegators :current_scope, :click_area, :clicks_area
def_delegators :current_scope, :click_link, :clicks_link
def_delegators :current_scope, :click_get_link, :clicks_get_link
def_delegators :current_scope, :click_delete_link, :clicks_delete_link
def_delegators :current_scope, :click_post_link, :clicks_post_link
def_delegators :current_scope, :click_put_link, :clicks_put_link
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
end
end
end

View File

@ -0,0 +1,58 @@
class Object #:nodoc:
# An object is blank if it's false, empty, or a whitespace string.
# For example, "", " ", +nil+, [], and {} are blank.
#
# This simplifies
#
# if !address.nil? && !address.empty?
#
# to
#
# if !address.blank?
def blank?
respond_to?(:empty?) ? empty? : !self
end
# An object is present if it's not blank.
def present?
!blank?
end
end
class NilClass #:nodoc:
def blank?
true
end
end
class FalseClass #:nodoc:
def blank?
true
end
end
class TrueClass #:nodoc:
def blank?
false
end
end
class Array #:nodoc:
alias_method :blank?, :empty?
end
class Hash #:nodoc:
alias_method :blank?, :empty?
end
class String #:nodoc:
def blank?
self !~ /\S/
end
end
class Numeric #:nodoc:
def blank?
false
end
end

View File

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

View File

@ -0,0 +1,12 @@
class Array #:nodoc:
def detect_mapped
each do |element|
result = yield element
return result if result
end
return nil
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

@ -0,0 +1,6 @@
class ::Object #:nodoc:
def meta_class
class << self; self end
end
end

View File

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

View File

@ -1,5 +1,28 @@
require "rubygems"
require "mechanize"
require File.dirname(__FILE__) + "/mechanize/mechanize_session"
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,25 +0,0 @@
module Webrat
class MechanizeSession < Session
def initialize(mechanize = WWW::Mechanize.new)
@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

75
lib/webrat/merb.rb Normal file
View File

@ -0,0 +1,75 @@
require "webrat/core"
require "cgi"
gem "extlib"
require "extlib"
require "merb-core"
HashWithIndifferentAccess = Mash
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
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

View File

@ -1,3 +1,102 @@
require File.dirname(__FILE__) + "/rails/redirect_actions"
require File.dirname(__FILE__) + "/rails/rails_session"
require File.dirname(__FILE__) + "/rails/session"
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

@ -1,68 +0,0 @@
module Webrat
class RailsSession < Session
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)
update_protocol(url)
@integration_session.get_via_redirect(remove_protocol(url), data, headers)
end
def post(url, data, headers = nil)
update_protocol(url)
@integration_session.post_via_redirect(remove_protocol(url), data, headers)
end
def put(url, data, headers = nil)
update_protocol(url)
@integration_session.put_via_redirect(remove_protocol(url), data, headers)
end
def delete(url, data, headers = nil)
update_protocol(url)
@integration_session.delete_via_redirect(remove_protocol(url), data, headers)
end
def response_body
response.body
end
def response_code
response.code.to_i
end
protected
def remove_protocol(href)
if href =~ %r{^https?://www.example.com(/.*)}
$LAST_MATCH_INFO.captures.first
else
href
end
end
def update_protocol(href)
if href =~ /^https:/
@integration_session.https!(true)
elsif href =~ /^http:/
@integration_session.https!(false)
end
end
def response
@integration_session.response
end
end
end

View File

@ -1,6 +1,6 @@
# For Rails before http://dev.rubyonrails.org/ticket/10497 was committed
module Webrat
module RedirectActions
module RedirectActions #:nodoc:
def put_via_redirect(path, parameters = {}, headers = {})
put path, parameters, headers

View File

@ -1,37 +0,0 @@
module ActionController
module Integration
class Session
unless instance_methods.include?("put_via_redirect")
include Webrat::RedirectActions
end
# Issues a GET request for a page, follows any redirects, and verifies the final page
# load was successful.
#
# Example:
# visits "/"
def visits(*args)
webrat_session.visits(*args)
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

@ -1,5 +1,3 @@
require "rubygems"
require "selenium"
require File.dirname(__FILE__) + "/selenium/selenium_session"
require "webrat/selenium/selenium_session"

View File

@ -0,0 +1,12 @@
if (locator == '*') {
return selenium.browserbot.locationStrategies['xpath'].call(this, "//input[@type='submit']", inDocument, inWindow)
}
var inputs = inDocument.getElementsByTagName('input');
return $A(inputs).find(function(candidate){
inputType = candidate.getAttribute('type');
if (inputType == 'submit' || inputType == 'image') {
var buttonText = $F(candidate);
return (PatternMatcher.matches(locator, buttonText));
}
return false;
});

View File

@ -0,0 +1,16 @@
var allLabels = inDocument.getElementsByTagName("label");
var candidateLabels = $A(allLabels).select(function(candidateLabel){
var regExp = new RegExp('^' + locator + '\\b', 'i');
var labelText = getText(candidateLabel).strip();
return (labelText.search(regExp) >= 0);
});
if (candidateLabels.length == 0) {
return null;
}
candidateLabels = candidateLabels.sortBy(function(s) { return s.length * -1; }); //reverse length sort
var locatedLabel = candidateLabels.first();
var labelFor = locatedLabel.getAttribute('for');
if ((labelFor == null) && (locatedLabel.hasChildNodes())) {
return locatedLabel.firstChild; //TODO: should find the first form field, not just any node
}
return selenium.browserbot.locationStrategies['id'].call(this, labelFor, inDocument, inWindow);

View File

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

View File

@ -0,0 +1,9 @@
var links = inDocument.getElementsByTagName('a');
var candidateLinks = $A(links).select(function(candidateLink) {
return PatternMatcher.matches(locator, getText(candidateLink));
});
if (candidateLinks.length == 0) {
return null;
}
candidateLinks = candidateLinks.sortBy(function(s) { return s.length * -1; }); //reverse length sort
return candidateLinks.first();

View File

@ -0,0 +1,15 @@
var locatorParts = locator.split('|');
var cssAncestor = locatorParts[0];
var linkText = locatorParts[1];
var matchingElements = cssQuery(cssAncestor, inDocument);
var candidateLinks = matchingElements.collect(function(ancestor){
var links = ancestor.getElementsByTagName('a');
return $A(links).select(function(candidateLink) {
return PatternMatcher.matches(linkText, getText(candidateLink));
});
}).flatten().compact();
if (candidateLinks.length == 0) {
return null;
}
candidateLinks = candidateLinks.sortBy(function(s) { return s.length * -1; }); //reverse length sort
return candidateLinks.first();

View File

@ -0,0 +1,5 @@
var optionElements = inDocument.getElementsByTagName('option');
var locatedOption = $A(optionElements).find(function(candidate){
return (PatternMatcher.matches(locator, getText(candidate)));
});
return locatedOption ? locatedOption.parentNode : null;

View File

@ -0,0 +1,6 @@
PatternMatcher.strategies['evalregex'] = function(regexpString) {
this.regexp = eval(regexpString);
this.matches = function(actual) {
return this.regexp.test(actual);
};
};

View File

@ -1,41 +1,56 @@
module Webrat
class SeleniumSession < Session
class SeleniumSession
def initialize(selenium_driver)
super
def initialize(selenium_driver) #:nodoc:
@selenium = selenium_driver
extend_selenium
define_location_strategies
end
def visits(url)
def visit(url)
@selenium.open(url)
end
def fills_in(field_identifier, options)
alias_method :visits, :visit
def fill_in(field_identifier, options)
locator = "webrat=#{Regexp.escape(field_identifier)}"
@selenium.type(locator, "#{options[:with]}")
end
def response_body
alias_method :fills_in, :fill_in
def response_body #:nodoc:
@selenium.get_html_source
end
def clicks_button(button_text = nil, options = {})
button_text, options = nil, button_text if button_text.is_a?(Hash) && options == {}
button_text ||= '*'
@selenium.click("button=#{button_text}")
wait_for_result(options[:wait])
end
def clicks_link(link_text, options = {})
@selenium.click("webratlink=#{link_text}")
def click_button(button_text_or_regexp = nil, options = {})
if button_text_or_regexp.is_a?(Hash) && options == {}
pattern, options = nil, button_text_or_regexp
else
pattern = adjust_if_regexp(button_text_or_regexp)
end
pattern ||= '*'
@selenium.click("button=#{pattern}")
wait_for_result(options[:wait])
end
def clicks_link_within(selector, link_text, options = {})
alias_method :clicks_button, :click_button
def click_link(link_text_or_regexp, options = {})
pattern = adjust_if_regexp(link_text_or_regexp)
@selenium.click("webratlink=#{pattern}")
wait_for_result(options[:wait])
end
alias_method :clicks_link, :click_link
def click_link_within(selector, link_text, options = {})
@selenium.click("webratlinkwithin=#{selector}|#{link_text}")
wait_for_result(options[:wait])
end
alias_method :clicks_link_within, :click_link_within
def wait_for_result(wait_type)
if wait_type == :ajax
@ -52,7 +67,7 @@ module Webrat
end
def wait_for_ajax(timeout = 15000)
@selenium.wait_for_condition "window.Ajax.activeRequestCount == 0", timeout
@selenium.wait_for_condition "Ajax.activeRequestCount == 0", timeout
end
def wait_for_effects(timeout = 15000)
@ -75,94 +90,48 @@ module Webrat
@selenium.select(select_locator, option_text)
end
def chooses(label_text)
def choose(label_text)
@selenium.click("webrat=#{label_text}")
end
alias_method :chooses, :choose
def checks(label_text)
def check(label_text)
@selenium.check("webrat=#{label_text}")
end
protected
def define_location_strategies
@selenium.add_location_strategy('label', <<-JS)
var allLabels = inDocument.getElementsByTagName("label");
var candidateLabels = $A(allLabels).select(function(candidateLabel){
var regExp = new RegExp('^' + locator + '\\\\b', 'i');
var labelText = getText(candidateLabel).strip();
return (labelText.search(regExp) >= 0);
});
if (candidateLabels.length == 0) {
return null;
}
candidateLabels = candidateLabels.sortBy(function(s) { return s.length * -1; }); //reverse length sort
var locatedLabel = candidateLabels.first();
var labelFor = locatedLabel.getAttribute('for');
return selenium.browserbot.locationStrategies['id'].call(this, labelFor, inDocument, inWindow);
JS
@selenium.add_location_strategy('webrat', <<-JS)
var locationStrategies = selenium.browserbot.locationStrategies;
return locationStrategies['id'].call(this, locator, inDocument, inWindow)
|| locationStrategies['name'].call(this, locator, inDocument, inWindow)
|| locationStrategies['label'].call(this, locator, inDocument, inWindow)
|| null;
JS
@selenium.add_location_strategy('button', <<-JS)
if (locator == '*') {
return selenium.browserbot.locationStrategies['xpath'].call(this, "//input[@type='submit']", inDocument, inWindow)
}
var inputs = inDocument.getElementsByTagName('input');
return $A(inputs).find(function(candidate){
inputType = candidate.getAttribute('type');
if (inputType == 'submit' || inputType == 'image') {
var buttonText = $F(candidate);
return (PatternMatcher.matches(locator + '*', buttonText));
}
return false;
});
JS
@selenium.add_location_strategy('webratlink', <<-JS)
var links = inDocument.getElementsByTagName('a');
var candidateLinks = $A(links).select(function(candidateLink) {
return PatternMatcher.matches(locator, getText(candidateLink));
});
if (candidateLinks.length == 0) {
return null;
}
candidateLinks = candidateLinks.sortBy(function(s) { return s.length * -1; }); //reverse length sort
return candidateLinks.first();
JS
@selenium.add_location_strategy('webratlinkwithin', <<-JS)
var locatorParts = locator.split('|');
var cssAncestor = locatorParts[0];
var linkText = locatorParts[1];
var matchingElements = cssQuery(cssAncestor, inDocument);
var candidateLinks = matchingElements.collect(function(ancestor){
var links = ancestor.getElementsByTagName('a');
return $A(links).select(function(candidateLink) {
return PatternMatcher.matches(linkText, getText(candidateLink));
});
}).flatten().compact();
if (candidateLinks.length == 0) {
return null;
}
candidateLinks = candidateLinks.sortBy(function(s) { return s.length * -1; }); //reverse length sort
return candidateLinks.first();
JS
@selenium.add_location_strategy('webratselectwithoption', <<-JS)
var optionElements = inDocument.getElementsByTagName('option');
var locatedOption = $A(optionElements).find(function(candidate){
return (PatternMatcher.matches(locator, getText(candidate)));
});
return locatedOption ? locatedOption.parentNode : null;
JS
alias_method :checks, :check
def is_ordered(*args) #:nodoc:
@selenium.is_ordered(*args)
end
def dragdrop(*args) #:nodoc:
@selenium.dragdrop(*args)
end
protected
def adjust_if_regexp(text_or_regexp) #:nodoc:
if text_or_regexp.is_a?(Regexp)
"evalregex:#{text_or_regexp.inspect}"
else
text_or_regexp
end
end
def extend_selenium #:nodoc:
extensions_file = File.join(File.dirname(__FILE__), "selenium_extensions.js")
extenions_js = File.read(extensions_file)
@selenium.get_eval(extenions_js)
end
def define_location_strategies #:nodoc:
Dir[File.join(File.dirname(__FILE__), "location_strategy_javascript", "*.js")].sort.each do |file|
strategy_js = File.read(file)
strategy_name = File.basename(file, '.js')
@selenium.add_location_strategy(strategy_name, strategy_js)
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

@ -1,10 +0,0 @@
require "lib/webrat"
require "lib/webrat/mechanize"
include Webrat
sess = MechanizeSession.new
sess.visits "http://www.google.com/"
sess.fills_in "q", :with => "Webrat"
sess.clicks_button
sess.save_and_open_page

View File

@ -1,34 +0,0 @@
require 'rubygems'
require "lib/webrat"
require "lib/webrat/selenium"
require 'selenium'
include Webrat
# To try it out:
#
# Install the required gem
# > sudo gem install Selenium
#
# Fire up the Selenium proxy server
# > selenium
#
# Run this script
# > ruby selenium_spike.rb
@sel = Selenium::SeleniumDriver.new("localhost", 4444, "*chrome", "http://localhost", 15000)
@sel.start
sess = SeleniumSession.new(@sel)
sess.visits "http://www.google.com/"
sess.fills_in "q", :with => "Webrat"
sess.clicks_link 'Images'
sess.clicks_button 'Search'
sess.selects 'Small images', :from => 'imagesize'
sess.clicks_link 'Preferences'
sess.chooses 'Do not filter'
sess.checks 'Open search results in a new browser window'
sess.clicks_button
sess.save_and_open_page
@sel.stop

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

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "checks" do
describe "check" do
before do
@session = Webrat::TestSession.new
end
@ -11,7 +11,7 @@ describe "checks" do
</form>
EOS
lambda { @session.checks "remember_me" }.should raise_error
lambda { @session.check "remember_me" }.should raise_error
end
it "should fail if input is not a checkbox" do
@ -21,7 +21,7 @@ describe "checks" do
</form>
EOS
lambda { @session.checks "remember_me" }.should raise_error
lambda { @session.check "remember_me" }.should raise_error
end
it "should check rails style checkboxes" do
@ -34,8 +34,8 @@ describe "checks" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "1"})
@session.checks "TOS"
@session.clicks_button
@session.check "TOS"
@session.click_button
end
it "should result in the value on being posted if not specified" do
@ -46,8 +46,18 @@ describe "checks" do
</form>
EOS
@session.should_receive(:post).with("/login", "remember_me" => "on")
@session.checks "remember_me"
@session.clicks_button
@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
@ -58,12 +68,12 @@ describe "checks" do
</form>
EOS
@session.should_receive(:post).with("/login", "remember_me" => "yes")
@session.checks "remember_me"
@session.clicks_button
@session.check "remember_me"
@session.click_button
end
end
describe "unchecks" do
describe "uncheck" do
before do
@session = Webrat::TestSession.new
end
@ -74,7 +84,7 @@ describe "unchecks" do
</form>
EOS
lambda { @session.unchecks "remember_me" }.should raise_error
lambda { @session.uncheck "remember_me" }.should raise_error
end
it "should fail if input is not a checkbox" do
@ -84,7 +94,17 @@ describe "unchecks" do
</form>
EOS
lambda { @session.unchecks "remember_me" }.should raise_error
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
@ -97,9 +117,9 @@ describe "unchecks" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "0"})
@session.checks "TOS"
@session.unchecks "TOS"
@session.clicks_button
@session.check "TOS"
@session.uncheck "TOS"
@session.click_button
end
it "should result in value not being posted" do
@ -110,7 +130,7 @@ describe "unchecks" do
</form>
EOS
@session.should_receive(:post).with("/login", {})
@session.unchecks "remember_me"
@session.clicks_button
@session.uncheck "remember_me"
@session.click_button
end
end

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "chooses" do
describe "choose" do
before do
@session = Webrat::TestSession.new
end
@ -11,7 +11,7 @@ describe "chooses" do
</form>
EOS
lambda { @session.chooses "first option" }.should raise_error
lambda { @session.choose "first option" }.should raise_error
end
it "should fail if input is not a radio button" do
@ -21,7 +21,7 @@ describe "chooses" do
</form>
EOS
lambda { @session.chooses "first_option" }.should raise_error
lambda { @session.choose "first_option" }.should raise_error
end
it "should check rails style radio buttons" do
@ -35,8 +35,8 @@ describe "chooses" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"gender" => "M"})
@session.chooses "Male"
@session.clicks_button
@session.choose "Male"
@session.click_button
end
it "should only submit last chosen value" do
@ -50,10 +50,21 @@ describe "chooses" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"gender" => "M"})
@session.chooses "Female"
@session.chooses "Male"
@session.clicks_button
end
@session.choose "Female"
@session.choose "Male"
@session.click_button
end
it "should fail if the radio button is disabled" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input type="radio" name="first_option" disabled="disabled" />
<input type="submit" />
</form>
EOS
lambda { @session.choose "first_option" }.should raise_error
end
it "should result in the value on being posted if not specified" do
@session.response_body = <<-EOS
@ -63,8 +74,8 @@ describe "chooses" do
</form>
EOS
@session.should_receive(:post).with("/login", "first_option" => "on")
@session.chooses "first_option"
@session.clicks_button
@session.choose "first_option"
@session.click_button
end
it "should result in the value on being posted if not specified and checked by default" do
@ -75,7 +86,7 @@ describe "chooses" do
</form>
EOS
@session.should_receive(:post).with("/login", "first_option" => "on")
@session.clicks_button
@session.click_button
end
it "should result in the value of the selected radio button being posted when a subsequent one is checked by default" do
@ -89,7 +100,7 @@ describe "chooses" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"gender" => "M"})
@session.chooses "Male"
@session.clicks_button
@session.choose "Male"
@session.click_button
end
end

View File

@ -0,0 +1,93 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "click_area" do
before do
@session = Webrat::TestSession.new
end
it "should use get by default" do
@session.response_body = <<-EOS
<map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_area "Berlin"
end
it "should assert valid response" do
@session.response_body = <<-EOS
<map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
@session.response_code = 501
lambda { @session.click_area "Berlin" }.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
<map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
@session.response_code = status
lambda { @session.click_area "Berlin" }.should_not raise_error
end
end
it "should fail if the area doesn't exist" do
@session.response_body = <<-EOS
<map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
lambda {
@session.click_area "Missing area"
}.should raise_error
end
it "should not be case sensitive" do
@session.response_body = <<-EOS
<map name="map_de" id="map_de">
<area href="/page" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
@session.should_receive(:get).with("/page", {})
@session.click_area "berlin"
end
it "should follow relative links" do
@session.stub!(:current_url).and_return("/page")
@session.response_body = <<-EOS
<map name="map_de" id="map_de">
<area href="sub" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
@session.should_receive(:get).with("/page/sub", {})
@session.click_area "Berlin"
end
it "should follow fully qualified local links" do
@session.response_body = <<-EOS
<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" />
</map>
EOS
@session.should_receive(:get).with("http://www.example.com/page", {})
@session.click_area "Berlin"
end
it "should follow query parameters" do
@session.response_body = <<-EOS
<map name="map_de" id="map_de">
<area href="/page?foo=bar" title="Berlin" id="berlin" shape="poly" alt="Berlin" coords="180,89,180" />
</map>
EOS
@session.should_receive(:get).with("/page?foo=bar", {})
@session.click_area "Berlin"
end
end

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "clicks_button" do
describe "click_button" do
before do
@session = Webrat::TestSession.new
end
@ -10,7 +10,7 @@ describe "clicks_button" do
<form method="get" action="/login"></form>
EOS
lambda { @session.clicks_button }.should raise_error
lambda { @session.click_button }.should raise_error
end
it "should fail if input is not a submit button" do
@ -20,7 +20,18 @@ describe "clicks_button" do
</form>
EOS
lambda { @session.clicks_button }.should raise_error
lambda { @session.click_button }.should raise_error
end
it "should fail if button is disabled" do
@session.response_body = <<-EOS
<form method="get" action="/login">
<input type="submit" disabled="disabled" />
</form>
EOS
lambda { @session.click_button }.should raise_error
end
it "should default to get method" do
@ -30,7 +41,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get)
@session.clicks_button
@session.click_button
end
it "should assert valid response" do
@ -39,8 +50,20 @@ describe "clicks_button" do
<input type="submit" />
</form>
EOS
@session.response_code = 404
lambda { @session.clicks_button }.should raise_error
@session.response_code = 501
lambda { @session.click_button }.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
<form action="/login">
<input type="submit" />
</form>
EOS
@session.response_code = status
lambda { @session.click_button }.should_not raise_error
end
end
it "should submit the first form by default" do
@ -53,7 +76,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/form1", {})
@session.clicks_button
@session.click_button
end
it "should not explode on file fields" do
@ -63,7 +86,7 @@ describe "clicks_button" do
<input type="submit" />
</form>
EOS
@session.clicks_button
@session.click_button
end
it "should submit the form with the specified button" do
@ -76,7 +99,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/form2", {})
@session.clicks_button "Form2"
@session.click_button "Form2"
end
it "should use action from form" do
@ -86,7 +109,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", {})
@session.clicks_button
@session.click_button
end
it "should use method from form" do
@ -96,7 +119,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:post)
@session.clicks_button
@session.click_button
end
it "should send button as param if it has a name" do
@ -107,7 +130,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:post).with("/login", "login" => "Login")
@session.clicks_button("Login")
@session.click_button("Login")
end
it "should not send button as param if it has no name" do
@ -118,7 +141,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:post).with("/login", {})
@session.clicks_button("Login")
@session.click_button("Login")
end
it "should send default password field values" do
@ -129,7 +152,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"password" => "mypass"})
@session.clicks_button
@session.click_button
end
it "should send default hidden field values" do
@ -140,7 +163,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"email" => "test@example.com"})
@session.clicks_button
@session.click_button
end
it "should send default text field values" do
@ -151,7 +174,22 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"email" => "test@example.com"})
@session.clicks_button
@session.click_button
end
it "should not send disabled field values" do
@session.response_body = <<-EOS
<form method="get" action="/login">
<input disabled id="user_email" name="user[email]" value="test@example.com" type="text" />
<input disabled id="user_gender_male" name="user[gender]" type="radio" value="M" />
<label for="user_gender_male">Male</label>
<input disabled id="user_gender_female" name="user[gender]" type="radio" value="F" checked="checked" />
<label for="user_gender_female">Female</label>
<input type="submit" />
</form>
EOS
@session.should_receive(:get).with("/login", {})
@session.click_button
end
it "should send default checked fields" do
@ -162,7 +200,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "1"})
@session.clicks_button
@session.click_button
end
it "should send default radio options" do
@ -176,7 +214,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"gender" => "F"})
@session.clicks_button
@session.click_button
end
it "should send correct data for rails style unchecked fields" do
@ -188,7 +226,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "0"})
@session.clicks_button
@session.click_button
end
it "should send correct data for rails style checked fields" do
@ -200,7 +238,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"tos" => "1"})
@session.clicks_button
@session.click_button
end
it "should send default collection fields" do
@ -222,7 +260,7 @@ describe "clicks_button" do
@session.should_receive(:post).with("/login",
"options" => ["burger", "fries", "soda", "soda", "dessert"],
"response" => { "choices" => [{"selected" => "one"}, {"selected" => "two"}, {"selected" => "two"}]})
@session.clicks_button
@session.click_button
end
it "should not send default unchecked fields" do
@ -233,7 +271,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", {})
@session.clicks_button
@session.click_button
end
it "should send default textarea values" do
@ -244,7 +282,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:post).with("/posts", "post" => {"body" => "Post body here!"})
@session.clicks_button
@session.click_button
end
it "should send default selected option value from select" do
@ -258,7 +296,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "month" => "2")
@session.clicks_button
@session.click_button
end
it "should send default selected option inner html from select when no value attribute" do
@ -272,7 +310,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "month" => "February")
@session.clicks_button
@session.click_button
end
it "should send first select option value when no option selected" do
@ -286,7 +324,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "month" => "1")
@session.clicks_button
@session.click_button
end
it "should handle nested properties" do
@ -298,7 +336,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:post).with("/login", "contestant" => {"scores" => {'1' => '2', '3' => '4'}})
@session.clicks_button
@session.click_button
end
it "should send default empty text field values" do
@ -309,7 +347,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"email" => ""})
@session.clicks_button
@session.click_button
end
it "should recognize button tags" do
@ -320,7 +358,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"email" => ""})
@session.clicks_button
@session.click_button
end
it "should recognize image button tags" do
@ -330,7 +368,17 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get)
@session.clicks_button
@session.click_button
end
it "should find buttons by their IDs" do
@session.response_body = <<-EOS
<form action="/">
<input type="submit" id="my_button" />
</form>
EOS
@session.should_receive(:get)
@session.click_button "my_button"
end
it "should find image buttons by their alt text" do
@ -340,7 +388,7 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get)
@session.clicks_button "Go"
@session.click_button "Go"
end
it "should recognize button tags by content" do
@ -351,6 +399,6 @@ describe "clicks_button" do
</form>
EOS
@session.should_receive(:get).with("/login", "user" => {"email" => ""})
@session.clicks_button "Login"
@session.click_button "Login"
end
end

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "clicks_link" do
describe "click_link" do
before do
@session = Webrat::TestSession.new
end
@ -10,7 +10,7 @@ describe "clicks_link" do
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.clicks_link "Link text"
@session.click_link "Link text"
end
it "should click get links" do
@ -18,7 +18,7 @@ describe "clicks_link" do
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.clicks_get_link "Link text"
@session.click_link "Link text", :method => :get
end
it "should click delete links" do
@ -26,7 +26,7 @@ describe "clicks_link" do
<a href="/page">Link text</a>
EOS
@session.should_receive(:delete).with("/page", {})
@session.clicks_delete_link "Link text"
@session.click_link "Link text", :method => :delete
end
@ -35,7 +35,7 @@ describe "clicks_link" do
<a href="/page">Link text</a>
EOS
@session.should_receive(:post).with("/page", {})
@session.clicks_post_link "Link text"
@session.click_link "Link text", :method => :post
end
it "should click put links" do
@ -43,7 +43,15 @@ describe "clicks_link" do
<a href="/page">Link text</a>
EOS
@session.should_receive(:put).with("/page", {})
@session.clicks_put_link "Link text"
@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
@ -62,7 +70,7 @@ describe "clicks_link" do
return false;">Posts</a>
EOS
@session.should_receive(:post).with("/posts", "authenticity_token" => "aa79cb354597a60a3786e7e291ed4f74d77d3a62")
@session.clicks_link "Posts"
@session.click_link "Posts"
end
it "should click rails javascript delete links" do
@ -81,7 +89,7 @@ describe "clicks_link" do
return false;">Delete</a>
EOS
@session.should_receive(:delete).with("/posts/1", {})
@session.clicks_link "Delete"
@session.click_link "Delete"
end
it "should click rails javascript post links" do
@ -95,7 +103,7 @@ describe "clicks_link" do
return false;">Posts</a>
EOS
@session.should_receive(:post).with("/posts", {})
@session.clicks_link "Posts"
@session.click_link "Posts"
end
it "should click rails javascript post links without javascript" do
@ -109,7 +117,7 @@ describe "clicks_link" do
return false;">Posts</a>
EOS
@session.should_receive(:get).with("/posts", {})
@session.clicks_link "Posts", :javascript => false
@session.click_link "Posts", :javascript => false
end
it "should click rails javascript put links" do
@ -128,7 +136,7 @@ describe "clicks_link" do
return false;">Put</a></h2>
EOS
@session.should_receive(:put).with("/posts", {})
@session.clicks_link "Put"
@session.click_link "Put"
end
it "should fail if the javascript link doesn't have a value for the _method input" do
@ -147,7 +155,7 @@ describe "clicks_link" do
EOS
lambda {
@session.clicks_link "Link"
@session.click_link "Link"
}.should raise_error
end
@ -155,8 +163,18 @@ describe "clicks_link" do
@session.response_body = <<-EOS
<a href="/page">Link text</a>
EOS
@session.response_code = 404
lambda { @session.clicks_link "Link text" }.should raise_error
@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
@ -165,7 +183,7 @@ describe "clicks_link" do
EOS
lambda {
@session.clicks_link "Missing link"
@session.click_link "Missing link"
}.should raise_error
end
@ -174,7 +192,7 @@ describe "clicks_link" do
<a href="/page">Link text</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.clicks_link "LINK TEXT"
@session.click_link "LINK TEXT"
end
it "should match link substrings" do
@ -182,7 +200,7 @@ describe "clicks_link" do
<a href="/page">This is some cool link text, isn't it?</a>
EOS
@session.should_receive(:get).with("/page", {})
@session.clicks_link "Link text"
@session.click_link "Link text"
end
it "should work with elements in the link" do
@ -190,7 +208,7 @@ describe "clicks_link" do
<a href="/page"><span>Link text</span></a>
EOS
@session.should_receive(:get).with("/page", {})
@session.clicks_link "Link text"
@session.click_link "Link text"
end
it "should match the first matching link" do
@ -199,7 +217,7 @@ describe "clicks_link" do
<a href="/page2">Link text</a>
EOS
@session.should_receive(:get).with("/page1", {})
@session.clicks_link "Link text"
@session.click_link "Link text"
end
it "should choose the shortest link text match" do
@ -209,7 +227,7 @@ describe "clicks_link" do
EOS
@session.should_receive(:get).with("/page2", {})
@session.clicks_link "Link"
@session.click_link "Link"
end
it "should treat non-breaking spaces as spaces" do
@ -218,7 +236,7 @@ describe "clicks_link" do
EOS
@session.should_receive(:get).with("/page1", {})
@session.clicks_link "This is a link"
@session.click_link "This is a link"
end
it "should click link within a selector" do
@ -230,7 +248,7 @@ describe "clicks_link" do
EOS
@session.should_receive(:get).with("/page2", {})
@session.clicks_link_within "#container", "Link"
@session.click_link_within "#container", "Link"
end
it "should not make request when link is local anchor" do
@ -239,7 +257,7 @@ describe "clicks_link" do
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.clicks_link "Jump to Section 1"
@session.click_link "Jump to Section 1"
end
it "should follow relative links" do
@ -248,7 +266,7 @@ describe "clicks_link" do
<a href="sub">Jump to sub page</a>
EOS
@session.should_receive(:get).with("/page/sub", {})
@session.clicks_link "Jump to sub page"
@session.click_link "Jump to sub page"
end
it "should follow fully qualified local links" do
@ -256,7 +274,7 @@ describe "clicks_link" do
<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.clicks_link "Jump to sub page"
@session.click_link "Jump to sub page"
end
it "should follow query parameters" do
@ -265,6 +283,6 @@ describe "clicks_link" do
<a href="?foo=bar">Jump to foo bar</a>
EOS
@session.should_receive(:get).with("/page?foo=bar", {})
@session.clicks_link "Jump to foo bar"
@session.click_link "Jump to foo bar"
end
end

View File

@ -0,0 +1,111 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "field_labeled" do
class << self
def using_this_html html
before(:each) do
@session = Webrat::TestSession.new
@session.response_body = html
end
end
def field_labeled label
@label = label
yield
end
def should_return_a type, opts
it "should return a textfield" do
@session.field_labeled(opts[:for]).should be_an_instance_of(type)
end
end
def with_an_id_of id, opts
it "should return an element with the correct id" do
@session.field_labeled(opts[:for]).should match_id(id)
end
end
def should_raise_error_matching regexp, opts
it "should raise with wrong label" do
lambda {
@session.field_labeled(opts[:for])
}.should raise_error(regexp)
end
end
end
def match_id(id)
simple_matcher "element with id #{id.inspect}" do |element, matcher|
element.matches_id? id
end
end
describe "finding a text field" do
using_this_html <<-HTML
<form>
<label for="element_42">The Label</label>
<input type="text" id="element_42">
</form>
HTML
should_return_a Webrat::TextField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end
describe "finding a hidden field" do
using_this_html <<-HTML
<form>
<label for="element_42">The Label</label>
<input type="hidden" id="element_42">
</form>
HTML
should_return_a Webrat::HiddenField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end
describe "finding a checkbox" do
using_this_html <<-HTML
<form>
<label for="element_42">The Label</label>
<input type="checkbox" id="element_42">
</form>
HTML
should_return_a Webrat::CheckboxField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end
describe "finding a radio button" do
using_this_html <<-HTML
<form>
<label for="element_42">The Label</label>
<input type="radio" id="element_42">
</form>
HTML
should_return_a Webrat::RadioField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end
describe "finding a text area" do
using_this_html <<-HTML
<form>
<label for="element_42">The Label</label>
<textarea id="element_42"></textarea>
</form>
HTML
should_return_a Webrat::TextareaField, :for => "The Label"
with_an_id_of "element_42", :for => "The Label"
should_raise_error_matching /Could not find .* "Other Label"/, :for => "Other Label"
end
end

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "fills_in" do
describe "fill_in" do
before do
@session = Webrat::TestSession.new
end
@ -14,8 +14,8 @@ describe "fills_in" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"text" => "filling text area"})
@session.fills_in "User Text", :with => "filling text area"
@session.clicks_button
@session.fill_in "User Text", :with => "filling text area"
@session.click_button
end
it "should work with password fields" do
@ -26,8 +26,8 @@ describe "fills_in" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"text" => "pass"})
@session.fills_in "user_text", :with => "pass"
@session.clicks_button
@session.fill_in "user_text", :with => "pass"
@session.click_button
end
it "should fail if input not found" do
@ -36,7 +36,19 @@ describe "fills_in" do
</form>
EOS
lambda { @session.fills_in "Email", :with => "foo@example.com" }.should raise_error
lambda { @session.fill_in "Email", :with => "foo@example.com" }.should raise_error
end
it "should fail if input is disabled" do
@session.response_body = <<-EOS
<form method="get" action="/login">
<label for="user_email">Email</label>
<input id="user_email" name="user[email]" type="text" disabled="disabled" />
<input type="submit" />
</form>
EOS
lambda { @session.fill_in "Email", :with => "foo@example.com" }.should raise_error
end
it "should allow overriding default form values" do
@ -48,8 +60,8 @@ describe "fills_in" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"email" => "foo@example.com"})
@session.fills_in "user[email]", :with => "foo@example.com"
@session.clicks_button
@session.fill_in "user[email]", :with => "foo@example.com"
@session.click_button
end
it "should choose the shortest label match" do
@ -64,8 +76,8 @@ describe "fills_in" do
EOS
@session.should_receive(:post).with("/login", "user" => {"mail1" => "", "mail2" => "value"})
@session.fills_in "Some", :with => "value"
@session.clicks_button
@session.fill_in "Some", :with => "value"
@session.click_button
end
it "should choose the first label match if closest is a tie" do
@ -80,8 +92,8 @@ describe "fills_in" do
EOS
@session.should_receive(:post).with("/login", "user" => {"mail1" => "value", "mail2" => ""})
@session.fills_in "Some mail", :with => "value"
@session.clicks_button
@session.fill_in "Some mail", :with => "value"
@session.click_button
end
it "should anchor label matches to start of label" do
@ -92,7 +104,7 @@ describe "fills_in" do
</form>
EOS
lambda { @session.fills_in "mail", :with => "value" }.should raise_error
lambda { @session.fill_in "mail", :with => "value" }.should raise_error
end
it "should anchor label matches to word boundaries" do
@ -103,7 +115,7 @@ describe "fills_in" do
</form>
EOS
lambda { @session.fills_in "Email", :with => "value" }.should raise_error
lambda { @session.fill_in "Email", :with => "value" }.should raise_error
end
it "should work with inputs nested in labels" do
@ -117,8 +129,8 @@ describe "fills_in" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"email" => "foo@example.com"})
@session.fills_in "Email", :with => "foo@example.com"
@session.clicks_button
@session.fill_in "Email", :with => "foo@example.com"
@session.click_button
end
it "should work with full input names" do
@ -129,8 +141,20 @@ describe "fills_in" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"email" => "foo@example.com"})
@session.fills_in "user[email]", :with => "foo@example.com"
@session.clicks_button
@session.fill_in "user[email]", :with => "foo@example.com"
@session.click_button
end
it "should work if the input type is not set" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<input id="user_email" name="user[email]" />
<input type="submit" />
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"email" => "foo@example.com"})
@session.fill_in "user[email]", :with => "foo@example.com"
@session.click_button
end
it "should work with symbols" do
@ -142,7 +166,20 @@ describe "fills_in" do
</form>
EOS
@session.should_receive(:post).with("/login", "user" => {"email" => "foo@example.com"})
@session.fills_in :email, :with => "foo@example.com"
@session.clicks_button
@session.fill_in :email, :with => "foo@example.com"
@session.click_button
end
it "should escape field values" do
@session.response_body = <<-EOS
<form method="post" action="/users">
<label for="user_phone">Phone</label>
<input id="user_phone" name="user[phone]" type="text" />
<input type="submit" />
</form>
EOS
@session.should_receive(:post).with("/users", "user" => {"phone" => "+1 22 33"})
@session.fill_in 'Phone', :with => "+1 22 33"
@session.click_button
end
end

178
spec/api/matchers_spec.rb Normal file
View File

@ -0,0 +1,178 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe Webrat::Matchers do
include Webrat::Matchers
include Webrat::HaveTagMatcher
before(:each) do
@body = <<-EOF
<div id='main'>
<div class='inner'>hello, world!</div>
<ul>
<li>First</li>
<li>Second</li>
</ul>
</div>
EOF
end
describe "#have_xpath" do
it "should be able to match an XPATH" do
@body.should have_xpath("//div")
end
it "should not match a XPATH that does not exist" do
@body.should_not have_xpath("//p")
end
it "should be able to loop over all the matched elements" do
@body.should have_xpath("//div") { |node| node.first.name.should == "div" }
end
it "should not match of any of the matchers in the block fail" do
lambda {
@body.should have_xpath("//div") { |node| node.first.name.should == "p" }
}.should raise_error(Spec::Expectations::ExpectationNotMetError)
end
it "should be able to use #have_xpath in the block" do
@body.should have_xpath("//div[@id='main']") { |node| node.should have_xpath("./div[@class='inner']") }
end
it "should convert absolute paths to relative in the block" do
@body.should have_xpath("//div[@id='main']") { |node| node.should have_xpath("//div[@class='inner']") }
end
it "should not match any parent tags in the block" do
lambda {
@body.should have_xpath("//div[@class='inner']") { |node| node.should have_xpath("//div[@id='main']") }
}.should raise_error(Spec::Expectations::ExpectationNotMetError)
end
end
describe "#have_selector" do
it "should be able to match a CSS selector" do
@body.should have_selector("div")
end
it "should not match a CSS selector that does not exist" do
@body.should_not have_selector("p")
end
it "should be able to loop over all the matched elements" do
@body.should have_selector("div") { |node| node.first.name.should == "div" }
end
it "should not match of any of the matchers in the block fail" do
lambda {
@body.should have_selector("div") { |node| node.first.name.should == "p" }
}.should raise_error(Spec::Expectations::ExpectationNotMetError)
end
it "should be able to use #have_selector in the block" do
@body.should have_selector("#main") { |node| node.should have_selector(".inner") }
end
it "should not match any parent tags in the block" do
lambda {
@body.should have_selector(".inner") { |node| node.should have_selector("#main") }
}.should raise_error(Spec::Expectations::ExpectationNotMetError)
end
end
describe "#have_tag" do
it "should be able to match a tag" do
@body.should have_tag("div")
end
it "should not match the tag when it should not match" do
@body.should_not have_tag("p")
end
it "should be able to specify the content of the tag" do
@body.should have_tag("div", :content => "hello, world!")
end
it "should be able to specify the attributes of the tag" do
@body.should have_tag("div", :class => "inner")
end
it "should be able to loop over all the matched elements" do
@body.should have_tag("div") { |node| node.first.name.should == "div" }
end
it "should not match of any of the matchers in the block fail" do
lambda {
@body.should have_tag("div") { |node| node.first.name.should == "p" }
}.should raise_error(Spec::Expectations::ExpectationNotMetError)
end
it "should be able to use #have_tag in the block" do
@body.should have_tag("div", :id => "main") { |node| node.should have_tag("div", :class => "inner") }
end
it "should not match any parent tags in the block" do
lambda {
@body.should have_tag("div", :class => "inner") { |node| node.should have_tag("div", :id => "main") }
}.should raise_error(Spec::Expectations::ExpectationNotMetError)
end
it "should work with items that have multiple child nodes" do
@body.should have_tag("ul") { |n|
n.should have_tag("li", :content => "First")
n.should have_tag("li", :content => "Second")
}
end
end
describe Webrat::Matchers::HasContent do
include Webrat::Matchers
before(:each) do
@body = <<-EOF
<div id='main'>
<div class='inner'>hello, world!</div>
</div>
EOF
end
describe "#matches?" do
it "should call element#contains? when the argument is a string" do
@body.should contain("hello, world!")
end
it "should call element#matches? when the argument is a regular expression" do
@body.should contain(/hello, world/)
end
end
describe "#failure_message" do
it "should include the content string" do
hc = Webrat::Matchers::HasContent.new("hello, world!")
hc.matches?(@body)
hc.failure_message.should include("\"hello, world!\"")
end
it "should include the content regular expresson" do
hc = Webrat::Matchers::HasContent.new(/hello,\sworld!/)
hc.matches?(@body)
hc.failure_message.should include("/hello,\\sworld!/")
end
it "should include the element's inner content" do
hc = Webrat::Matchers::HasContent.new(/hello,\sworld!/)
hc.matches?(@body)
hc.failure_message.should include("hello, world!")
end
end
end
end

View File

@ -9,7 +9,7 @@ describe "reloads" do
it "should reload the page with http referer" do
@session.should_receive(:get).with("/", {})
@session.should_receive(:get).with("/", {}, {"HTTP_REFERER"=>"/"})
@session.visits("/")
@session.visit("/")
@session.reloads
end
end

View File

@ -35,6 +35,18 @@ describe "selects" do
lambda { @session.selects "February", :from => "year" }.should raise_error
end
it "should fail if the select is disabled" do
@session.response_body = <<-EOS
<form method="post" action="/login">
<select name="month" disabled="disabled"><option value="1">January</option></select>
<input type="submit" />
</form>
EOS
lambda { @session.selects "January", :from => "month" }.should raise_error
end
it "should send value from option" do
@session.response_body = <<-EOS
@ -45,7 +57,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "month" => "1")
@session.selects "January", :from => "month"
@session.clicks_button
@session.click_button
end
it "should send values with HTML encoded ampersands" do
@ -57,7 +69,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "encoded" => "A & B")
@session.selects "Encoded", :from => "encoded"
@session.clicks_button
@session.click_button
end
it "should work with empty select lists" do
@ -68,7 +80,7 @@ describe "selects" do
</form>
EOS
@session.should_receive(:post).with("/login", 'month' => '')
@session.clicks_button
@session.click_button
end
it "should work without specifying the field name or label" do
@ -80,7 +92,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "month" => "1")
@session.selects "January"
@session.clicks_button
@session.click_button
end
it "should send value from option in list specified by name" do
@ -93,7 +105,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "start_month" => "s1", "end_month" => "e1")
@session.selects "January", :from => "end_month"
@session.clicks_button
@session.click_button
end
it "should send value from option in list specified by label" do
@ -108,7 +120,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "start_month" => "s1", "end_month" => "e1")
@session.selects "January", :from => "End Month"
@session.clicks_button
@session.click_button
end
it "should use option text if no value" do
@ -120,7 +132,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "month" => "January")
@session.selects "January", :from => "month"
@session.clicks_button
@session.click_button
end
it "should find option by regexp" do
@ -132,7 +144,7 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "month" => "January")
@session.selects(/jan/i)
@session.clicks_button
@session.click_button
end
it "should fail if no option matching the regexp exists" do
@ -160,6 +172,6 @@ describe "selects" do
EOS
@session.should_receive(:post).with("/login", "start_month" => "s1", "end_month" => "e1")
@session.selects(/jan/i, :from => "End Month")
@session.clicks_button
@session.click_button
end
end

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "visits" do
describe "visit" do
before do
@session = Webrat::TestSession.new
@session.response_body = "Hello world"
@ -8,20 +8,27 @@ describe "visits" do
it "should use get" do
@session.should_receive(:get).with("/", {})
@session.visits("/")
@session.visit("/")
end
it "should assert valid response" do
@session.response_code = 404
lambda { @session.visits("/") }.should raise_error
@session.response_code = 501
lambda { @session.visit("/") }.should raise_error
end
[200, 300, 400, 499].each do |status|
it "should consider the #{status} status code as success" do
@session.response_code = status
lambda { @session.visit("/") }.should_not raise_error
end
end
it "should require a visit before manipulating page" do
lambda { @session.fills_in "foo", :with => "blah" }.should raise_error
lambda { @session.fill_in "foo", :with => "blah" }.should raise_error
end
end
describe "visits with referer" do
describe "visit with referer" do
before do
@session = Webrat::TestSession.new
@session.instance_variable_set(:@current_url, "/old_url")
@ -30,7 +37,7 @@ describe "visits with referer" do
it "should use get with referer header" do
@session.should_receive(:get).with("/", {}, {"HTTP_REFERER" => "/old_url"})
@session.visits("/")
@session.visit("/")
end
end
end

View File

@ -5,6 +5,24 @@ describe "within" do
@session = Webrat::TestSession.new
end
it "should work when nested" do
@session.response_body = <<-EOS
<div>
<a href="/page1">Link</a>
</div>
<div id="container">
<div><a href="/page2">Link</a></div>
</div>
EOS
@session.should_receive(:get).with("/page2", {})
@session.within "#container" do
@session.within "div" do
@session.click_link "Link"
end
end
end
it "should click links within a scope" do
@session.response_body = <<-EOS
<a href="/page1">Link</a>
@ -14,8 +32,8 @@ describe "within" do
EOS
@session.should_receive(:get).with("/page2", {})
@session.within "#container" do |scope|
scope.clicks_link "Link"
@session.within "#container" do
@session.click_link "Link"
end
end
@ -32,9 +50,9 @@ describe "within" do
EOS
@session.should_receive(:get).with("/form2", "email" => "test@example.com")
@session.within "#form2" do |scope|
scope.fill_in "Email", :with => "test@example.com"
scope.clicks_button
@session.within "#form2" do
@session.fill_in "Email", :with => "test@example.com"
@session.click_button
end
end
@ -47,9 +65,9 @@ describe "within" do
</form>
EOS
@session.within "#form2" do |scope|
@session.within "#form2" do
lambda {
scope.clicks_button
@session.click_button
}.should raise_error
end
end

View File

@ -7,6 +7,10 @@ module Webrat
File.expand_path(File.join(".", "public"))
end
def response
@response ||= Object.new
end
def response_code
@response_code || 200
end

View File

@ -4,16 +4,8 @@ require "spec"
# gem install redgreen for colored test output
begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
require "active_support"
silence_warnings do
require "action_controller"
require "action_controller/integration"
end
require File.expand_path(File.dirname(__FILE__) + "/../lib/webrat")
require File.expand_path(File.dirname(__FILE__) + "/../lib/webrat/rails")
require File.dirname(__FILE__) + "/fakes/test_session"
require File.expand_path(File.dirname(__FILE__) + "/fakes/test_session")
Spec::Runner.configure do |config|
# Nothing to configure yet

View File

@ -0,0 +1,15 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
module Webrat
describe CheckboxField do
it "should say it is checked if it is" do
checkbox = CheckboxField.new(nil, (Webrat.nokogiri_document("<input type='checkbox' checked='checked'>")/'input').first)
checkbox.should be_checked
end
it "should say it is not checked if it is not" do
checkbox = CheckboxField.new(nil, (Webrat.nokogiri_document("<input type='checkbox'>")/'input').first)
checkbox.should_not be_checked
end
end
end

View File

@ -1,12 +1,10 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
describe Webrat::Logging do
include Webrat::Logging
it "should log to RAILS_DEFAULT_LOGGER" do
logger = mock("logger")
RAILS_DEFAULT_LOGGER = logger
logger.should_receive(:debug).with("Testing")
debug_log "Testing"
it "should not log if there is no logger" do
klass = Class.new
klass.send(:include, Webrat::Logging)
klass.new.debug_log "Testing"
end
end

View File

@ -10,11 +10,15 @@ describe Webrat::Session do
it "should expose the current_dom" do
session = Webrat::Session.new
def session.response
Object.new
end
def session.response_body
"<html></html>"
end
session.current_dom.should be_an_instance_of(Hpricot::Doc)
session.current_dom.should be_an_instance_of(Nokogiri::HTML::Document)
end
it "should open the page in the browser" do
@ -29,5 +33,56 @@ describe Webrat::Session do
current_page.should_not be_nil
current_page.should respond_to(:url)
end
it "should allow custom headers to be set" do
session = Webrat::Session.new
session.header('Accept', 'application/xml')
session.instance_variable_get(:@custom_headers)['Accept'].should == 'application/xml'
end
it "should return a copy of the headers to be sent" do
session = Webrat::Session.new
session.instance_eval {
@default_headers = {'HTTP_X_FORWARDED_FOR' => '192.168.1.1'}
@custom_headers = {'Accept' => 'application/xml'}
}
session.headers.should == {'HTTP_X_FORWARDED_FOR' => '192.168.1.1', 'Accept' => 'application/xml'}
end
describe "#http_accept" do
before(:each) do
@session = Webrat::Session.new
end
it "should set the Accept header with the string Mime type" do
@session.http_accept('application/xml')
@session.headers['Accept'].should == 'application/xml'
end
it "should set the Accept head with the string value of the symbol Mime type" do
@session.http_accept(:xml)
@session.headers['Accept'].should == 'application/xml'
end
it "should raise an error if a symbol Mime type is passed that does not exist" do
lambda { @session.http_accept(:oogabooga) }.should raise_error(ArgumentError)
end
end
describe "#request_page" do
before(:each) do
@session = Webrat::Session.new
end
end
it "should raise an error if the request is not a success" do
@session.stub!(:get)
@session.stub!(:response_body).and_return("Exception caught")
@session.stub!(:response_code).and_return(500)
@session.stub!(:formatted_error).and_return("application error")
@session.stub!(:save_and_open_page)
lambda { @session.request_page('some url', :get, {}) }.should raise_error(Webrat::PageLoadError)
end
end
end

View File

@ -0,0 +1,15 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
require "mechanize"
require "webrat/mechanize"
describe Webrat::MechanizeSession do
before(:each) do
@mech = Webrat::MechanizeSession.new
end
describe "headers method" do
it "should return empty headers for a newly initialized session" do
@mech.headers.should == {}
end
end
end

View File

@ -0,0 +1,2 @@
require 'merb-core'
require File.expand_path(File.dirname(__FILE__) + "/../../../lib/webrat/merb")

View File

@ -0,0 +1,48 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
require File.expand_path(File.dirname(__FILE__) + "/helper")
describe HashWithIndifferentAccess do
it "should not update constructor when not a hash" do
HashWithIndifferentAccess.should_receive(:update).never
HashWithIndifferentAccess.new('test')
end
it "should get the default for key" do
h = HashWithIndifferentAccess.new(:test => 'me')
h.should_receive(:super).never
h.default(:test).should == 'me'
end
context "a hash with a test value applied" do
setup do
@h = HashWithIndifferentAccess.new
@h[:test] = 'me'
end
it "should assign a new value" do
@h[:test].should == 'me'
end
it "should return true if asked for existing key" do
@h.key?(:test).should be_true
end
it "should return array of values for keys" do
@h.values_at(:test).should == ['me']
end
it "should merge with another hash" do
another = HashWithIndifferentAccess.new(:value => 'test')
@h.merge(another).values_at(:test, :value).should == ['me','test']
end
it "should delete the key" do
@h.delete(:test)
@h.any?.should be_false
@h[:test].should be_nil
end
end
end

View File

@ -0,0 +1,43 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
require File.expand_path(File.dirname(__FILE__) + "/helper")
describe Webrat::Session do
it "should not pass empty params if data is and empty hash" do
session = Webrat::MerbSession.new
response = OpenStruct.new
response.status = 200
session.should_receive(:request).with('url', {:params=> nil, :method=>"GET", :headers=>nil}).and_return(response)
session.get('url', {}, nil)
end
%w{post put delete}.each do |request_method|
it "should call do request with method #{request_method.upcase} for a #{request_method} call" do
session = Webrat::MerbSession.new
response = OpenStruct.new
response.status = 200
session.should_receive(:request).with('url', {:params=>nil, :method=>request_method.upcase, :headers=>nil}).and_return(response)
session.send(request_method, 'url', {}, nil)
end
end
context "a session with a response" do
setup do
@session = Webrat::MerbSession.new
@response = OpenStruct.new
@response.status = 200
@response.body = 'test response'
@session.instance_variable_set(:@response, @response)
end
it "should return body of a request as a response_body" do
@session.response_body.should == @response.body
end
it "should return status of a request as a response_code" do
@session.response_code.should == @response.status
end
end
end

View File

@ -0,0 +1,75 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "Nokogiri Extension" do
include Webrat::Matchers
def fail
raise_error(Spec::Expectations::ExpectationNotMetError)
end
before(:each) do
@text_and_password = <<-HTML
<div>
<input type="text"/>
<input type="password"/>
<span type="text"/>
</div>
HTML
@text_only = <<-HTML
<div>
<input type="text" disabled="disabled" />
</div>
HTML
@password_only = <<-HTML
<div>
<input type="password"/>
<div>
HTML
end
describe ":text" do
it "passes have_selector(:text) if a node with type=text exists" do
@text_and_password.should have_selector(":text")
end
it "passes not have_selector(:text) if no node with text=text exists" do
@password_only.should_not have_selector(":text")
end
it "fails have_selector(:text) if no node with type=text exists" do
lambda { @password_only.should have_selector(":text") }.should fail
end
it "fails not have_selector(:text) if a node with type=text exists" do
lambda { @text_only.should_not have_selector(":text") }.should fail
end
it "works together with other selectors" do
@text_and_password.should have_selector("input:text[type*='te']")
end
end
describe ":password" do
it "passes have_selector(:password) if a node with type=password exists" do
@text_and_password.should have_selector(":password")
end
it "passes not have_selector(:text) if no node with text=text exists" do
@text_only.should_not have_selector(":password")
end
it "fails have_selector(:password) if no node with type=password exists" do
lambda { @text_only.should have_selector(":password") }.should fail
end
it "fails not have_selector(:password) if a node with type=password exists" do
lambda { @password_only.should_not have_selector(":password") }.should fail
end
it "works together with other selectors" do
@text_and_password.should have_selector("input:password[type*='pa']")
end
end
end

View File

@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "attaches_file" do
describe "attach_file" do
before do
@session = Webrat::TestSession.new
@ -14,7 +14,7 @@ describe "attaches_file" do
<form method="post" action="/widgets">
</form>
EOS
lambda { @session.attaches_file("Doc", "/some/path") }.should raise_error
lambda { @session.attach_file("Doc", "/some/path") }.should raise_error
end
it "should submit empty strings for blank file fields" do
@ -25,7 +25,7 @@ describe "attaches_file" do
</form>
EOS
@session.should_receive(:post).with("/widgets", { "widget" => { "file" => "" } })
@session.clicks_button
@session.click_button
end
it "should submit the attached file" do
@ -37,8 +37,8 @@ describe "attaches_file" do
</form>
EOS
@session.should_receive(:post).with("/widgets", { "widget" => { "file" => @uploaded_file } })
@session.attaches_file "Document", @filename
@session.clicks_button
@session.attach_file "Document", @filename
@session.click_button
end
it "should support collections" do
@ -52,9 +52,9 @@ describe "attaches_file" do
</form>
EOS
@session.should_receive(:post).with("/widgets", { "widget" => { "files" => [@uploaded_file, @uploaded_file] } })
@session.attaches_file "Document", @filename
@session.attaches_file "Spreadsheet", @filename
@session.clicks_button
@session.attach_file "Document", @filename
@session.attach_file "Spreadsheet", @filename
@session.click_button
end
it "should allow the content type to be specified" do
@ -66,7 +66,7 @@ describe "attaches_file" do
</form>
EOS
ActionController::TestUploadedFile.should_receive(:new).with(@filename, "image/png").any_number_of_times
@session.attaches_file "Picture", @filename, "image/png"
@session.clicks_button
@session.attach_file "Picture", @filename, "image/png"
@session.click_button
end
end
end

View File

@ -0,0 +1,9 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
require "active_support"
silence_warnings do
require "action_controller"
require "action_controller/integration"
end
require "webrat/rails"

View File

@ -1,4 +1,4 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe Webrat::RailsSession do
it "should require a Rails Integration session to be initialized" do
@ -19,31 +19,31 @@ describe Webrat::RailsSession do
Webrat::RailsSession.new(integration_session).response_code.should == 42
end
it "should delegate get to get_via_redirect on the integration session" do
it "should delegate get to request_via_redirect on the integration session" do
integration_session = mock("integration session")
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:get_via_redirect).with("url", "data", "headers")
integration_session.should_receive(:request_via_redirect).with(:get, "url", "data", "headers")
rails_session.get("url", "data", "headers")
end
it "should delegate post to post_via_redirect on the integration session" do
it "should delegate post to request_via_redirect on the integration session" do
integration_session = mock("integration session")
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:post_via_redirect).with("url", "data", "headers")
integration_session.should_receive(:request_via_redirect).with(:post, "url", "data", "headers")
rails_session.post("url", "data", "headers")
end
it "should delegate put to put_via_redirect on the integration session" do
it "should delegate put to request_via_redirect on the integration session" do
integration_session = mock("integration session")
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:put_via_redirect).with("url", "data", "headers")
integration_session.should_receive(:request_via_redirect).with(:put, "url", "data", "headers")
rails_session.put("url", "data", "headers")
end
it "should delegate delete to delete_via_redirect on the integration session" do
it "should delegate delete to request_via_redirect on the integration session" do
integration_session = mock("integration session")
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:delete_via_redirect).with("url", "data", "headers")
integration_session.should_receive(:request_via_redirect).with(:delete, "url", "data", "headers")
rails_session.delete("url", "data", "headers")
end
@ -51,14 +51,14 @@ describe Webrat::RailsSession do
it "should just pass on the path" do
integration_session = mock("integration session", :https! => nil)
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:get_via_redirect).with("/url", "data", "headers")
integration_session.should_receive(:request_via_redirect).with(:get, "/url", "data", "headers")
rails_session.get("http://www.example.com/url", "data", "headers")
end
end
context "the URL is https://" do
it "should call #https! with true before the request" do
integration_session = mock("integration session", :get_via_redirect => nil)
integration_session = mock("integration session", :request_via_redirect => nil)
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:https!).with(true)
rails_session.get("https://www.example.com/url", "data", "headers")
@ -67,7 +67,7 @@ describe Webrat::RailsSession do
context "the URL is http://" do
it "should call #https! with true before the request" do
integration_session = mock("integration session", :get_via_redirect => nil)
integration_session = mock("integration session", :request_via_redirect => nil)
rails_session = Webrat::RailsSession.new(integration_session)
integration_session.should_receive(:https!).with(false)
rails_session.get("http://www.example.com/url", "data", "headers")

View File

@ -7,7 +7,7 @@
# EOS
# @page.stub!(:url).and_return("/current")
# @session.should_receive(:get).with("/current", {})
# @session.clicks_button
# @session.click_button
# end
#
# it "should follow fully qualified secure local links" do
@ -16,5 +16,5 @@
# EOS
# @session.should_receive(:https!).with(true)
# @session.should_receive(:get).with("/page/sub", {})
# @session.clicks_link "Jump to sub page"
# @session.click_link "Jump to sub page"
# end