diff --git a/History.txt b/History.txt
index edbf24a..77af696 100644
--- a/History.txt
+++ b/History.txt
@@ -2,9 +2,11 @@
* Enhancements
+ * Support file fields using attaches_file (Patch from Kyle Hargraves)
* Support button elements (Patch from Nick Sieger)
* Support matching select options by regexp (Patch from Kyle Hargraves)
* Support relative links, including href="?foo=bar" (Patch from Kyle Hargraves)
+ * Support links to fully qualified URLs starting with http:// or https:// (Luke Melia)
* Bug fixes
@@ -26,7 +28,7 @@
* Added reloads method to reload the page (Patch from Kamal Fariz Mahyuddi)
* Prevent making a request if clicking on local anchor link (Patch from Kamal Fariz Mahyuddi)
* Added clicks_link_within(selector, link_text), allowing restricting link search
- to within a given css selector (Path from Luke Melia)
+ to within a given css selector (Patch from Luke Melia)
* Allow specifying the input name/label when doing a select (Patch from David Chelimsky)
* Raise a specific exception if the developer tries to manipulate form elements before loading a page (Patch from James Deville)
* Add support for alternate POST, PUT and DELETE link clicking (Patch from Kyle Hargraves)
diff --git a/README.txt b/README.txt
index ffbdf08..df8ec0e 100644
--- a/README.txt
+++ b/README.txt
@@ -1,14 +1,14 @@
-= Webrat - Ruby Acceptance Testing for Web applications
+Webrat
+======
- http://rubyforge.org/projects/webrat
- http://github.com/brynary/webrat
+- [Code on GitHub](http://github.com/brynary/webrat)
+- [Tickets on Lighthouse](http://webrat.lighthouseapp.com/)
-* mailto:bryan@brynary.com
-* mailto:seth@mojodna.net
+Description
+-----------
-== DESCRIPTION:
-
-Webrat lets you quickly write robust and thorough acceptance tests for a Ruby
+Webrat (_Ruby Acceptance Testing for Web applications_)
+lets you quickly write robust and thorough acceptance tests for a Ruby
web application. By leveraging the DOM, it can run tests similarly to an
in-browser testing solution without the associated performance hit (and
browser dependency). The result is tests that are less fragile and more
@@ -19,20 +19,21 @@ Selenium, the primary consideration should be how much JavaScript the
application uses. In-browser testing is currently the only way to test JS, and
that may make it a requirement for your project. If JavaScript is not central
to your application, Webrat is a simpler, effective solution that will let you
-run your tests much faster and more frequently. (Benchmarks forthcoming.)
+run your tests much faster and more frequently.
-Initial development was sponsored by EastMedia (http://www.eastmedia.com).
+Initial development was sponsored by [EastMedia](http://www.eastmedia.com).
-== SYNOPSIS:
+Synopsis
+--------
- def test_sign_up
- visits "/"
- clicks_link "Sign up"
- fills_in "Email", :with => "good@example.com"
- selects "Free account"
- clicks_button "Register"
- ...
- end
+ def test_sign_up
+ visits "/"
+ clicks_link "Sign up"
+ fills_in "Email", :with => "good@example.com"
+ selects "Free account"
+ clicks_button "Register"
+ ...
+ end
Behind the scenes, this will perform the following work:
@@ -50,37 +51,47 @@ Behind the scenes, this will perform the following work:
Take special note of the things _not_ specified in that test, that might cause
tests to break unnecessarily as your application evolves:
-* The input field IDs or names (e.g. "user_email" or "user[email]"), which
+- The input field IDs or names (e.g. "user_email" or "user[email]"), which
could change if you rename a model
-* The ID of the form element (Webrat can do a good job of guessing, even if
+- The ID of the form element (Webrat can do a good job of guessing, even if
there are multiple forms on the page.)
-* The URLs of links followed
-* The URL the form submission should be sent to, which could change if you
+- The URLs of links followed
+- The URL the form submission should be sent to, which could change if you
adjust your routes or controllers
-* The HTTP method for the login request
+- The HTTP method for the login request
-A test written with Webrat can handle these changes smoothly.
+A test written with Webrat can handle these changes to these without any modifications.
-== REQUIREMENTS:
+Install
+-------
-* Rails >= 1.2.6
-* Hpricot >= 0.6
-* Rails integration tests in Test::Unit _or_
-* RSpec stories (using an RSpec version >= revision 2997)
+To install the latest release:
-== INSTALL:
+ sudo gem install webrat
In your stories/helper.rb:
- require "webrat"
+ require "webrat"
You could also unpack the gem into vendor/plugins.
-== HISTORY:
+Requirements
+------------
-See CHANGELOG in this directory.
+- Rails >= 1.2.6
+- Hpricot >= 0.6
+- Rails integration tests in Test::Unit _or_
+- RSpec stories (using an RSpec version >= revision 2997)
-== LICENSE:
+Authors
+-------
+
+- Maintained by [Bryan Helmkamp](mailto:bryan@brynary.com)
+- Original code written by [Seth Fitzsimmons](mailto:seth@mojodna.net)
+- Many other contributors. See attributions in History.txt
+
+License
+-------
Copyright (c) 2007 Bryan Helmkamp, Seth Fitzsimmons.
-See MIT-LICENSE in this directory.
+See MIT-LICENSE.txt in this directory.
diff --git a/Rakefile b/Rakefile
index d1ce857..35fcfc8 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,5 +1,7 @@
require 'rubygems'
require 'hoe'
+require 'spec'
+require 'spec/rake/spectask'
require './lib/webrat.rb'
Hoe.new('webrat', Webrat::VERSION) do |p|
@@ -16,10 +18,48 @@ Hoe.new('webrat', Webrat::VERSION) do |p|
p.extra_deps << ["hpricot", ">= 0.6"]
p.remote_rdoc_dir = '' # Release to root
- p.test_globs = ['test/**/*_test.rb']
end
desc "Upload rdoc to brynary.com"
task :publish_rdoc => :docs do
sh "scp -r doc/ brynary.com:/apps/uploads/webrat"
+end
+
+Rake::TaskManager.class_eval do
+ def remove_task(task_name)
+ @tasks.delete(task_name.to_s)
+ end
+end
+
+def remove_task(task_name)
+ Rake.application.remove_task(task_name)
+end
+
+remove_task "test"
+remove_task "test_deps"
+
+desc "Run all specs in spec directory"
+Spec::Rake::SpecTask.new do |t|
+ t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
+ t.spec_files = FileList['spec/**/*_spec.rb']
+end
+
+desc "Run all specs in spec directory with RCov"
+Spec::Rake::SpecTask.new(:rcov) do |t|
+ t.spec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.rcov = true
+ t.rcov_opts = lambda do
+ IO.readlines(File.dirname(__FILE__) + "/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
+ end
+end
+
+require 'spec/rake/verify_rcov'
+RCov::VerifyTask.new(:verify_rcov => :rcov) do |t|
+ t.threshold = 95.5 # Make sure you have rcov 0.7 or higher!
+end
+
+remove_task "default"
+task :default do
+ Rake::Task["verify_rcov"].invoke
end
\ No newline at end of file
diff --git a/lib/webrat.rb b/lib/webrat.rb
index 4401f1a..26c8dde 100644
--- a/lib/webrat.rb
+++ b/lib/webrat.rb
@@ -35,7 +35,7 @@ module ActionController
current_page.save_and_open
end
- [:reloads, :fills_in, :clicks_button, :selects, :chooses, :checks, :unchecks, :clicks_link, :clicks_link_within, :clicks_put_link, :clicks_get_link, :clicks_post_link, :clicks_delete_link].each do |method_name|
+ [:reloads, :fills_in, :clicks_button, :selects, :attaches_file, :chooses, :checks, :unchecks, :clicks_link, :clicks_link_within, :clicks_put_link, :clicks_get_link, :clicks_post_link, :clicks_delete_link].each do |method_name|
define_method(method_name) do |*args|
current_page.send(method_name, *args)
end
diff --git a/lib/webrat/field.rb b/lib/webrat/field.rb
index f6b1a1e..e45a192 100644
--- a/lib/webrat/field.rb
+++ b/lib/webrat/field.rb
@@ -97,6 +97,21 @@ module Webrat
end
end
+ def replace_param_value(params, oval, nval)
+ output = Hash.new
+ params.each do |key, value|
+ case value
+ when Hash
+ value = replace_param_value(value, oval, nval)
+ when Array
+ value = value.map { |o| o == oval ? nval : oval }
+ when oval
+ value = nval
+ end
+ output[key] = value
+ end
+ output
+ end
end
class ButtonField < Field
@@ -221,6 +236,15 @@ module Webrat
end
class FileField < Field
+
+ def to_param
+ if @value.nil?
+ super
+ else
+ replace_param_value(super, @value, ActionController::TestUploadedFile.new(@value))
+ end
+ end
+
end
class TextField < Field
diff --git a/lib/webrat/link.rb b/lib/webrat/link.rb
index d9ff682..e13d21d 100644
--- a/lib/webrat/link.rb
+++ b/lib/webrat/link.rb
@@ -10,6 +10,7 @@ module Webrat
method ||= http_method
return if href =~ /^#/ && method == :get
+ update_protocol(href)
Page.new(@page.session, absolute_href, method, authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token})
end
@@ -26,9 +27,19 @@ module Webrat
def href
@element["href"]
end
+
+ def update_protocol(href)
+ if href =~ /^https:/
+ @page.session.https!(true)
+ elsif href =~ /^http:/
+ @page.session.https!(false)
+ end
+ end
def absolute_href
- if href =~ /^\?/
+ if href =~ %r{^https?://www.example.com(/.*)}
+ $LAST_MATCH_INFO.captures.first
+ elsif href =~ /^\?/
"#{@page.url}#{href}"
elsif href !~ /^\//
"#{@page.url}/#{href}"
diff --git a/lib/webrat/page.rb b/lib/webrat/page.rb
index 65e5056..17fcd0e 100644
--- a/lib/webrat/page.rb
+++ b/lib/webrat/page.rb
@@ -88,6 +88,17 @@ module Webrat
option.choose
end
+ # Verifies that an input file field exists on the current page and sets
+ # its value to the given +file+, so that the file will be uploaded
+ # along with the form.
+ #
+ # Example:
+ # attaches_file "Photo", "/path/to/the/photo.jpg"
+ def attaches_file(id_or_name_or_label, path)
+ field = find_field(id_or_name_or_label, FileField)
+ field.set(path)
+ end
+
# Saves the currently loaded page out to RAILS_ROOT/tmp/ and opens it in the default
# web browser if on OS X. Useful for debugging.
#
diff --git a/spec/attaches_file_spec.rb b/spec/attaches_file_spec.rb
new file mode 100644
index 0000000..30111c1
--- /dev/null
+++ b/spec/attaches_file_spec.rb
@@ -0,0 +1,62 @@
+require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
+
+describe "attaches_file" do
+ before do
+ @session = ActionController::Integration::Session.new
+ @session.stubs(:assert_response)
+ @session.stubs(:get_via_redirect)
+ @session.stubs(:response).returns(@response=mock)
+
+ @filename = __FILE__
+ @uploaded_file = mock
+ ActionController::TestUploadedFile.stubs(:new).returns(@uploaded_file)
+ end
+
+ it "should fail if no file field found" do
+ @response.stubs(:body).returns(<<-EOS)
+
+ EOS
+ lambda { @session.attaches_file("Doc", "/some/path") }.should raise_error
+ end
+
+ it "should submit empty strings for blank file fields" do
+ @response.stubs(:body).returns(<<-EOS)
+
+ EOS
+ @session.expects(:post_via_redirect).with("/widgets", { "widget" => { "file" => "" } })
+ @session.clicks_button
+ end
+
+ it "should submit the attached file" do
+ @response.stubs(:body).returns(<<-EOS)
+
+ EOS
+ @session.expects(:post_via_redirect).with("/widgets", { "widget" => { "file" => @uploaded_file } })
+ @session.attaches_file "Document", @filename
+ @session.clicks_button
+ end
+
+ it "should support collections" do
+ @response.stubs(:body).returns(<<-EOS)
+
+ EOS
+ @session.expects(:post_via_redirect).with("/widgets", { "widget" => { "files" => [@uploaded_file, @uploaded_file] } })
+ @session.attaches_file "Document", @filename
+ @session.attaches_file "Spreadsheet", @filename
+ @session.clicks_button
+ end
+end
diff --git a/test/checks_test.rb b/spec/checks_spec.rb
similarity index 76%
rename from test/checks_test.rb
rename to spec/checks_spec.rb
index 7d3113b..43ab091 100644
--- a/test/checks_test.rb
+++ b/spec/checks_spec.rb
@@ -1,37 +1,33 @@
-require File.dirname(__FILE__) + "/helper"
+require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
-class ChecksTest < Test::Unit::TestCase
- def setup
+describe "checks" do
+ before do
@session = ActionController::Integration::Session.new
@session.stubs(:assert_response)
@session.stubs(:get_via_redirect)
@session.stubs(:response).returns(@response=mock)
end
- def test_should_fail_if_no_checkbox_found
+ it "should fail if no checkbox found" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.checks "remember_me"
- end
+ lambda { @session.checks "remember_me" }.should raise_error
end
- def test_should_fail_if_input_is_not_a_checkbox
+ it "should fail if input is not a checkbox" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.checks "remember_me"
- end
+ lambda { @session.checks "remember_me" }.should raise_error
end
- def test_should_check_rails_style_checkboxes
+ it "should check rails style checkboxes" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.unchecks "remember_me"
- end
+ lambda { @session.unchecks "remember_me" }.should raise_error
end
- def test_should_fail_if_input_is_not_a_checkbox
+ it "should fail if input is not a checkbox" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.unchecks "remember_me"
- end
+ lambda { @session.unchecks "remember_me" }.should raise_error
end
- def test_should_uncheck_rails_style_checkboxes
+ it "should uncheck rails style checkboxes" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.chooses "first option"
- end
+ lambda { @session.chooses "first option" }.should raise_error
end
- def test_should_fail_if_input_is_not_a_radio_button
+ it "should fail if input is not a radio button" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.chooses "first_option"
- end
+ lambda { @session.chooses "first_option" }.should raise_error
end
- def test_should_check_rails_style_radio_buttons
+ it "should check rails style radio buttons" do
@response.stubs(:body).returns(<<-EOS)
EOS
- assert_raises RuntimeError do
- @session.clicks_button
- end
+ lambda { @session.clicks_button }.should raise_error
end
- def test_should_fail_if_input_is_not_a_submit_button
+ it "should fail if input is not a submit button" do
@response.stubs(:body).returns(<<-EOS)
EOS
-
- assert_raises RuntimeError do
- @session.clicks_button
- end
+
+ lambda { @session.clicks_button }.should raise_error
end
- def test_should_default_to_get_method
+ it "should default to get method" do
@response.stubs(:body).returns(<<-EOS)