Compare commits

..

No commits in common. "master" and "0-4-stable" have entirely different histories.

186 changed files with 2723 additions and 5021 deletions

4
.gitignore vendored
View File

@ -12,7 +12,3 @@ log
results results
test_apps test_apps
*.tmproj *.tmproj
*.log
*.pid
bin
vendor/gems

13
Gemfile
View File

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

View File

@ -1,216 +1,3 @@
== 0.7.1 / 2010-04-26
* Minor enhancements
* Move verbose selenium output that can clutter build output behind setting
* Added application_port_for_selenium to webrat configuration. The use case is when you want to test through a web server sitting in front of your application server. (Luke Melia)
* New webrat configuration option selenium_firefox_profile which is passed to selenium server
* Allow submit_form to select by CSS too (Damian Janowski)
* Bug fixes
* Fix that current_url wasn't reflecting redirects in Mechanize [#332] (Emrys Ingersoll)
* Fix attach_file with nested params [#341] (Álvaro Gil)
* Fix that a 304 was considered a redirect (Larry Marburger)
* Fix selection of LABEL elements in Selenium mode under IE [#317] (Damian Janowski, Noah Davis)
* Fix have_xpath not matching negative expectation in the block [#182] (Luismi Cavallé)
== 0.7.0 / 2010-01-17
* Major enhancements
* Upgrade bundled Selenium server JAR to 2.0a1 (Henry Poydar and Jake Scruggs)
* Minor enhancements
* Save and open page directory specified via configuration, defaults to tmp dir otherwise current dir (Noah Davis)
* Bug fixes
* Added missing dependency "rack-test" to gemspec (LH #339) (Noah Davis)
* Removed save_and_open_page's rewriting of static asset paths before saving (was not actually working) (Noah Davis)
* Make "should contain" ignore extra whitespace when doing string comparisons (Noah Davis)
* Make selenium matchers handle negative match more consistently with positive match (Luke Melia)
== 0.6.0 / 2009-11-28
REMOVED: Support for Hpricot + REXML as an alternative to Nokogiri.
Hpricot and REXML were difficult to work with, REXML is terribly slow,
and Nokogiri is recommended even by the author of Hpricot (_why). Now
that Nokogiri works on JRuby, Webrat is going to use it as its sole
XML backend.
CHANGED: Due to a reorganization, if you're currently requiring "webrat/rspec-rails",
please change your code to require "webrat/integrations/rspec-rails"
* Minor enhancements
* Support Rails 2.3.4 JavaScript form authenticity tokens in simulated mode (Jonathan Weiss)
* When a timeout occurs in wait_for, include the HTML from Selenium in the exception
* Update the Merb support to be based directly on Rack (Simon Rozet)
* Support multiple select fields (Kieran P)
* When locating select options, always match against text, not HTML
* Bug fixes
* Remove newlines from HTTP Basic authentication credentials (Michael Klett)
* Require nokogiri form rspec-rails.rb (David Chelimsky)
* Fix logger issue when running inside Cucumber (Damian Janowski)
* Fix various issues related to submitting values with HTML entities (Kieran P)
* Call #to_i on the :count option in matchers (Michael Christenson II)
* Fix bug where multiline param values were getting truncated
== 0.5.3 / 2009-08-27
* Minor enhancements
* Remove unnecessary requires which are to the wrong paths on Edge Rails
== 0.5.1 / 2009-08-18
* Minor enhancements
* Base Webrat::MIME on Rack::Mime (Simon Rozet)
* Support file uploads in Rack mode (Simon Rozet)
* Bug fixes
* Fix bug in Webrat::Methods preventing Selenium mode from working [#277]
== 0.5.0 / 2009-08-12
* Major enhancements
* Depreacate :rack_test and :sinatra in favor of :rack, which uses Rack::Test (Simon Rozet)
* Minor enhancements
* Don't require rubygems at runtime (Simon Rozet)
== 0.4.5 / 2009-08-10
* Major enhancements
* Ruby 1.9 compatibility (Michael Fellinger, Jakub Kuźma)
* Improve performance (~2x) on JRuby by supporting Nokogiri
* Added support for external app servers in selenium mode (basically don't start one) (Mike Gaffney)
* Added support for uploading files for Merb (Ryan Carver)
* Minor enhancements
* Upgrade to selenium-client to 1.2.16 (Brian Landau)
* Upgrade selenium-server.jar to 1.0.1 (Brian Landau)
* Make redirect detection work in the face of rational maths (like when ruby-units is active) (Piers Cawley)
* Use Launchy to handle opening pages in the browser with cross-platform compatibility (Bryan Helmkamp)
* Added support for field_labeled_locators ending in non word characters
lh 148 (Zach Dennis)
* Filled in tests on click link lh 195 (diabolo)
* Added current_url to selenium session lh 215 (Luke Amdor)
* Added silence spec to selenium lh 238 (Martin Gamsjaeger aka snusnu)
* Added ability to configure the browser startup timeout for selenium lh 242 (Mike Gaffney, Mark Dodwell)
* Added delegation for field_named lh194 (pivotal labs)
* Added fix to keep from escaping field values in mechanize mode lh256 (jish)
* Adding fixes for click button/link and made the integration specs pass for the same in selenium lh254 (Ldiehl, Matthias Marschall)
* Adding clicking link by id in selenium mode lh221 (larrytheliquid)
* Adding fix for have_selector and have_xpath in descendent blocks lh234 (Thomas Jack)
* Adding fix for fields with labels with special characters (Thomas Jack, Mike Gaffney, Bryan Hemlkamp)
* Deprecated current_page lh50 (Mike Gaffney)
* Fixed issue with redirects and multiple hosts lh168 (Mutwin Kraus)
* Bug fixes
* Wait for application servers socket on 0.0.0.0 instead of the application_address
* Translate CSS and image paths with single quotes in save_and_open_page (Erin Staniland)
== 0.4.4 / 2009-04-06
* Major enhancements
* Make selenium process management code more robust and informative
* Minor enhancements
* Add support for Rails javascript post links (Mark Menard)
* Upgrade selenium-client dependency to 1.2.14, and update for new waiting
API (Balint Erdi)
* Change default app environment from "selenium" to "test"
* Bug fixes
* Don't create a new instance of WWW::Mechanize for each request
(Mark Menard)
* Select fields with duplicate selected options sent an incorrect value (Noah Davis)
== 0.4.3 / 2009-03-17
* Minor enhancements
* Support Rails 2.3. Use Rack::Utils to parse params (Matthew Ford)
* Support for "modular" Sinatra app style (Simon Rozet)
* Initial Merb and Sinatra compatibility for Selenium mode (Corey Donohoe)
* When faced with a label with no for attribute, that contains a hidden field
and another field, as can be the case in Rails 2.3's checkbox view,
webrat now locates the non-hidden field. (Luke Melia)
* Add application_framework config for Selenium mode to determine how to
start and stop the app server (Corey Donohoe)
* Bug fixes
* Fix following of absolute redirect URL in Sinatra (Simon Rozet)
== 0.4.2 / 2009-02-24
* Major enhancements
* Significant improvements to have_selector. It now supports specifying
attributes in a hash and :count and :content options. See
have_selector_spec.rb for more.
* Add the same functionality mentioned above to have_xpath
* Minor enhancements
* Squeeze extra whitespace out of failures messages from contain
matcher
* Detect infinite redirects and raise a Webrat::InfiniteRedirectError
(Daniel Lucraft)
* Bug fixes
* Properly quote single and double quotes strings in XPath
* Fix warning caused by Nokogiri deprecating CSS::Parser.parse
(Aaron Patterson)
* Accept do/end blocks in matchers. [#157] (Peter Jaros)
* Quote --chdir option to mongrel_rails to support RAILS_ROOTs with spaces
(T.J. VanSlyke)
== 0.4.1 / 2009-01-31
* Minor enhancements
* Support Sinatra 0.9 (Harry Vangberg)
* Update query param parsing to work with latest Edge Rails
* Added #redirected_to method to easily check where an external redirect was
redirected to (Adam Greene)
* Recognize input tags with type button (Lena Herrmann)
* Add uncheck method to Selenium mode (Lee Bankewitz)
* Bug fixes
* Make requests to a Rails app using a full URL instead of a relative path. This change
is helpful for Rails apps that use subdomains. (John Hwang and Zach Dennis)
* Follow redirects that are on the same domain but a different subdomain
(Adam Greene)
* rescue from Webrat::TimeoutError in selenium matchers which allows NegativeMatchers
to behave correctly (Noah Davis)
* Switch to using selenium.click instead of .check when checking a checkbox
(Noah Davis)
* Create tmp/pids directory if directory does not exist. (Amos King and Mike Gaffney)
* Setup deprecated writers for the selenium_environment= and selenium_port= config
* Ensure the previous pages params aren't passed through redirect (Daniel Lucraft and
Bryan Helmkamp)
* Labels should only search for fields within the current scope (Kyle Hargraves)
== 0.4.0 / 2009-01-18 == 0.4.0 / 2009-01-18
* _IMPORTANT_ Breaking change: * _IMPORTANT_ Breaking change:

180
Rakefile
View File

@ -1,67 +1,93 @@
begin # require 'rubygems'
require 'spec/rake/spectask' require "rake/gempackagetask"
rescue LoadError require 'rake/rdoctask'
desc "Run specs" require "rake/clean"
task(:spec) { $stderr.puts '`gem install rspec` to run specs' } require 'spec'
else require 'spec/rake/spectask'
desc "Run API and Core specs" require 'spec/rake/verify_rcov'
Spec::Rake::SpecTask.new do |t| require File.expand_path('./lib/webrat.rb')
##############################################################################
# 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 install.rb MIT-LICENSE.txt README.rdoc Rakefile) + Dir["lib/**/*"] + Dir["vendor/**/*"]
# rdoc
s.has_rdoc = true
s.extra_rdoc_files = %w(README.rdoc MIT-LICENSE.txt)
# Dependencies
s.add_dependency "nokogiri", ">= 1.1.0"
s.rubyforge_project = "webrat"
end
Rake::GemPackageTask.new(spec) do |package|
package.gem_spec = spec
end
desc 'Show information about the gem.'
task :debug_gem do
puts spec.to_ruby
end
CLEAN.include ["pkg", "*.gem", "doc", "ri", "coverage"]
desc "Upload rdoc to brynary.com"
task :publish_rdoc => :docs do
sh "scp -r doc/ brynary.com:/apps/uploads/webrat"
end
desc "Run API and Core specs"
Spec::Rake::SpecTask.new do |t|
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""] t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
t.spec_files = FileList['spec/public/**/*_spec.rb'] + FileList['spec/private/**/*_spec.rb'] t.spec_files = FileList['spec/public/**/*_spec.rb'] + FileList['spec/private/**/*_spec.rb']
end end
desc "Run all specs in spec directory with RCov" desc "Run all specs in spec directory with RCov"
Spec::Rake::SpecTask.new(:rcov) do |t| Spec::Rake::SpecTask.new(:rcov) do |t|
t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""] t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
t.spec_files = FileList['spec/public/**/*_spec.rb'] + FileList['spec/private/**/*_spec.rb'] t.spec_files = FileList['spec/public/**/*_spec.rb'] + FileList['spec/private/**/*_spec.rb']
t.rcov = true t.rcov = true
t.rcov_opts = lambda do t.rcov_opts = lambda do
IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
end end
end
end end
desc "Run everything against multiruby" RCov::VerifyTask.new(:verify_rcov => :rcov) do |t|
task :multiruby do t.threshold = 96.2 # Make sure you have rcov 0.7 or higher!
result = system "multiruby -S rake spec" end
raise "Multiruby tests failed" unless result
result = system "jruby -S rake spec"
raise "JRuby tests failed" unless result
Dir.chdir "spec/integration/rails" do desc 'Install the package as a gem.'
result = system "multiruby -S rake test_unit:rails" task :install_gem => [:clean, :package] do
raise "Rails integration tests failed" unless result gem_filename = Dir['pkg/*.gem'].first
sh "sudo gem install --local #{gem_filename}"
end
result = system "jruby -S rake test_unit:rails" desc "Delete generated RDoc"
raise "Rails integration tests failed" unless result task :clobber_docs do
end FileUtils.rm_rf("doc")
end
Dir.chdir "spec/integration/merb" do desc "Generate RDoc"
result = system "multiruby -S rake spec" task :docs => :clobber_docs do
raise "Merb integration tests failed" unless result system "hanna --title 'Webrat #{Webrat::VERSION} API Documentation'"
end
result = system "jruby -S rake spec" desc "Run specs using jruby"
raise "Rails integration tests failed" unless result task "spec:jruby" do
end system "jruby -S rake spec"
Dir.chdir "spec/integration/sinatra" do
result = system "multiruby -S rake test"
raise "Sinatra integration tests failed" unless result
result = system "jruby -S rake test"
raise "Sinatra integration tests failed" unless result
end
Dir.chdir "spec/integration/rack" do
result = system "multiruby -S rake test"
raise "Rack integration tests failed" unless result
result = system "jruby -S rake test"
raise "Rack integration tests failed" unless result
end
puts
puts "Multiruby OK!"
end end
desc "Run each spec in isolation to test for dependency issues" desc "Run each spec in isolation to test for dependency issues"
@ -73,38 +99,27 @@ task :spec_deps do
end end
end end
task :prepare do
system "ln -s ../../../../.. ./spec/integration/rails/vendor/plugins/webrat"
end
namespace :spec do namespace :spec do
desc "Run the integration specs" desc "Run the integration specs"
task :integration => [ task :integration => ["integration:rails", "integration:merb", "integration:sinatra"]
"integration:rack",
"integration:sinatra",
"integration:merb",
"integration:mechanize",
"integration:rails:webrat",
"integration:rails:selenium",
]
namespace :integration do namespace :integration do
namespace :rails do desc "Run the Rails integration specs"
task :selenium do task :rails do
Dir.chdir "spec/integration/rails" do Dir.chdir "spec/integration/rails" do
result = system "rake -rubygems test_unit:selenium" result = system "rake test:integration"
raise "Rails integration tests failed" unless result raise "Rails integration tests failed" unless result
end end
end end
task :webrat do
Dir.chdir "spec/integration/rails" do
result = system "rake -rubygems test_unit:rails"
raise "Rails integration tests failed" unless result
end
end
end
desc "Run the Merb integration specs" desc "Run the Merb integration specs"
task :merb do task :merb do
Dir.chdir "spec/integration/merb" do Dir.chdir "spec/integration/merb" do
result = system "rake -rubygems spec" result = system "rake spec"
raise "Merb integration tests failed" unless result raise "Merb integration tests failed" unless result
end end
end end
@ -112,32 +127,13 @@ namespace :spec do
desc "Run the Sinatra integration specs" desc "Run the Sinatra integration specs"
task :sinatra do task :sinatra do
Dir.chdir "spec/integration/sinatra" do Dir.chdir "spec/integration/sinatra" do
result = system "rake -rubygems test" result = system "rake test"
raise "Sinatra integration tests failed" unless result raise "Sinatra tntegration tests failed" unless result
end
end
desc "Run the Sinatra integration specs"
task :rack do
Dir.chdir "spec/integration/rack" do
result = system "rake -rubygems test"
raise "Rack integration tests failed" unless result
end
end
desc "Run the Mechanize integration specs"
task :mechanize do
Dir.chdir "spec/integration/mechanize" do
result = system "rake -rubygems spec"
raise "Mechanize integration tests failed" unless result
end end
end end
end end
end end
desc 'Removes trailing whitespace'
task :whitespace do
sh %{find . -name '*.rb' -exec sed -i '' 's/ *$//g' {} \\;}
end
task :default => :spec task :default => :spec
task :precommit => ["spec", "spec:jruby", "spec:integration"]

118
Thorfile
View File

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

View File

@ -1,18 +1,34 @@
require "rack" require "rubygems"
require "nokogiri"
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
module Webrat module Webrat
VERSION = "0.7.2.pre"
autoload :MechanizeAdapter, "webrat/adapters/mechanize"
autoload :MerbAdapter, "webrat/adapters/merb"
autoload :RackAdapter, "webrat/adapters/rack"
autoload :RailsAdapter, "webrat/adapters/rails"
autoload :SinatraAdapter, "webrat/adapters/sinatra"
# The common base class for all exceptions raised by Webrat. # The common base class for all exceptions raised by Webrat.
class WebratError < StandardError class WebratError < StandardError
end end
VERSION = '0.4.0'
def self.require_xml
gem "nokogiri", ">= 1.0.6"
if on_java?
# We need Nokogiri's CSS to XPath support, even if using REXML and Hpricot for parsing and searching
require "nokogiri/css"
require "hpricot"
require "rexml/document"
else
require "nokogiri"
require "webrat/core/xml/nokogiri"
end
end
def self.on_java?
RUBY_PLATFORM =~ /java/
end
end end
Webrat.require_xml
require "webrat/core" require "webrat/core"

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
require "webrat/core/configuration" require "webrat/core/configuration"
require "webrat/core/xml" require "webrat/core/xml"
require "webrat/core/xml/nokogiri"
require "webrat/core/logging" require "webrat/core/logging"
require "webrat/core/elements/form" require "webrat/core/elements/form"
require "webrat/core/scope" require "webrat/core/scope"

View File

@ -1,5 +1,4 @@
require "webrat/core_extensions/deprecate" require "webrat/core_extensions/deprecate"
require "pathname"
module Webrat module Webrat
@ -17,38 +16,26 @@ module Webrat
# Webrat can be configured using the Webrat.configure method. For example: # Webrat can be configured using the Webrat.configure method. For example:
# #
# Webrat.configure do |config| # Webrat.configure do |config|
# config.mode = :sinatra # config.parse_with_nokogiri = false
# end # end
class Configuration class Configuration
# Should XHTML be parsed with Nokogiri? Defaults to true, except on JRuby. When false, Hpricot and REXML are used
attr_writer :parse_with_nokogiri
# Webrat's mode, set automatically when requiring webrat/rails, webrat/merb, etc. # Webrat's mode, set automatically when requiring webrat/rails, webrat/merb, etc.
attr_reader :mode # :nodoc: attr_reader :mode # :nodoc:
# Save and open pages with error status codes (500-599) in a browser? Defualts to true. # Save and open pages with error status codes (500-599) in a browser? Defualts to true.
attr_writer :open_error_files attr_writer :open_error_files
# Save and open page storage directory, defaults to "tmp" under current directory if exists, otherwise current directory # Which rails environment should the selenium tests be run in? Defaults to selenium.
attr_accessor :saved_pages_dir
# Which rails environment should the selenium tests be run in? Defaults to test.
attr_accessor :application_environment attr_accessor :application_environment
webrat_deprecate :selenium_environment, :application_environment webrat_deprecate :selenium_environment, :application_environment
webrat_deprecate :selenium_environment=, :application_environment=
# Which port is the application running on for selenium testing? Defaults to 3001. # Which port is the application running on for selenium testing? Defaults to 3001.
attr_accessor :application_port attr_accessor :application_port
webrat_deprecate :selenium_port, :application_port webrat_deprecate :selenium_port, :application_port
webrat_deprecate :selenium_port=, :application_port=
# Which port should selenium use to access the application. Defaults to application_port
attr_writer :application_port_for_selenium
def application_port_for_selenium
@application_port_for_selenium || self.application_port
end
# Which underlying app framework we're testing with selenium
attr_accessor :application_framework
# Which server the application is running on for selenium testing? Defaults to localhost # Which server the application is running on for selenium testing? Defaults to localhost
attr_accessor :application_address attr_accessor :application_address
@ -62,34 +49,18 @@ module Webrat
# Set the key that Selenium uses to determine the browser running. Default *firefox # Set the key that Selenium uses to determine the browser running. Default *firefox
attr_accessor :selenium_browser_key attr_accessor :selenium_browser_key
# Set the timeout for waiting for the browser process to start
attr_accessor :selenium_browser_startup_timeout
# Set the firefox profile for selenium to use
attr_accessor :selenium_firefox_profile
# How many redirects to the same URL should be halted as an infinite redirect
# loop? Defaults to 10
attr_accessor :infinite_redirect_limit
# Print out the full HTML on wait failure
# Defaults to false
attr_accessor :selenium_verbose_output
def initialize # :nodoc: def initialize # :nodoc:
self.open_error_files = true self.open_error_files = true
self.application_environment = :test self.parse_with_nokogiri = !Webrat.on_java?
self.application_environment = :selenium
self.application_port = 3001 self.application_port = 3001
self.application_address = 'localhost' self.application_address = 'localhost'
self.application_framework = :rails
self.selenium_server_port = 4444 self.selenium_server_port = 4444
self.infinite_redirect_limit = 10
self.selenium_browser_key = '*firefox' self.selenium_browser_key = '*firefox'
self.selenium_browser_startup_timeout = 5 end
self.selenium_verbose_output = false
tmp_dir = Pathname.new(Dir.pwd).join("tmp") def parse_with_nokogiri? #:nodoc:
self.saved_pages_dir = tmp_dir.exist? ? tmp_dir : Dir.pwd @parse_with_nokogiri ? true : false
end end
def open_error_files? #:nodoc: def open_error_files? #:nodoc:
@ -101,11 +72,13 @@ module Webrat
def mode=(mode) def mode=(mode)
@mode = mode.to_sym @mode = mode.to_sym
begin # This is a temporary hack to support backwards compatibility
require("webrat/integrations/#{mode}") # with Merb 1.0.8 until it's updated to use the new Webrat.configure
rescue LoadError # syntax
# Only some modes have integration code that needs to if @mode == :merb
# be loaded, so this is OK require("webrat/merb_session")
else
require("webrat/#{mode}")
end end
end end

View File

@ -4,7 +4,7 @@ module Webrat
class Area < Element #:nodoc: class Area < Element #:nodoc:
def self.xpath_search def self.xpath_search
[".//area"] ".//area"
end end
def click(method = nil, options = {}) def click(method = nil, options = {})
@ -14,7 +14,7 @@ module Webrat
protected protected
def href def href
@element["href"] Webrat::XML.attribute(@element, "href")
end end
def absolute_href def absolute_href

View File

@ -3,14 +3,14 @@ module Webrat
class Element # :nodoc: class Element # :nodoc:
def self.load_all(session, dom) def self.load_all(session, dom)
dom.xpath(*xpath_search).map do |element| Webrat::XML.xpath_search(dom, xpath_search).map do |element|
load(session, element) load(session, element)
end end
end end
def self.load(session, element) def self.load(session, element)
return nil if element.nil? return nil if element.nil?
session.elements[element.path] ||= self.new(session, element) session.elements[Webrat::XML.xpath_to(element)] ||= self.new(session, element)
end end
attr_reader :element attr_reader :element
@ -21,7 +21,7 @@ module Webrat
end end
def path def path
@element.path Webrat::XML.xpath_to(@element)
end end
def inspect def inspect

View File

@ -1,7 +1,6 @@
require "cgi" require "cgi"
require "digest/md5"
require "webrat/core_extensions/blank" require "webrat/core_extensions/blank"
require "webrat/core_extensions/nil_to_query_string" require "webrat/core_extensions/nil_to_param"
require "webrat/core/elements/element" require "webrat/core/elements/element"
@ -14,11 +13,7 @@ module Webrat
attr_reader :value attr_reader :value
def self.xpath_search def self.xpath_search
".//button|.//input|.//textarea|.//select" [".//button", ".//input", ".//textarea", ".//select"]
end
def self.xpath_search_excluding_hidden
[".//button", ".//input[ @type != 'hidden']", ".//textarea", ".//select"]
end end
def self.field_classes def self.field_classes
@ -33,21 +28,16 @@ module Webrat
def self.load(session, element) def self.load(session, element)
return nil if element.nil? return nil if element.nil?
session.elements[element.path] ||= field_class(element).new(session, element) session.elements[Webrat::XML.xpath_to(element)] ||= field_class(element).new(session, element)
end end
def self.field_class(element) def self.field_class(element)
case element.name case element.name
when "button" then ButtonField when "button" then ButtonField
when "select" when "select" then SelectField
if element.attributes["multiple"].nil?
SelectField
else
MultipleSelectField
end
when "textarea" then TextareaField when "textarea" then TextareaField
else else
case element["type"] case Webrat::XML.attribute(element, "type")
when "checkbox" then CheckboxField when "checkbox" then CheckboxField
when "hidden" then HiddenField when "hidden" then HiddenField
when "radio" then RadioField when "radio" then RadioField
@ -55,7 +45,6 @@ module Webrat
when "file" then FileField when "file" then FileField
when "reset" then ResetField when "reset" then ResetField
when "submit" then ButtonField when "submit" then ButtonField
when "button" then ButtonField
when "image" then ButtonField when "image" then ButtonField
else TextField else TextField
end end
@ -73,11 +62,11 @@ module Webrat
end end
def id def id
@element["id"] Webrat::XML.attribute(@element, "id")
end end
def disabled? def disabled?
@element.attributes.has_key?("disabled") && @element["disabled"] != 'false' @element.attributes.has_key?("disabled") && Webrat::XML.attribute(@element, "disabled") != 'false'
end end
def raise_error_if_disabled def raise_error_if_disabled
@ -85,17 +74,17 @@ module Webrat
raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})") raise DisabledFieldError.new("Cannot interact with disabled form element (#{self})")
end end
def to_query_string def to_param
return nil if disabled? return nil if disabled?
query_string = case Webrat.configuration.mode case Webrat.configuration.mode
when :rails, :merb, :rack, :sinatra when :rails
build_query_string rails_request_parser.parse_query_parameters("#{name}=#{escaped_value}")
when :mechanize when :merb
build_query_string(false) ::Merb::Parse.query("#{name}=#{escaped_value}")
else
{ name => escaped_value }
end end
query_string
end end
def set(value) def set(value)
@ -108,6 +97,14 @@ module Webrat
protected protected
def rails_request_parser
if defined?(ActionController::RequestParser) # For Rails > 2.2
ActionController::RequestParser
else
ActionController::AbstractRequest
end
end
def form def form
Form.load(@session, form_element) Form.load(@session, form_element)
end end
@ -122,19 +119,7 @@ module Webrat
end end
def name def name
@element["name"] Webrat::XML.attribute(@element, "name")
end
def build_query_string(escape_value=true)
if @value.is_a?(Array)
@value.collect {|value| "#{name}=#{ escape_value ? escape(value) : value }" }.join("&")
else
"#{name}=#{ escape_value ? escape(value) : value }"
end
end
def escape(value)
CGI.escape(value.to_s)
end end
def escaped_value def escaped_value
@ -161,24 +146,40 @@ module Webrat
end end
unless id.blank? unless id.blank?
@label_elements += form.element.xpath(".//label[@for = '#{id}']") @label_elements += Webrat::XML.xpath_search(form.element, ".//label[@for = '#{id}']")
end end
@label_elements @label_elements
end end
def default_value def default_value
@element["value"] Webrat::XML.attribute(@element, "value")
end
def replace_param_value(params, oval, nval)
output = Hash.new
params.each do |key, value|
case value
when Hash
value = replace_param_value(value, oval, nval)
when Array
value = value.map { |o| o == oval ? nval : oval }
when oval
value = nval
end
output[key] = value
end
output
end end
end end
class ButtonField < Field #:nodoc: class ButtonField < Field #:nodoc:
def self.xpath_search def self.xpath_search
[".//button", ".//input[@type = 'submit']", ".//input[@type = 'button']", ".//input[@type = 'image']"] [".//button", ".//input[@type = 'submit']", ".//input[@type = 'image']"]
end end
def to_query_string def to_param
return nil if @value.nil? return nil if @value.nil?
super super
end end
@ -189,7 +190,7 @@ module Webrat
def click def click
raise_error_if_disabled raise_error_if_disabled
set(@element["value"]) unless @element["name"].blank? set(Webrat::XML.attribute(@element, "value")) unless Webrat::XML.attribute(@element, "name").blank?
form.submit form.submit
end end
@ -201,13 +202,13 @@ module Webrat
".//input[@type = 'hidden']" ".//input[@type = 'hidden']"
end end
def to_query_string def to_param
if collection_name? if collection_name?
super super
else else
checkbox_with_same_name = form.field_named(name, CheckboxField) checkbox_with_same_name = form.field_named(name, CheckboxField)
if checkbox_with_same_name.to_query_string.blank? if checkbox_with_same_name.to_param.blank?
super super
else else
nil nil
@ -229,18 +230,18 @@ module Webrat
".//input[@type = 'checkbox']" ".//input[@type = 'checkbox']"
end end
def to_query_string def to_param
return nil if @value.nil? return nil if @value.nil?
super super
end end
def check def check
raise_error_if_disabled raise_error_if_disabled
set(@element["value"] || "on") set(Webrat::XML.attribute(@element, "value") || "on")
end end
def checked? def checked?
@element["checked"] == "checked" Webrat::XML.attribute(@element, "checked") == "checked"
end end
def uncheck def uncheck
@ -251,8 +252,8 @@ module Webrat
protected protected
def default_value def default_value
if @element["checked"] == "checked" if Webrat::XML.attribute(@element, "checked") == "checked"
@element["value"] || "on" Webrat::XML.attribute(@element, "value") || "on"
else else
nil nil
end end
@ -274,7 +275,7 @@ module Webrat
".//input[@type = 'radio']" ".//input[@type = 'radio']"
end end
def to_query_string def to_param
return nil if @value.nil? return nil if @value.nil?
super super
end end
@ -285,11 +286,11 @@ module Webrat
option.set(nil) option.set(nil)
end end
set(@element["value"] || "on") set(Webrat::XML.attribute(@element, "value") || "on")
end end
def checked? def checked?
@element["checked"] == "checked" Webrat::XML.attribute(@element, "checked") == "checked"
end end
protected protected
@ -299,8 +300,8 @@ module Webrat
end end
def default_value def default_value
if @element["checked"] == "checked" if Webrat::XML.attribute(@element, "checked") == "checked"
@element["value"] || "on" Webrat::XML.attribute(@element, "value") || "on"
else else
nil nil
end end
@ -317,7 +318,7 @@ module Webrat
protected protected
def default_value def default_value
@element.inner_html Webrat::XML.inner_html(@element)
end end
end end
@ -331,32 +332,25 @@ module Webrat
attr_accessor :content_type attr_accessor :content_type
def set(value, content_type = nil) def set(value, content_type = nil)
@original_value = @value
@content_type ||= content_type
super(value) super(value)
@content_type = content_type
end end
def digest_value def to_param
@value ? Digest::MD5.hexdigest(self.object_id.to_s) : "" if @value.nil?
end
def to_query_string
@value.nil? ? set("") : set(digest_value)
super super
else
replace_param_value(super, @value, test_uploaded_file)
end end
end
protected
def test_uploaded_file def test_uploaded_file
return "" if @original_value.blank?
case Webrat.configuration.mode
when :rails
if content_type if content_type
ActionController::TestUploadedFile.new(@original_value, content_type) ActionController::TestUploadedFile.new(@value, content_type)
else else
ActionController::TestUploadedFile.new(@original_value) ActionController::TestUploadedFile.new(@value)
end
when :rack, :merb
Rack::Test::UploadedFile.new(@original_value, content_type)
end end
end end
@ -370,69 +364,31 @@ module Webrat
class ResetField < Field #:nodoc: class ResetField < Field #:nodoc:
def self.xpath_search def self.xpath_search
[".//input[@type = 'reset']"] ".//input[@type = 'reset']"
end end
end end
class SelectField < Field #:nodoc: class SelectField < Field #:nodoc:
def self.xpath_search def self.xpath_search
[".//select[not(@multiple)]"] ".//select"
end end
def options def options
@options ||= SelectOption.load_all(@session, @element) @options ||= SelectOption.load_all(@session, @element)
end end
def unset(value)
@value = nil
end
protected protected
def default_value def default_value
selected_options = @element.xpath(".//option[@selected = 'selected']") selected_options = Webrat::XML.xpath_search(@element, ".//option[@selected = 'selected']")
selected_options = @element.xpath(".//option[position() = 1]") if selected_options.empty? selected_options = Webrat::XML.xpath_search(@element, ".//option[position() = 1]") if selected_options.empty?
selected_options.map do |option| selected_options.map do |option|
return "" if option.nil? return "" if option.nil?
option["value"] || option.inner_html Webrat::XML.attribute(option, "value") || Webrat::XML.inner_html(option)
end.uniq.first end
end end
end end
class MultipleSelectField < Field #:nodoc:
def self.xpath_search
[".//select[@multiple='multiple']"]
end
def options
@options ||= SelectOption.load_all(@session, @element)
end
def set(value)
@value << value
end
def unset(value)
@value.delete(value)
end
protected
# Overwrite SelectField definition because we don't want to select the first option
# (mutliples don't select the first option unlike their non multiple versions)
def default_value
selected_options = @element.xpath(".//option[@selected = 'selected']")
selected_options.map do |option|
return "" if option.nil?
option["value"] || option.inner_html
end.uniq
end
end
end end

View File

@ -9,7 +9,7 @@ module Webrat
attr_reader :element attr_reader :element
def self.xpath_search def self.xpath_search
[".//form"] ".//form"
end end
def fields def fields
@ -27,7 +27,7 @@ module Webrat
protected protected
def dom def dom
@session.dom.xpath(path).first Webrat::XML.xpath_at(@session.dom, path)
end end
def fields_by_type(field_types) def fields_by_type(field_types)
@ -38,90 +38,66 @@ module Webrat
end end
end end
# iterate over all form fields to build a request querystring to get params from it,
# for file_field we made a work around to pass a digest as value to later replace it
# in params hash with the real file.
def params def params
query_string = [] all_params = {}
replaces = {}
fields.each do |field| fields.each do |field|
next if field.to_query_string.nil? next if field.to_param.nil?
replaces.merge!({field.digest_value => field.test_uploaded_file}) if field.is_a?(FileField) merge(all_params, field.to_param)
query_string << field.to_query_string
end end
query_params = self.class.query_string_to_params(query_string.join('&')) all_params
query_params = self.class.replace_params_values(query_params, replaces)
self.class.unescape_params(query_params)
end end
def form_method def form_method
@element["method"].blank? ? :get : @element["method"].downcase Webrat::XML.attribute(@element, "method").blank? ? :get : Webrat::XML.attribute(@element, "method").downcase
end end
def form_action def form_action
@element["action"].blank? ? @session.current_url : @element["action"] Webrat::XML.attribute(@element, "action").blank? ? @session.current_url : Webrat::XML.attribute(@element, "action")
end end
def self.replace_param_value(params, oval, nval) def merge(all_params, new_param)
output = Hash.new new_param.each do |key, value|
params.each do |key, value| case all_params[key]
case value when *hash_classes
when Hash merge_hash_values(all_params[key], value)
value = replace_param_value(value, oval, nval)
when Array when Array
value = value.map { |o| o == oval ? nval : ( o.is_a?(Hash) ? replace_param_value(o, oval, nval) : o) } all_params[key] += value
when oval
value = nval
end
output[key] = value
end
output
end
def self.replace_params_values(params, values)
values.each do |key, value|
params = replace_param_value(params, key, value)
end
params
end
def self.unescape_params(params)
case params.class.name
when 'Hash', 'Mash'
params.each { |key,value| params[key] = unescape_params(value) }
params
when 'Array'
params.collect { |value| unescape_params(value) }
else else
params.is_a?(String) ? CGI.unescapeHTML(params) : params all_params[key] = value
end
end end
end end
def self.query_string_to_params(query_string) def merge_hash_values(a, b) # :nodoc:
a.keys.each do |k|
if b.has_key?(k)
case [a[k], b[k]].map{|value| value.class}
when *hash_classes.zip(hash_classes)
a[k] = merge_hash_values(a[k], b[k])
b.delete(k)
when [Array, Array]
a[k] += b[k]
b.delete(k)
end
end
end
a.merge!(b)
end
def hash_classes
klasses = [Hash]
case Webrat.configuration.mode case Webrat.configuration.mode
when :rails when :rails
parse_rails_request_params(query_string) klasses << HashWithIndifferentAccess
when :merb when :merb
::Merb::Parse.query(query_string) klasses << Mash
when :rack, :sinatra
Rack::Utils.parse_nested_query(query_string)
else
Hash[query_string.split('&').map {|query| [ query.split('=').first, query.split('=').last ]}]
end
end end
def self.parse_rails_request_params(query_string) klasses
if defined?(ActionController::AbstractRequest)
ActionController::AbstractRequest.parse_query_parameters(query_string)
elsif defined?(ActionController::UrlEncodedPairParser)
ActionController::UrlEncodedPairParser.parse_query_parameters(query_string)
else
Rack::Utils.parse_nested_query(query_string)
end
end end
end end
end end

View File

@ -6,11 +6,11 @@ module Webrat
attr_reader :element attr_reader :element
def self.xpath_search def self.xpath_search
[".//label"] ".//label"
end end
def for_id def for_id
@element["for"] Webrat::XML.attribute(@element, "for")
end end
def field def field
@ -21,9 +21,9 @@ module Webrat
def field_element def field_element
if for_id.blank? if for_id.blank?
@element.xpath(*Field.xpath_search_excluding_hidden).first Webrat::XML.xpath_at(@element, *Field.xpath_search)
else else
@session.current_dom.css("#" + for_id).first Webrat::XML.css_search(@session.dom, "#" + for_id).first
end end
end end

View File

@ -1,13 +1,12 @@
require "English"
require "webrat/core_extensions/blank" require "webrat/core_extensions/blank"
require "webrat/core/elements/element" require "webrat/core/elements/element"
module Webrat module Webrat
class Link < Element #:nodoc: class Link < Element #:nodoc:
def self.xpath_search def self.xpath_search
[".//a[@href]"] ".//a[@href]"
end end
def click(options = {}) def click(options = {})
@ -26,7 +25,7 @@ module Webrat
protected protected
def id def id
@element["id"] Webrat::XML.attribute(@element, "id")
end end
def data def data
@ -34,11 +33,11 @@ module Webrat
end end
def title def title
@element["title"] Webrat::XML.attribute(@element, "title")
end end
def href def href
@element["href"] Webrat::XML.attribute(@element, "href")
end end
def absolute_href def absolute_href
@ -53,12 +52,12 @@ module Webrat
def authenticity_token def authenticity_token
return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") && return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") &&
( onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/ || onclick =~ /s\.setAttribute\('value', '(.{44})'\);/ ) onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/
$LAST_MATCH_INFO.captures.first $LAST_MATCH_INFO.captures.first
end end
def onclick def onclick
@element["onclick"] Webrat::XML.attribute(@element, "onclick")
end end
def http_method def http_method
@ -82,8 +81,6 @@ module Webrat
:delete :delete
elsif onclick.include?("m.setAttribute('value', 'put')") elsif onclick.include?("m.setAttribute('value', 'put')")
:put :put
elsif onclick.include?("m.setAttribute('value', 'post')")
:post
else else
raise Webrat::WebratError.new("No HTTP method for _method param in #{onclick.inspect}") raise Webrat::WebratError.new("No HTTP method for _method param in #{onclick.inspect}")
end end

View File

@ -4,7 +4,7 @@ module Webrat
class SelectOption < Element #:nodoc: class SelectOption < Element #:nodoc:
def self.xpath_search def self.xpath_search
[".//option"] ".//option"
end end
def choose def choose
@ -12,15 +12,6 @@ module Webrat
select.set(value) select.set(value)
end end
def unchoose
select.raise_error_if_disabled
select.unset(value)
end
def inner_text
@element.inner_text
end
protected protected
def select def select
@ -37,11 +28,7 @@ module Webrat
end end
def value def value
@element["value"] || @element.inner_html Webrat::XML.attribute(@element, "value") || Webrat::XML.inner_html(@element)
end
def label
@element.inner_html
end end
end end

View File

@ -13,7 +13,7 @@ module Webrat
module Locators module Locators
def field_by_xpath(xpath) def field_by_xpath(xpath)
Field.load(@session, dom.xpath(xpath).first) Field.load(@session, Webrat::XML.xpath_at(dom, xpath))
end end
end end

View File

@ -11,8 +11,8 @@ module Webrat
def area_element def area_element
area_elements.detect do |area_element| area_elements.detect do |area_element|
area_element["title"] =~ matcher || Webrat::XML.attribute(area_element, "title") =~ matcher ||
area_element["id"] =~ matcher Webrat::XML.attribute(area_element, "id") =~ matcher
end end
end end
@ -21,7 +21,7 @@ module Webrat
end end
def area_elements def area_elements
@dom.xpath(*Area.xpath_search) Webrat::XML.xpath_search(@dom, Area.xpath_search)
end end
def error_message def error_message

View File

@ -20,24 +20,24 @@ module Webrat
end end
def matches_id?(element) def matches_id?(element)
(@value.is_a?(Regexp) && element["id"] =~ @value) || (@value.is_a?(Regexp) && Webrat::XML.attribute(element, "id") =~ @value) ||
(!@value.is_a?(Regexp) && element["id"] == @value.to_s) (!@value.is_a?(Regexp) && Webrat::XML.attribute(element, "id") == @value.to_s)
end end
def matches_value?(element) def matches_value?(element)
element["value"] =~ /^\W*#{Regexp.escape(@value.to_s)}/i Webrat::XML.attribute(element, "value") =~ /^\W*#{Regexp.escape(@value.to_s)}/i
end end
def matches_html?(element) def matches_html?(element)
element.inner_html =~ /#{Regexp.escape(@value.to_s)}/i Webrat::XML.inner_html(element) =~ /#{Regexp.escape(@value.to_s)}/i
end end
def matches_alt?(element) def matches_alt?(element)
element["alt"] =~ /^\W*#{Regexp.escape(@value.to_s)}/i Webrat::XML.attribute(element, "alt") =~ /^\W*#{Regexp.escape(@value.to_s)}/i
end end
def button_elements def button_elements
@dom.xpath(*ButtonField.xpath_search) Webrat::XML.xpath_search(@dom, *ButtonField.xpath_search)
end end
def error_message def error_message

View File

@ -12,15 +12,15 @@ module Webrat
def field_element def field_element
field_elements.detect do |field_element| field_elements.detect do |field_element|
if @value.is_a?(Regexp) if @value.is_a?(Regexp)
field_element["id"] =~ @value Webrat::XML.attribute(field_element, "id") =~ @value
else else
field_element["id"] == @value.to_s Webrat::XML.attribute(field_element, "id") == @value.to_s
end end
end end
end end
def field_elements def field_elements
@dom.xpath(*Field.xpath_search) Webrat::XML.xpath_search(@dom, *Field.xpath_search)
end end
def error_message def error_message

View File

@ -20,12 +20,12 @@ module Webrat
def matching_label_elements def matching_label_elements
label_elements.select do |label_element| label_elements.select do |label_element|
text(label_element) =~ /^\W*#{Regexp.escape(@value.to_s)}(\b|\Z)/i text(label_element) =~ /^\W*#{Regexp.escape(@value.to_s)}\b/i
end end
end end
def label_elements def label_elements
@dom.xpath(*Label.xpath_search) Webrat::XML.xpath_search(@dom, Label.xpath_search)
end end
def error_message def error_message
@ -33,7 +33,7 @@ module Webrat
end end
def text(element) def text(element)
str = element.inner_text str = Webrat::XML.all_inner_text(element)
str.gsub!("\n","") str.gsub!("\n","")
str.strip! str.strip!
str.squeeze!(" ") str.squeeze!(" ")

View File

@ -11,19 +11,19 @@ module Webrat
def field_element def field_element
field_elements.detect do |field_element| field_elements.detect do |field_element|
field_element["name"] == @value.to_s Webrat::XML.attribute(field_element, "name") == @value.to_s
end end
end end
def field_elements def field_elements
@dom.xpath(*xpath_searches) Webrat::XML.xpath_search(@dom, *xpath_searches)
end end
def xpath_searches def xpath_searches
if @field_types.any? if @field_types.any?
@field_types.map { |field_type| field_type.xpath_search }.flatten @field_types.map { |field_type| field_type.xpath_search }.flatten
else else
Field.xpath_search Array(Field.xpath_search)
end end
end end

View File

@ -10,7 +10,7 @@ module Webrat
end end
def form_element def form_element
@dom.css("#" + @value).first || @dom.css(@value).first Webrat::XML.css_at(@dom, "#" + @value)
end end
end end

View File

@ -12,16 +12,16 @@ module Webrat
def label_element def label_element
label_elements.detect do |label_element| label_elements.detect do |label_element|
text(label_element) =~ /^\W*#{Regexp.escape(@value.to_s)}(\b|\Z)/i text(label_element) =~ /^\W*#{Regexp.escape(@value.to_s)}\b/i
end end
end end
def label_elements def label_elements
@dom.xpath(*Label.xpath_search) Webrat::XML.xpath_search(@dom, Label.xpath_search)
end end
def text(label_element) def text(label_element)
str = label_element.inner_text str = Webrat::XML.all_inner_text(label_element)
str.gsub!("\n","") str.gsub!("\n","")
str.strip! str.strip!
str.squeeze!(" ") str.squeeze!(" ")

View File

@ -10,7 +10,7 @@ module Webrat
end end
def link_element def link_element
matching_links.min { |a, b| a.inner_text.length <=> b.inner_text.length } matching_links.min { |a, b| Webrat::XML.all_inner_text(a).length <=> Webrat::XML.all_inner_text(b).length }
end end
def matching_links def matching_links
@ -27,33 +27,25 @@ module Webrat
matcher = /#{Regexp.escape(@value.to_s)}/i matcher = /#{Regexp.escape(@value.to_s)}/i
end end
replace_nbsp(link.inner_text) =~ matcher || replace_nbsp(Webrat::XML.all_inner_text(link)) =~ matcher ||
replace_nbsp_ref(link.inner_html) =~ matcher || replace_nbsp_ref(Webrat::XML.inner_html(link)) =~ matcher ||
link["title"] =~ matcher Webrat::XML.attribute(link, "title")=~ matcher
end end
def matches_id?(link) def matches_id?(link)
if @value.is_a?(Regexp) if @value.is_a?(Regexp)
link["id"] =~ @value ? true : false (Webrat::XML.attribute(link, "id") =~ @value) ? true : false
else else
link["id"] == @value ? true : false (Webrat::XML.attribute(link, "id") == @value) ? true : false
end end
end end
def link_elements def link_elements
@dom.xpath(*Link.xpath_search) Webrat::XML.xpath_search(@dom, *Link.xpath_search)
end end
def replace_nbsp(str) def replace_nbsp(str)
if str.respond_to?(:valid_encoding?) str.gsub([0xA0].pack('U'), ' ')
if str.valid_encoding?
str.gsub(/\xc2\xa0/u, ' ')
else
str.force_encoding('UTF-8').gsub(/\xc2\xa0/u, ' ')
end
else
str.gsub(/\xc2\xa0/u, ' ')
end
end end
def replace_nbsp_ref(str) def replace_nbsp_ref(str)

View File

@ -19,17 +19,17 @@ module Webrat
field.options.detect do |o| field.options.detect do |o|
if @option_text.is_a?(Regexp) if @option_text.is_a?(Regexp)
o.element.inner_text =~ @option_text Webrat::XML.inner_html(o.element) =~ @option_text
else else
o.inner_text == @option_text.to_s Webrat::XML.inner_html(o.element) == @option_text.to_s
end end
end end
else else
option_element = option_elements.detect do |o| option_element = option_elements.detect do |o|
if @option_text.is_a?(Regexp) if @option_text.is_a?(Regexp)
o.inner_text =~ @option_text Webrat::XML.inner_html(o) =~ @option_text
else else
o.inner_text == @option_text.to_s Webrat::XML.inner_html(o) == @option_text.to_s
end end
end end
@ -38,7 +38,7 @@ module Webrat
end end
def option_elements def option_elements
@dom.xpath(*SelectOption.xpath_search) Webrat::XML.xpath_search(@dom, *SelectOption.xpath_search)
end end
def error_message def error_message

View File

@ -1,5 +1,3 @@
require "logger"
module Webrat module Webrat
module Logging #:nodoc: module Logging #:nodoc:
@ -13,9 +11,9 @@ module Webrat
when :rails when :rails
defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : nil defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : nil
when :merb when :merb
::Merb.logger Merb.logger
else else
@logger ||= ::Logger.new("webrat.log") nil
end end
end end

View File

@ -7,12 +7,17 @@ module Webrat
end end
def matches?(stringlike) def matches?(stringlike)
@document = Webrat::XML.document(stringlike) if Webrat.configuration.parse_with_nokogiri?
@element = @document.inner_text @document = Webrat.nokogiri_document(stringlike)
else
@document = Webrat.hpricot_document(stringlike)
end
@element = Webrat::XML.inner_text(@document)
case @content case @content
when String when String
@element.gsub(/\s+/, ' ').include?(@content) @element.include?(@content)
when Regexp when Regexp
@element.match(@content) @element.match(@content)
end end
@ -21,17 +26,13 @@ module Webrat
# ==== Returns # ==== Returns
# String:: The failure message. # String:: The failure message.
def failure_message def failure_message
"expected the following element's content to #{content_message}:\n#{squeeze_space(@element)}" "expected the following element's content to #{content_message}:\n#{@element}"
end end
# ==== Returns # ==== Returns
# String:: The failure message to be displayed in negative matches. # String:: The failure message to be displayed in negative matches.
def negative_failure_message def negative_failure_message
"expected the following element's content to not #{content_message}:\n#{squeeze_space(@element)}" "expected the following element's content to not #{content_message}:\n#{@element}"
end
def squeeze_space(inner_text)
inner_text.gsub(/^\s*$/, "").squeeze("\n")
end end
def content_message def content_message

View File

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

View File

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

View File

@ -1,71 +1,57 @@
require "webrat/core/xml" require "webrat/core/xml/nokogiri"
require "webrat/core/xml/rexml"
module Webrat module Webrat
module Matchers module Matchers
class HaveXpath #:nodoc: class HaveXpath #:nodoc:
def initialize(expected, options = {}, &block) def initialize(expected, &block)
@expected = expected @expected = expected
@options = options
@block = block @block = block
end end
def matches?(stringlike, &block) def matches?(stringlike)
@block ||= block if Webrat.configuration.parse_with_nokogiri?
matched = matches(stringlike) matches_nokogiri?(stringlike)
@block.call(matched) if @block
if @options[:count]
matched.size == @options[:count].to_i
else else
matched.any? matches_rexml?(stringlike)
end end
end end
def matches(stringlike) def matches_rexml?(stringlike)
nokogiri_matches(stringlike) if REXML::Node === stringlike || Array === stringlike
end @query = query.map { |q| q.gsub(%r'//', './') }
def nokogiri_matches(stringlike)
if Nokogiri::XML::NodeSet === stringlike
@query = query.gsub(%r'^//', './/')
else else
@query = query @query = query
end end
add_options_conditions_to(@query) @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::XML.document(stringlike) @document = Webrat::XML.document(stringlike)
@document.xpath(*@query) matched = @document.xpath(*@query)
end matched.any? && (!@block || @block.call(matched))
def add_options_conditions_to(query)
add_attributes_conditions_to(query)
add_content_condition_to(query)
end
def add_attributes_conditions_to(query)
attribute_conditions = []
@options.each do |key, value|
next if [:content, :count].include?(key)
attribute_conditions << "@#{key} = #{xpath_escape(value)}"
end
if attribute_conditions.any?
query << "[#{attribute_conditions.join(' and ')}]"
end
end
def add_content_condition_to(query)
if @options[:content]
query << "[contains(., #{xpath_escape(@options[:content])})]"
end
end end
def query def query
@expected [@expected].flatten.compact
end end
# ==== Returns # ==== Returns
@ -79,23 +65,6 @@ module Webrat
def negative_failure_message def negative_failure_message
"expected following text to not match xpath #{@expected}:\n#{@document}" "expected following text to not match xpath #{@expected}:\n#{@document}"
end end
protected
def xpath_escape(string)
if string.include?("'") && string.include?('"')
parts = string.split("'").map do |part|
"'#{part}'"
end
"concat(" + parts.join(", \"'\", ") + ")"
elsif string.include?("'")
"\"#{string}\""
else
"'#{string}'"
end
end
end end
# Matches HTML content against an XPath query # Matches HTML content against an XPath query
@ -105,18 +74,18 @@ module Webrat
# #
# ==== Returns # ==== Returns
# HaveXpath:: A new have xpath matcher. # HaveXpath:: A new have xpath matcher.
def have_xpath(expected, options = {}, &block) def have_xpath(expected, &block)
HaveXpath.new(expected, options, &block) HaveXpath.new(expected, &block)
end end
alias_method :match_xpath, :have_xpath alias_method :match_xpath, :have_xpath
def assert_have_xpath(expected, options = {}, &block) def assert_have_xpath(expected, &block)
hs = HaveXpath.new(expected, options, &block) hs = HaveXpath.new(expected, &block)
assert hs.matches?(response_body), hs.failure_message assert hs.matches?(response_body), hs.failure_message
end end
def assert_have_no_xpath(expected, options = {}, &block) def assert_have_no_xpath(expected, &block)
hs = HaveXpath.new(expected, options, &block) hs = HaveXpath.new(expected, &block)
assert !hs.matches?(response_body), hs.negative_failure_message assert !hs.matches?(response_body), hs.negative_failure_message
end end

View File

@ -3,7 +3,7 @@ module Webrat
def self.delegate_to_session(*meths) def self.delegate_to_session(*meths)
meths.each do |meth| meths.each do |meth|
self.class_eval(<<-RUBY, __FILE__, __LINE__) self.class_eval <<-RUBY
def #{meth}(*args, &blk) def #{meth}(*args, &blk)
webrat_session.#{meth}(*args, &blk) webrat_session.#{meth}(*args, &blk)
end end
@ -16,11 +16,7 @@ module Webrat
end end
def webrat_session def webrat_session
@_webrat_session ||= begin @_webrat_session ||= ::Webrat.session_class.new(self)
session = Webrat.session_class.new
session.adapter = Webrat.adapter_class.new(self) if session.respond_to?(:adapter=)
session
end
end end
# all of these methods delegate to the @session, which should # all of these methods delegate to the @session, which should
@ -39,7 +35,6 @@ module Webrat
:unchecks, :uncheck, :unchecks, :uncheck,
:chooses, :choose, :chooses, :choose,
:selects, :select, :selects, :select,
:unselects, :unselect,
:attaches_file, :attach_file, :attaches_file, :attach_file,
:current_page, :current_page,
:current_url, :current_url,
@ -58,7 +53,9 @@ module Webrat
:field_by_xpath, :field_by_xpath,
:field_with_id, :field_with_id,
:selenium, :selenium,
:simulate, :automate, :simulate, :automate
:field_named
end end
end end

View File

@ -1,18 +1,29 @@
module Webrat #:nodoc: module Webrat #:nodoc:
module MIME #:nodoc: module MIME #:nodoc:
MIME_TYPES = Rack::Mime::MIME_TYPES.dup.merge(
".multipart_form" => "multipart/form-data",
".url_encoded_form" => "application/x-www-form-urlencoded"
).freeze
def mime_type(type) def self.mime_type(string_or_symbol) #:nodoc:
return type if type.nil? || type.to_s.include?("/") if string_or_symbol.is_a?(String)
type = ".#{type}" unless type.to_s[0] == ?. string_or_symbol
MIME_TYPES.fetch(type) { |invalid_type| else
raise ArgumentError.new("Invalid Mime type: #{invalid_type}") 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
module_function :mime_type
end end
end end

View File

@ -6,22 +6,44 @@ module Webrat
# Example: # Example:
# save_and_open_page # save_and_open_page
def save_and_open_page def save_and_open_page
return unless File.exist?(Webrat.configuration.saved_pages_dir) return unless File.exist?(saved_page_dir)
filename = "#{Webrat.configuration.saved_pages_dir}/webrat-#{Time.now.to_i}.html" filename = "#{saved_page_dir}/webrat-#{Time.now.to_i}.html"
File.open(filename, "w") do |f| File.open(filename, "w") do |f|
f.write response_body f.write rewrite_css_and_image_references(response_body)
end end
open_in_browser(filename) open_in_browser(filename)
end end
def open_in_browser(path) # :nodoc def open_in_browser(path) # :nodoc
require "launchy" platform = ruby_platform
Launchy::Browser.run(path) if platform =~ /cygwin/ || platform =~ /win32/
rescue LoadError `rundll32 url.dll,FileProtocolHandler #{path.gsub("/", "\\\\")}`
warn "Sorry, you need to install launchy to open pages: `gem install launchy`" elsif platform =~ /darwin/
`open #{path}`
end
end
def rewrite_css_and_image_references(response_html) # :nodoc:
return response_html unless doc_root
response_html.gsub(/"\/(stylesheets|images)/, doc_root + '/\1')
end
def saved_page_dir #:nodoc:
File.expand_path(".")
end
def doc_root #:nodoc:
nil
end
private
# accessor for testing
def ruby_platform
RUBY_PLATFORM
end end
end end

View File

@ -28,7 +28,6 @@ module Webrat
attr_reader :session attr_reader :session
def initialize(session, &block) #:nodoc: def initialize(session, &block) #:nodoc:
@selector, @dom = nil
@session = session @session = session
instance_eval(&block) if block_given? instance_eval(&block) if block_given?
@ -113,21 +112,6 @@ module Webrat
webrat_deprecate :selects, :select webrat_deprecate :selects, :select
# Verifies that a an option element exists on the current page with the specified
# text. You can optionally restrict the search to a specific select list by
# assigning <tt>options[:from]</tt> the value of the select list's name or
# a label. Remove the option's value before the form is submitted.
#
# Examples:
# unselect "January"
# unselect "February", :from => "event_month"
# unselect "February", :from => "Event Month"
def unselect(option_text, options={})
select_option(option_text, options[:from]).unchoose
end
webrat_deprecate :unselects, :unselect
DATE_TIME_SUFFIXES = { DATE_TIME_SUFFIXES = {
:year => '1i', :year => '1i',
:month => '2i', :month => '2i',
@ -225,8 +209,8 @@ module Webrat
# along with the form. An optional <tt>content_type</tt> may be given. # along with the form. An optional <tt>content_type</tt> may be given.
# #
# Example: # Example:
# attach_file "Resume", "/path/to/the/resume.txt" # attaches_file "Resume", "/path/to/the/resume.txt"
# attach_file "Photo", "/path/to/the/image.png", "image/png" # attaches_file "Photo", "/path/to/the/image.png", "image/png"
def attach_file(field_locator, path, content_type = nil) def attach_file(field_locator, path, content_type = nil)
locate_field(field_locator, FileField).set(path, content_type) locate_field(field_locator, FileField).set(path, content_type)
end end
@ -328,12 +312,12 @@ module Webrat
dom = Webrat::XML.html_document(@response_body) dom = Webrat::XML.html_document(@response_body)
end end
Webrat::XML.define_dom_method(@response, dom) Webrat.define_dom_method(@response, dom)
return dom return dom
end end
def scoped_dom def scoped_dom
@scope.dom.css(@selector).first Webrat::XML.css_at(@scope.dom, @selector)
end end
def locate_field(field_locator, *field_types) #:nodoc: def locate_field(field_locator, *field_types) #:nodoc:

View File

@ -9,34 +9,20 @@ module Webrat
class PageLoadError < WebratError class PageLoadError < WebratError
end end
class InfiniteRedirectError < WebratError
end
def self.session_class def self.session_class
if Webrat.configuration.mode == :selenium
SeleniumSession
else
Session
end
end
def self.adapter_class
case Webrat.configuration.mode case Webrat.configuration.mode
when :rails when :rails
RailsAdapter RailsSession
when :merb when :merb
MerbAdapter MerbSession
when :selenium
SeleniumSession
when :rack when :rack
RackAdapter RackSession
when :rack_test
warn("The :rack_test mode is deprecated. Please use :rack instead")
require "webrat/rack"
RackAdapter
when :sinatra when :sinatra
warn("The :sinatra mode is deprecated. Please use :rack instead") SinatraSession
SinatraAdapter
when :mechanize when :mechanize
MechanizeAdapter MechanizeSession
else else
raise WebratError.new(<<-STR) raise WebratError.new(<<-STR)
Unknown Webrat mode: #{Webrat.configuration.mode.inspect} Unknown Webrat mode: #{Webrat.configuration.mode.inspect}
@ -59,23 +45,16 @@ For example:
extend Forwardable extend Forwardable
include Logging include Logging
include SaveAndOpenPage include SaveAndOpenPage
attr_accessor :adapter
attr_reader :current_url attr_reader :current_url
attr_reader :elements attr_reader :elements
def_delegators :@adapter, :response, :response_code, :response_body, :response_headers, def initialize(context = nil) #:nodoc:
:response_body=, :response_code=,
:get, :post, :put, :delete
def initialize(adapter = nil)
@adapter = adapter
@http_method = :get @http_method = :get
@data = {} @data = {}
@default_headers = {} @default_headers = {}
@custom_headers = {} @custom_headers = {}
@current_url = nil @context = context
reset reset
end end
@ -85,7 +64,6 @@ For example:
# For backwards compatibility -- removing in 1.0 # For backwards compatibility -- removing in 1.0
def current_page #:nodoc: def current_page #:nodoc:
warn "current_page is deprecated and will be going away in the next release. Use current_url instead."
page = OpenStruct.new page = OpenStruct.new
page.url = @current_url page.url = @current_url
page.http_method = @http_method page.http_method = @http_method
@ -93,6 +71,10 @@ For example:
page page
end end
def doc_root #:nodoc:
nil
end
def header(key, value) def header(key, value)
@custom_headers[key] = value @custom_headers[key] = value
end end
@ -102,7 +84,7 @@ For example:
end end
def basic_auth(user, pass) def basic_auth(user, pass)
encoded_login = ["#{user}:#{pass}"].pack("m*").gsub(/\n/, '') encoded_login = ["#{user}:#{pass}"].pack("m*")
header('HTTP_AUTHORIZATION', "Basic #{encoded_login}") header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
end end
@ -115,8 +97,11 @@ For example:
h['HTTP_REFERER'] = @current_url if @current_url h['HTTP_REFERER'] = @current_url if @current_url
debug_log "REQUESTING PAGE: #{http_method.to_s.upcase} #{url} with #{data.inspect} and HTTP headers #{h.inspect}" debug_log "REQUESTING PAGE: #{http_method.to_s.upcase} #{url} with #{data.inspect} and HTTP headers #{h.inspect}"
if h.empty?
process_request(http_method, url, data, h) send "#{http_method}", url, data || {}
else
send "#{http_method}", url, data || {}, h
end
save_and_open_page if exception_caught? && Webrat.configuration.open_error_files? save_and_open_page if exception_caught? && Webrat.configuration.open_error_files?
raise PageLoadError.new("Page load was not successful (Code: #{response_code.inspect}):\n#{formatted_error}") unless success_code? raise PageLoadError.new("Page load was not successful (Code: #{response_code.inspect}):\n#{formatted_error}") unless success_code?
@ -127,49 +112,21 @@ For example:
@http_method = http_method @http_method = http_method
@data = data @data = data
if internal_redirect? request_page(response_location, :get, data) if internal_redirect?
check_for_infinite_redirects
request_page(response_location, :get, {})
end
return response return response
end end
def check_for_infinite_redirects
if current_url == response_location
@_identical_redirect_count ||= 0
@_identical_redirect_count += 1
end
if infinite_redirect_limit_exceeded?
raise InfiniteRedirectError.new("#{Webrat.configuration.infinite_redirect_limit} redirects to the same URL (#{current_url.inspect})")
end
end
def infinite_redirect_limit_exceeded?
Webrat.configuration.infinite_redirect_limit &&
(@_identical_redirect_count || 0) > Webrat.configuration.infinite_redirect_limit
end
def success_code? #:nodoc: def success_code? #:nodoc:
(200..499).include?(response_code) (200..499).include?(response_code)
end end
def redirect? #:nodoc: def redirect? #:nodoc:
[301, 302, 303, 307].include?(response_code) response_code / 100 == 3
end end
def internal_redirect? def internal_redirect? #:nodoc:
return false unless redirect? redirect? && current_host == response_location_host
#should keep internal_redirects if the subdomain changes
current_host_domain = current_host.split('.')[-2..-1].join('.') rescue current_host
response_location_host_domain = response_location_host.split('.')[-2..-1].join('.') rescue response_location_host
current_host_domain == response_location_host_domain
end
#easy helper to pull out where we were redirected to
def redirected_to
redirect? ? response_location : nil
end end
def exception_caught? #:nodoc: def exception_caught? #:nodoc:
@ -257,7 +214,6 @@ For example:
def_delegators :current_scope, :uncheck, :unchecks def_delegators :current_scope, :uncheck, :unchecks
def_delegators :current_scope, :choose, :chooses def_delegators :current_scope, :choose, :chooses
def_delegators :current_scope, :select, :selects def_delegators :current_scope, :select, :selects
def_delegators :current_scope, :unselect, :unselects
def_delegators :current_scope, :select_datetime, :selects_datetime def_delegators :current_scope, :select_datetime, :selects_datetime
def_delegators :current_scope, :select_date, :selects_date def_delegators :current_scope, :select_date, :selects_date
def_delegators :current_scope, :select_time, :selects_time def_delegators :current_scope, :select_time, :selects_time
@ -269,24 +225,15 @@ For example:
def_delegators :current_scope, :field_by_xpath def_delegators :current_scope, :field_by_xpath
def_delegators :current_scope, :field_with_id def_delegators :current_scope, :field_with_id
def_delegators :current_scope, :select_option def_delegators :current_scope, :select_option
def_delegators :current_scope, :field_named
private private
def process_request(http_method, url, data, headers)
if headers.empty?
send "#{http_method}", url, data || {}
else
send "#{http_method}", url, data || {}, headers
end
end
def response_location def response_location
response_headers['Location'] response.headers["Location"]
end end
def current_host def current_host
URI.parse(current_url).host || @custom_headers["Host"] || "www.example.com" URI.parse(current_url).host || "www.example.com"
end end
def response_location_host def response_location_host

View File

@ -1,72 +1,115 @@
require "webrat/core_extensions/meta_class" require "webrat/core/xml/nokogiri"
require "webrat/core/xml/hpricot"
require "webrat/core/xml/rexml"
module Webrat #:nodoc: module Webrat #:nodoc:
module XML #:nodoc: module XML #:nodoc:
def self.document(stringlike) #:nodoc: def self.document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom) if Webrat.configuration.parse_with_nokogiri?
Webrat.nokogiri_document(stringlike)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif stringlike.respond_to?(:body)
Nokogiri::HTML(stringlike.body.to_s)
else else
Nokogiri::HTML(stringlike.to_s) Webrat.rexml_document(Webrat.hpricot_document(stringlike).to_html)
end end
end end
def self.html_document(stringlike) #:nodoc: def self.html_document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom) if Webrat.configuration.parse_with_nokogiri?
Webrat.html_nokogiri_document(stringlike)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif stringlike.respond_to?(:body)
Nokogiri::HTML(stringlike.body.to_s)
else else
Nokogiri::HTML(stringlike.to_s) Webrat.rexml_document(Webrat.hpricot_document(stringlike).to_html)
end end
end end
def self.xml_document(stringlike) #:nodoc: def self.xml_document(stringlike) #:nodoc:
return stringlike.dom if stringlike.respond_to?(:dom) if Webrat.configuration.parse_with_nokogiri?
Webrat.xml_nokogiri_document(stringlike)
if Nokogiri::HTML::Document === stringlike
stringlike
elsif Nokogiri::XML::NodeSet === stringlike
stringlike
elsif stringlike.respond_to?(:body)
Nokogiri::XML(stringlike.body.to_s)
else else
Nokogiri::XML(stringlike.to_s) Webrat.rexml_document(Webrat.hpricot_document(stringlike).to_html)
end end
end end
def self.define_dom_method(object, dom) #:nodoc: def self.to_html(element)
object.meta_class.send(:define_method, :dom) do if Webrat.configuration.parse_with_nokogiri?
dom element.to_html
else
element.to_s
end end
end end
def self.inner_html(element)
if Webrat.configuration.parse_with_nokogiri?
element.inner_html
else
element.text
end
end
def self.all_inner_text(element)
if Webrat.configuration.parse_with_nokogiri?
element.inner_text
else
Hpricot(element.to_s).children.first.inner_text
end
end
def self.inner_text(element)
if Webrat.configuration.parse_with_nokogiri?
element.inner_text
else
if defined?(Hpricot::Doc) && element.is_a?(Hpricot::Doc)
element.inner_text
else
element.text
end
end
end
def self.xpath_to(element)
if Webrat.configuration.parse_with_nokogiri?
element.path
else
element.xpath
end
end
def self.attribute(element, attribute_name)
return element[attribute_name] if element.is_a?(Hash)
if Webrat.configuration.parse_with_nokogiri?
element[attribute_name]
else
element.attributes[attribute_name]
end
end
def self.xpath_at(*args)
xpath_search(*args).first
end
def self.css_at(*args)
css_search(*args).first
end
def self.xpath_search(element, *searches)
searches.flatten.map do |search|
if Webrat.configuration.parse_with_nokogiri?
element.xpath(search)
else
REXML::XPath.match(element, search)
end
end.flatten.compact
end
def self.css_search(element, *searches) #:nodoc:
xpath_search(element, css_to_xpath(*searches))
end
def self.css_to_xpath(*selectors)
selectors.map do |rule|
Nokogiri::CSS.xpath_for(rule, :prefix => ".//")
end.flatten.uniq
end
end end
end end
module Nokogiri #:nodoc:
module CSS #:nodoc:
class XPathVisitor #:nodoc:
def visit_pseudo_class_text(node) #:nodoc:
"@type='text'"
end
def visit_pseudo_class_password(node) #:nodoc:
"@type='password'"
end
end
end
end

View File

@ -0,0 +1,19 @@
module Webrat
def self.hpricot_document(stringlike)
return stringlike.dom if stringlike.respond_to?(:dom)
if Hpricot::Doc === stringlike
stringlike
elsif Hpricot::Elements === stringlike
stringlike
elsif StringIO === stringlike
Hpricot(stringlike.string)
elsif stringlike.respond_to?(:body)
Hpricot(stringlike.body.to_s)
else
Hpricot(stringlike.to_s)
end
end
end

View File

@ -0,0 +1,76 @@
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.html_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.xml_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::XML(stringlike.string)
elsif stringlike.respond_to?(:body)
Nokogiri::XML(stringlike.body.to_s)
else
Nokogiri::XML(stringlike.to_s)
end
end
def self.define_dom_method(object, dom) #:nodoc:
object.meta_class.send(:define_method, :dom) do
dom
end
end
end
module Nokogiri #:nodoc:
module CSS #:nodoc:
class XPathVisitor #:nodoc:
def visit_pseudo_class_text(node) #:nodoc:
"@type='text'"
end
def visit_pseudo_class_password(node) #:nodoc:
"@type='password'"
end
end
end
end

View File

@ -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

@ -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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,17 +1,11 @@
require "mechanize" require "mechanize"
module Webrat #:nodoc: module Webrat #:nodoc:
class MechanizeAdapter #:nodoc: class MechanizeSession < Session #:nodoc:
extend Forwardable
Mechanize = WWW::Mechanize if defined?(WWW::Mechanize)
attr_accessor :response attr_accessor :response
alias :page :response alias :page :response
def initialize(*args)
end
def request_page(url, http_method, data) #:nodoc: def request_page(url, http_method, data) #:nodoc:
super(absolute_url(url), http_method, data) super(absolute_url(url), http_method, data)
end end
@ -22,11 +16,6 @@ module Webrat #:nodoc:
def post(url, data, headers_argument_not_used = nil) def post(url, data, headers_argument_not_used = nil)
post_data = data.inject({}) do |memo, param| post_data = data.inject({}) do |memo, param|
case param
when Hash
param.each {|attribute, value| memo[attribute] = value }
memo
when Array
case param.last case param.last
when Hash when Hash
param.last.each {|attribute, value| memo["#{param.first}[#{attribute}]"] = value } param.last.each {|attribute, value| memo["#{param.first}[#{attribute}]"] = value }
@ -35,7 +24,6 @@ module Webrat #:nodoc:
end end
memo memo
end end
end
@response = mechanize.post(url, post_data) @response = mechanize.post(url, post_data)
end end
@ -47,16 +35,8 @@ module Webrat #:nodoc:
@response.code.to_i @response.code.to_i
end end
def response_headers
@response.header
end
def mechanize def mechanize
@mechanize ||= begin @mechanize = WWW::Mechanize.new
mechanize = Mechanize.new
mechanize.redirect_ok = false
mechanize
end
end end
def_delegators :mechanize, :basic_auth def_delegators :mechanize, :basic_auth
@ -87,7 +67,7 @@ module Webrat #:nodoc:
else else
current_path.split("/")[0..(-1 - levels_up)].join("/") current_path.split("/")[0..(-1 - levels_up)].join("/")
end end
descendent = url.split("/")[levels_up..-1].join descendent = url.split("/")[levels_up..-1]
"#{ancestor}/#{descendent}" "#{ancestor}/#{descendent}"
end end
end end

View File

@ -0,0 +1,65 @@
require "webrat"
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)
end
end
end
module Merb #:nodoc:
module Test #:nodoc:
module RequestHelper #:nodoc:
def request(uri, env = {})
@_webrat_session ||= Webrat::MerbSession.new
@_webrat_session.response = @_webrat_session.request(uri, env)
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,14 +1,34 @@
require "webrat/integrations/rails" require "webrat"
require "action_controller"
require "action_controller/integration"
require "action_controller/record_identifier" require "action_controller/record_identifier"
module Webrat module Webrat
class RailsAdapter #:nodoc: class RailsSession < Session #:nodoc:
include ActionController::RecordIdentifier include ActionController::RecordIdentifier
attr_reader :integration_session # The Rails version of within supports passing in a model and Webrat
# will apply a scope based on Rails' dom_id for that model.
#
# Example:
# within User.last do
# click_link "Delete"
# end
def within(selector_or_object, &block)
if selector_or_object.is_a?(String)
super
else
super('#' + dom_id(selector_or_object), &block)
end
end
def initialize(session) def doc_root
@integration_session = session File.expand_path(File.join(RAILS_ROOT, 'public'))
end
def saved_page_dir
File.expand_path(File.join(RAILS_ROOT, "tmp"))
end end
def get(url, data, headers = nil) def get(url, data, headers = nil)
@ -35,16 +55,16 @@ module Webrat
response.code.to_i response.code.to_i
end end
def response_headers
response.headers
end
def xml_content_type? def xml_content_type?
response.headers["Content-Type"].to_s =~ /xml/ response.headers["Content-Type"].to_s =~ /xml/
end end
protected protected
def integration_session
@context
end
def do_request(http_method, url, data, headers) #:nodoc: def do_request(http_method, url, data, headers) #:nodoc:
update_protocol(url) update_protocol(url)
integration_session.send(http_method, normalize_url(url), data, headers) integration_session.send(http_method, normalize_url(url), data, headers)
@ -53,13 +73,11 @@ module Webrat
# remove protocol, host and anchor # remove protocol, host and anchor
def normalize_url(href) #:nodoc: def normalize_url(href) #:nodoc:
uri = URI.parse(href) uri = URI.parse(href)
normalized_url = [] normalized_url = uri.path
normalized_url << "#{uri.scheme}://" if uri.scheme if uri.query
normalized_url << uri.host if uri.host normalized_url += "?" + uri.query
normalized_url << ":#{uri.port}" if uri.port && ![80,443].include?(uri.port) end
normalized_url << uri.path if uri.path normalized_url
normalized_url << "?#{uri.query}" if uri.query
normalized_url.join
end end
def update_protocol(href) #:nodoc: def update_protocol(href) #:nodoc:
@ -73,5 +91,13 @@ module Webrat
def response #:nodoc: def response #:nodoc:
integration_session.response integration_session.response
end end
end
end
module ActionController #:nodoc:
IntegrationTest.class_eval do
include Webrat::Methods
include Webrat::Matchers
end end
end end

View File

@ -1,2 +1,13 @@
warn("Requiring 'webrat/rspec-rails' is deprecated. Please require 'webrat/integrations/rspec-rails' instead") # Supports using the matchers in controller, helper, and view specs if you're
require "webrat/integrations/rspec-rails" # using rspec-rails. Just add a require statement to spec/spec_helper.rb or env.rb:
#
# require 'webrat/rspec-rails'
#
require "webrat/core/matchers"
Spec::Runner.configure do |config|
# rspec should support :type => [:controller, :helper, :view] - but until it does ...
config.include(Webrat::Matchers, :type => :controller)
config.include(Webrat::Matchers, :type => :helper)
config.include(Webrat::Matchers, :type => :view)
end

View File

@ -1,11 +1,41 @@
require "webrat" require "webrat"
gem "selenium-client", ">=1.2.9"
require "selenium/client" require "selenium/client"
require "webrat/selenium/silence_stream"
require "webrat/selenium/selenium_session" require "webrat/selenium/selenium_session"
require "webrat/selenium/matchers" require "webrat/selenium/matchers"
require "webrat/core_extensions/tcp_socket"
module Webrat module Webrat
def self.with_selenium_server #:nodoc:
start_selenium_server
yield
stop_selenium_server
end
def self.start_selenium_server #:nodoc:
unless Webrat.configuration.selenium_server_address
remote_control = ::Selenium::RemoteControl::RemoteControl.new("0.0.0.0", Webrat.configuration.selenium_server_port, 5)
remote_control.jar_file = File.expand_path(__FILE__ + "../../../../vendor/selenium-server.jar")
remote_control.start :background => true
end
TCPSocket.wait_for_service :host => (Webrat.configuration.selenium_server_address || "0.0.0.0"), :port => Webrat.configuration.selenium_server_port
end
def self.stop_selenium_server #:nodoc:
::Selenium::RemoteControl::RemoteControl.new("0.0.0.0", Webrat.configuration.selenium_server_port, 5).stop unless Webrat.configuration.selenium_server_address
end
def self.start_app_server #:nodoc:
pid_file = File.expand_path(RAILS_ROOT + "/tmp/pids/mongrel_selenium.pid")
system("mongrel_rails start -d --chdir=#{RAILS_ROOT} --port=#{Webrat.configuration.application_port} --environment=#{Webrat.configuration.application_environment} --pid #{pid_file} &")
TCPSocket.wait_for_service :host => Webrat.configuration.application_address, :port => Webrat.configuration.application_port.to_i
end
def self.stop_app_server #:nodoc:
pid_file = File.expand_path(RAILS_ROOT + "/tmp/pids/mongrel_selenium.pid")
system "mongrel_rails stop -c #{RAILS_ROOT} --pid #{pid_file}"
end
# To use Webrat's Selenium support, you'll need the selenium-client gem installed. # To use Webrat's Selenium support, you'll need the selenium-client gem installed.
# Activate it with (for example, in your <tt>env.rb</tt>): # Activate it with (for example, in your <tt>env.rb</tt>):
# #
@ -25,25 +55,11 @@ module Webrat
# selenium.dragdrop("id=photo_123", "+350, 0") # selenium.dragdrop("id=photo_123", "+350, 0")
# end # end
# #
# == Choosing the underlying framework to test # == Auto-starting of the mongrel and java server
#
# Webrat assumes you're using rails by default but it can also work with sinatra
# and merb. To take advantage of this you can use the configuration block to
# set the application_framework variable.
# require "webrat"
#
# Webrat.configure do |config|
# config.mode = :selenium
# config.application_port = 4567
# config.application_framework = :sinatra # could also be :merb
# end
#
# == Auto-starting of the appserver and java server
# #
# Webrat will automatically start the Selenium Java server process and an instance # Webrat will automatically start the Selenium Java server process and an instance
# of Mongrel when a test is run. The Mongrel will run in the "selenium" environment # of Mongrel when a test is run. The Mongrel will run in the "selenium" environment
# instead of "test", so ensure you've got that defined, and will run on port # instead of "test", so ensure you've got that defined, and will run on port 3001.
# Webrat.configuration.application_port.
# #
# == Waiting # == Waiting
# #
@ -68,3 +84,11 @@ module Webrat
end end
end end
end end
module ActionController #:nodoc:
IntegrationTest.class_eval do
include Webrat::Methods
include Webrat::Selenium::Methods
include Webrat::Selenium::Matchers
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,146 @@
require "webrat/selenium/matchers/have_xpath" module Webrat
require "webrat/selenium/matchers/have_selector" module Selenium
# require "webrat/selenium/matchers/have_tag" module Matchers
require "webrat/selenium/matchers/have_content"
class HaveXpath
def initialize(expected)
@expected = expected
end
def matches?(response)
response.session.wait_for do
response.selenium.is_element_present("xpath=#{@expected}")
end
end
# ==== Returns
# String:: The failure message.
def failure_message
"expected following text to match xpath #{@expected}:\n#{@document}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following text to not match xpath #{@expected}:\n#{@document}"
end
end
def have_xpath(xpath)
HaveXpath.new(xpath)
end
def assert_have_xpath(expected)
hs = HaveXpath.new(expected)
assert hs.matches?(response), hs.failure_message
end
def assert_have_no_xpath(expected)
hs = HaveXpath.new(expected)
assert !hs.matches?(response), hs.negative_failure_message
end
class HaveSelector
def initialize(expected)
@expected = expected
end
def matches?(response)
response.session.wait_for do
response.selenium.is_element_present("css=#{@expected}")
end
end
# ==== Returns
# String:: The failure message.
def failure_message
"expected following text to match selector #{@expected}:\n#{@document}"
end
# ==== Returns
# String:: The failure message to be displayed in negative matches.
def negative_failure_message
"expected following text to not match selector #{@expected}:\n#{@document}"
end
end
def have_selector(content)
HaveSelector.new(content)
end
# Asserts that the body of the response contains
# the supplied selector
def assert_have_selector(expected)
hs = HaveSelector.new(expected)
assert hs.matches?(response), hs.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_have_no_selector(expected)
hs = HaveSelector.new(expected)
assert !hs.matches?(response), hs.negative_failure_message
end
class HasContent #:nodoc:
def initialize(content)
@content = content
end
def matches?(response)
if @content.is_a?(Regexp)
text_finder = "regexp:#{@content.source}"
else
text_finder = @content
end
response.session.wait_for do
response.selenium.is_text_present(text_finder)
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
def contain(content)
HasContent.new(content)
end
# Asserts that the body of the response contain
# the supplied string or regexp
def assert_contain(content)
hc = HasContent.new(content)
assert hc.matches?(response), hc.failure_message
end
# Asserts that the body of the response
# does not contain the supplied string or regepx
def assert_not_contain(content)
hc = HasContent.new(content)
assert !hc.matches?(response), hc.negative_failure_message
end
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,4 @@
require "webrat/core/save_and_open_page" require "webrat/core/save_and_open_page"
require "webrat/selenium/selenium_rc_server"
require "webrat/selenium/application_server_factory"
require "webrat/selenium/application_servers/base"
begin
require "selenium"
rescue LoadError => e
e.message << " (You may need to install the selenium-rc gem)"
raise e
end
module Webrat module Webrat
class TimeoutError < WebratError class TimeoutError < WebratError
@ -30,7 +20,6 @@ module Webrat
class SeleniumSession class SeleniumSession
include Webrat::SaveAndOpenPage include Webrat::SaveAndOpenPage
include Webrat::Selenium::SilenceStream
def initialize(*args) # :nodoc: def initialize(*args) # :nodoc:
end end
@ -49,8 +38,8 @@ module Webrat
webrat_deprecate :visits, :visit webrat_deprecate :visits, :visit
def fill_in(field_identifier, options) def fill_in(field_identifier, options)
locator = "webrat=#{field_identifier}" locator = "webrat=#{Regexp.escape(field_identifier)}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 selenium.wait_for_element locator, 5
selenium.type(locator, "#{options[:with]}") selenium.type(locator, "#{options[:with]}")
end end
@ -64,10 +53,6 @@ module Webrat
selenium.get_html_source selenium.get_html_source
end end
def current_url
selenium.location
end
def click_button(button_text_or_regexp = nil, options = {}) def click_button(button_text_or_regexp = nil, options = {})
if button_text_or_regexp.is_a?(Hash) && options == {} if button_text_or_regexp.is_a?(Hash) && options == {}
pattern, options = nil, button_text_or_regexp pattern, options = nil, button_text_or_regexp
@ -77,21 +62,16 @@ module Webrat
pattern ||= '*' pattern ||= '*'
locator = "button=#{pattern}" locator = "button=#{pattern}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 selenium.wait_for_element locator, 5
selenium.click locator selenium.click locator
end end
webrat_deprecate :clicks_button, :click_button webrat_deprecate :clicks_button, :click_button
def click_link(link_text_or_regexp, options = {}) def click_link(link_text_or_regexp, options = {})
if link_text_or_regexp.is_a?(Regexp) pattern = adjust_if_regexp(link_text_or_regexp)
pattern = "evalregex:#{link_text_or_regexp.inspect}"
else
pattern = link_text_or_regexp.to_s
end
locator = "webratlink=#{pattern}" locator = "webratlink=#{pattern}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 selenium.wait_for_element locator, 5
selenium.click locator selenium.click locator
end end
@ -99,7 +79,7 @@ module Webrat
def click_link_within(selector, link_text, options = {}) def click_link_within(selector, link_text, options = {})
locator = "webratlinkwithin=#{selector}|#{link_text}" locator = "webratlinkwithin=#{selector}|#{link_text}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 selenium.wait_for_element locator, 5
selenium.click locator selenium.click locator
end end
@ -114,7 +94,7 @@ module Webrat
select_locator = "webratselectwithoption=#{option_text}" select_locator = "webratselectwithoption=#{option_text}"
end end
selenium.wait_for_element select_locator, :timeout_in_seconds => 5 selenium.wait_for_element select_locator, 5
selenium.select(select_locator, option_text) selenium.select(select_locator, option_text)
end end
@ -122,7 +102,7 @@ module Webrat
def choose(label_text) def choose(label_text)
locator = "webrat=#{label_text}" locator = "webrat=#{label_text}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 selenium.wait_for_element locator, 5
selenium.click locator selenium.click locator
end end
@ -130,10 +110,9 @@ module Webrat
def check(label_text) def check(label_text)
locator = "webrat=#{label_text}" locator = "webrat=#{label_text}"
selenium.wait_for_element locator, :timeout_in_seconds => 5 selenium.wait_for_element locator, 5
selenium.click locator selenium.check locator
end end
alias_method :uncheck, :check
webrat_deprecate :checks, :check webrat_deprecate :checks, :check
@ -163,10 +142,8 @@ module Webrat
begin begin
value = yield value = yield
rescue Exception => e rescue ::Spec::Expectations::ExpectationNotMetError, ::Selenium::CommandError, Webrat::WebratError
unless is_ignorable_wait_for_exception?(e) value = nil
raise e
end
end end
return value if value return value if value
@ -174,19 +151,7 @@ module Webrat
sleep 0.25 sleep 0.25
end end
error_message = "#{message} (after #{timeout} sec)" raise Webrat::TimeoutError.new(message + " (after #{timeout} sec)")
if $browser && Webrat.configuration.selenium_verbose_output
error_message += <<-EOS
HTML of the page was:
#{selenium.get_html_source}"
EOS
end
raise Webrat::TimeoutError.new(error_message)
true true
end end
@ -200,9 +165,9 @@ EOS
def save_and_open_screengrab def save_and_open_screengrab
return unless File.exist?(Webrat.configuration.saved_pages_dir) return unless File.exist?(saved_page_dir)
filename = "#{Webrat.configuration.saved_pages_dir}/webrat-#{Time.now.to_i}.png" filename = "#{saved_page_dir}/webrat-#{Time.now.to_i}.png"
if $browser.chrome_backend? if $browser.chrome_backend?
$browser.capture_entire_page_screenshot(filename, '') $browser.capture_entire_page_screenshot(filename, '')
@ -210,24 +175,19 @@ EOS
$browser.capture_screenshot(filename) $browser.capture_screenshot(filename)
end end
open_in_browser(filename) open_in_browser(filename)
end end
protected protected
def is_ignorable_wait_for_exception?(exception) #:nodoc:
if defined?(::Spec::Expectations::ExpectationNotMetError)
return true if exception.class == ::Spec::Expectations::ExpectationNotMetError
end
return true if [::Selenium::CommandError, Webrat::WebratError].include?(exception.class)
return false
end
def setup #:nodoc: def setup #:nodoc:
Webrat::Selenium::SeleniumRCServer.boot silence_stream(STDOUT) do
Webrat::Selenium::ApplicationServerFactory.app_server_instance.boot Webrat.start_selenium_server
Webrat.start_app_server
end
create_browser create_browser
$browser.start $browser.start
teardown_at_exit
extend_selenium extend_selenium
define_location_strategies define_location_strategies
@ -237,12 +197,16 @@ EOS
def create_browser def create_browser
$browser = ::Selenium::Client::Driver.new(Webrat.configuration.selenium_server_address || "localhost", $browser = ::Selenium::Client::Driver.new(Webrat.configuration.selenium_server_address || "localhost",
Webrat.configuration.selenium_server_port, Webrat.configuration.selenium_browser_key, "http://#{Webrat.configuration.application_address}:#{Webrat.configuration.application_port_for_selenium}") Webrat.configuration.selenium_server_port, Webrat.configuration.selenium_browser_key, "http://#{Webrat.configuration.application_address}:#{Webrat.configuration.application_port}")
$browser.set_speed(0) unless Webrat.configuration.selenium_server_address $browser.set_speed(0) unless Webrat.configuration.selenium_server_address
end
def teardown_at_exit #:nodoc:
at_exit do at_exit do
silence_stream(STDOUT) do silence_stream(STDOUT) do
$browser.stop $browser.stop
Webrat.stop_app_server
Webrat.stop_selenium_server
end end
end end
end end

View File

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

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

@ -0,0 +1,29 @@
require 'webrat/rack'
require 'sinatra'
require 'sinatra/test/methods'
class Sinatra::Application
# Override this to prevent Sinatra from barfing on the options passed from RSpec
def self.load_default_options_from_command_line!
end
end
disable :run
disable :reload
module Webrat
class SinatraSession < RackSession #:nodoc:
include Sinatra::Test::Methods
attr_reader :request, :response
%w(get head post put delete).each do |verb|
define_method(verb) do |*args| # (path, data, headers = nil)
path, data, headers = *args
data = data.inject({}) {|data, (key,value)| data[key] = Rack::Utils.unescape(value); data }
params = data.merge(:env => headers || {})
self.__send__("#{verb}_it", path, params)
end
end
end
end

View File

@ -1,13 +1,14 @@
module Webrat #:nodoc: module Webrat #:nodoc:
def self.adapter_class #:nodoc: def self.session_class #:nodoc:
TestAdapter TestSession
end end
class TestAdapter #:nodoc: class TestSession < Session #:nodoc:
attr_accessor :response_body attr_accessor :response_body
attr_writer :response_code attr_writer :response_code
def initialize(*args) def doc_root
File.expand_path(File.join(".", "public"))
end end
def response def response
@ -15,7 +16,7 @@ module Webrat #:nodoc:
end end
def response_code def response_code
@response_code ||= 200 @response_code || 200
end end
def get(url, data, headers = nil) def get(url, data, headers = nil)

View File

@ -1,7 +0,0 @@
require 'rubygems'
require 'spec/rake/spectask'
Spec::Rake::SpecTask.new do |t|
t.spec_opts = ['--color']
t.spec_files = FileList['spec/**/*_spec.rb']
end

View File

@ -1,2 +0,0 @@
require "sample_app"
run SampleApp

View File

@ -1,35 +0,0 @@
require "sinatra/base"
class SampleApp < Sinatra::Default
get "/" do
"Hello World"
end
get "/internal_redirect" do
redirect URI.join(request.url, "redirected").to_s
end
get "/external_redirect" do
redirect "http://example.tst/"
end
get "/redirected" do
"Redirected"
end
get "/form" do
<<-EOS
<html>
<form action="/form" method="post">
<input type="hidden" name="_method" value="put" />
<label for="email">Email:</label> <input type="text" id="email" name="email" /></label>
<input type="submit" value="Add" />
</form>
</html>
EOS
end
put "/form" do
"Welcome #{params[:email]}"
end
end

View File

@ -1,30 +0,0 @@
require File.dirname(__FILE__) + "/spec_helper"
describe "Webrat's Mechanize mode" do
it "should work" do
response = visit("http://localhost:9292/")
response.should contain("Hello World")
end
it "should follow redirects" do
response = visit("http://localhost:9292/internal_redirect")
response.should contain("Redirected")
end
it "should follow links"
it "should submit forms" do
visit "http://localhost:9292/form"
fill_in "Email", :with => "albert@example.com"
response = click_button "Add"
response.should contain("Welcome albert@example.com")
end
it "should not follow external redirects" do
pending do
response = visit("http://localhost:9292/external_redirect")
response.should contain("Foo")
end
end
end

View File

@ -1,29 +0,0 @@
require "rubygems"
require "spec"
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../../../../lib"
require "webrat"
Webrat.configure do |config|
config.mode = :mechanize
end
Spec::Runner.configure do |config|
config.include Webrat::Methods
config.include Webrat::Matchers
config.before :suite do
if File.exists?("rack.pid")
Process.kill("TERM", File.read("rack.pid").to_i)
end
system "rackup --daemonize --pid rack.pid config.ru"
end
config.after :suite do
if File.exists?("rack.pid")
Process.kill("TERM", File.read("rack.pid").to_i)
end
end
end

View File

@ -4,15 +4,6 @@ class Testing < Application
render render
end end
def upload
case request.method
when :get then render
when :post then
uploaded_file = params[:uploaded_file]
render [uploaded_file[:filename], uploaded_file[:tempfile].class.name].inspect
end
end
def submit_form def submit_form
end end

View File

@ -1,9 +0,0 @@
<h1>Webrat Form</h1>
<form action="/upload" method="post">
<label>
File
<input type="file" name="uploaded_file" />
</label>
<input type="submit" value="Upload">
</form>

View File

@ -28,7 +28,6 @@
Merb.logger.info("Compiling routes...") Merb.logger.info("Compiling routes...")
Merb::Router.prepare do Merb::Router.prepare do
match("/").to(:controller => "testing", :action => "show_form") match("/").to(:controller => "testing", :action => "show_form")
match("/upload").to(:controller => "testing", :action => "upload")
match("/internal_redirect").to(:controller => "testing", :action => "internal_redirect") match("/internal_redirect").to(:controller => "testing", :action => "internal_redirect")
match("/external_redirect").to(:controller => "testing", :action => "external_redirect") match("/external_redirect").to(:controller => "testing", :action => "external_redirect")
end end

View File

@ -1,7 +1,5 @@
require "rubygems" require "rubygems"
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../../../../lib"
# Add the local gems dir if found within the app root; any dependencies loaded # Add the local gems dir if found within the app root; any dependencies loaded
# hereafter will try to load from the local gems before loading system gems. # hereafter will try to load from the local gems before loading system gems.
if (local_gem_dir = File.join(File.dirname(__FILE__), '..', 'gems')) && $BUNDLE.nil? if (local_gem_dir = File.join(File.dirname(__FILE__), '..', 'gems')) && $BUNDLE.nil?

View File

@ -29,11 +29,4 @@ describe "Webrat" do
response = visit "/external_redirect" response = visit "/external_redirect"
response.status.should == 302 response.status.should == 302
end end
it "should upload files" do
visit "/upload"
attach_file "File", __FILE__
response = click_button "Upload"
response.should contain(%(["webrat_spec.rb", "Tempfile"]))
end
end end

View File

@ -1,5 +0,0 @@
require "rake/testtask"
Rake::TestTask.new do |t|
t.test_files = FileList["test/*_test.rb"]
end

View File

@ -1,89 +0,0 @@
require "sinatra/base"
class RackApp < Sinatra::Base
use_in_file_templates!
get "/" do
erb :home
end
get "/go" do
erb :go
end
get "/internal_redirect" do
redirect "/"
end
get "/external_redirect" do
redirect "http://google.com"
end
get "/absolute_redirect" do
redirect URI.join(request.url, "foo").to_s
end
get "/foo" do
"spam"
end
post "/go" do
@user = params[:name]
@email = params[:email]
erb :hello
end
get "/upload" do
erb :uploader
end
post "/upload" do
uploaded_file = params[:uploaded_file]
Marshal.dump(:tempfile => uploaded_file[:tempfile].read, :type => uploaded_file[:type], :filename => uploaded_file[:filename])
end
end
__END__
@@ layout
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<title>sinatra testing with webrat</title>
<body>
<%= yield %>
</body>
</html>
@@ home
<p> visit <a href="/go">there</a></p>
<form>
<label>
Prefilled
<input type="text" name="prefilled" value="text" />
</label>
</form>
@@ go
<form method="post" action="/go">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name">
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email">
</div>
<input type="submit" value="Submit" />
</form>
@@ hello
<p>Hello, <%= @user %></p>
<p>Your email is: <%= @email %></p>
@@ uploader
<form action="/upload" method="post">
<label>
File <input type="file" name="uploaded_file" />
</label>
<input type="submit" value="Upload">
</form>

View File

@ -1,20 +0,0 @@
require "test/unit"
require "rack/test"
# require "redgreen"
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../../../../lib"
require "webrat"
require File.dirname(__FILE__) + "/../app"
Webrat.configure do |config|
config.mode = :rack
end
class Test::Unit::TestCase
def app
Rack::Builder.new {
use Rack::Lint
run RackApp.new
}
end
end

View File

@ -1,74 +0,0 @@
require "rubygems"
require File.dirname(__FILE__) + "/helper"
class WebratRackTest < Test::Unit::TestCase
include Rack::Test::Methods
include Webrat::Methods
include Webrat::Matchers
include Webrat::HaveTagMatcher
def build_rack_mock_session
Rack::MockSession.new(app, "www.example.com")
end
def test_visits_pages
visit "/"
click_link "there"
assert_have_tag("form[@method='post'][@action='/go']")
end
def test_submits_form
visit "/go"
fill_in "Name", :with => "World"
fill_in "Email", :with => "world@example.org"
click_button "Submit"
assert_contain "Hello, World"
assert_contain "Your email is: world@example.org"
end
def test_check_value_of_field
visit "/"
assert_equal field_labeled("Prefilled").value, "text"
end
def test_follows_internal_redirects
visit "/internal_redirect"
assert_contain "visit"
end
def test_does_not_follow_external_redirects
visit "/external_redirect"
assert last_response.redirect?
end
def test_absolute_url_redirect
visit "/absolute_redirect"
assert_contain "spam"
end
def test_upload_file
visit "/upload"
attach_file "File", __FILE__, "text/ruby"
click_button "Upload"
upload = Marshal.load(response_body)
assert_equal "text/ruby", upload[:type]
assert_equal "webrat_rack_test.rb", upload[:filename]
assert_equal File.read(__FILE__), upload[:tempfile]
end
end
class WebratRackSetupTest < Test::Unit::TestCase
def test_usable_without_mixin
rack_test_session = Rack::Test::Session.new(Rack::MockSession.new(app))
adapter = Webrat::RackAdapter.new(rack_test_session)
session = Webrat::Session.new(adapter)
session.visit "/foo"
assert_equal "spam", session.response_body
assert_equal "spam", rack_test_session.last_response.body
end
end

View File

@ -1,3 +1 @@
vendor/plugins/webrat vendor/plugins/webrat
tmp/pids/*.pid
log/*.log

View File

@ -8,23 +8,3 @@ require 'rake/testtask'
require 'rake/rdoctask' require 'rake/rdoctask'
require 'tasks/rails' require 'tasks/rails'
namespace :test_unit do
desc "runs the test::unit based tests in webrat mode"
task :rails do
ENV['WEBRAT_INTEGRATION_MODE'] = 'rails'
Rake::Task['test:integration'].execute
end
desc "runs the test::unit based tests in selenium mode"
task :selenium do
ENV['WEBRAT_INTEGRATION_MODE'] = 'selenium'
Rake::Task['test:integration'].execute
end
desc "run both selenium and rails mode Test::Unit suites"
task :all => [:rails, :selenium]
end

View File

@ -1,7 +0,0 @@
class ButtonsController < ApplicationController
def show
end
def create
render :text => "success"
end
end

View File

@ -1,4 +0,0 @@
class FieldsController < ApplicationController
def show
end
end

View File

@ -1,7 +0,0 @@
class LinksController < ApplicationController
def show
if params[:value]
render :text => "Link:#{params[:value]}"
end
end
end

View File

@ -10,37 +10,15 @@ class WebratController < ApplicationController
end end
def submit def submit
render :text => "OK <a href='/' id='link_id'>Test Link Text</a>" render :text => "OK"
end end
def internal_redirect def internal_redirect
redirect_to submit_path redirect_to :submit
end
def infinite_redirect
redirect_to infinite_redirect_path
end end
def external_redirect def external_redirect
redirect_to "http://google.com" redirect_to "http://google.com"
end end
def host_redirect
redirect_to submit_url
end
def before_redirect_form
end
def redirect_to_show_params
redirect_to show_params_path(:custom_param => "123")
end
def show_params
render :text => params.to_json
end
def within
end
end end

View File

@ -1,2 +0,0 @@
module ButtonsHelper
end

View File

@ -1,2 +0,0 @@
module FieldsHelper
end

View File

@ -1,2 +0,0 @@
module LinksHelper
end

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