Skip to content

Commit

Permalink
Added webrat, thanks to jrun and gwynm for they're initial work on this
Browse files Browse the repository at this point in the history
  • Loading branch information
orangewolf committed Oct 21, 2008
1 parent 2fd734f commit 86879c1
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 10 deletions.
7 changes: 7 additions & 0 deletions README.txt
Expand Up @@ -59,6 +59,13 @@ tests to break unnecessarily as your application evolves:

A test written with Webrat can handle these changes to these without any modifications.

=== Merb
To avoid losing sessions, you need this in environments/test.rb:

Merb::Config.use do |c|
c[:session_store] = 'memory'
end

=== Install

To install the latest release:
Expand Down
15 changes: 13 additions & 2 deletions Rakefile
Expand Up @@ -35,25 +35,36 @@ def remove_task(task_name)
Rake.application.remove_task(task_name)
end

def set_file_list
if ENV['TEST_MODE'] == "merb"
list = FileList['spec/**/*_spec.rb']
list = list.find_all do |file| !file.match("rails") end
return list
else
return FileList['spec/**/*_spec.rb']
end
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']
t.spec_files = set_file_list
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.spec_files = set_file_list
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 = 97.3 # Make sure you have rcov 0.7 or higher!
Expand Down
1 change: 1 addition & 0 deletions lib/webrat.rb
Expand Up @@ -6,3 +6,4 @@ module Webrat

require File.dirname(__FILE__) + "/webrat/core"
require File.dirname(__FILE__) + "/webrat/rails" if defined?(RAILS_ENV)
require File.dirname(__FILE__) + "/webrat/merb" if defined?(Merb)
2 changes: 2 additions & 0 deletions lib/webrat/core/logging.rb
Expand Up @@ -9,6 +9,8 @@ def debug_log(message) # :nodoc:
def logger # :nodoc:
if defined? RAILS_DEFAULT_LOGGER
RAILS_DEFAULT_LOGGER
elsif defined? Merb
Merb.logger
else
nil
end
Expand Down
45 changes: 45 additions & 0 deletions lib/webrat/merb.rb
@@ -0,0 +1,45 @@
module Webrat
class Session
include Merb::Test::RequestHelper

attr_reader :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

protected
def do_request(url, data, headers, method)
@response = request(url, :params => data, :headers => headers, :method => method)
self.get(@response.headers['Location'], nil, @response.headers) if @response.status == 302
end

end
end

class Merb::Test::RspecStory
def browser
@browser ||= Webrat::Session.new
end
end

125 changes: 125 additions & 0 deletions lib/webrat/merb/indifferent_access.rb
@@ -0,0 +1,125 @@
# 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
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


17 changes: 17 additions & 0 deletions lib/webrat/merb/param_parser.rb
@@ -0,0 +1,17 @@
module Webrat
class ParamParser
def self.parse_query_parameters(query_string)
return {} if query_string.blank?

pairs = query_string.split('&').collect do |chunk|
next if chunk.empty?
key, value = chunk.split('=', 2)
next if key.empty?
value = value.nil? ? nil : CGI.unescape(value)
[ CGI.unescape(key), value ]
end.compact

UrlEncodedPairParser.new(pairs).result
end
end
end
12 changes: 12 additions & 0 deletions lib/webrat/merb/support.rb
@@ -0,0 +1,12 @@
class Hash
def with_indifferent_access
hash = HashWithIndifferentAccess.new(self)
hash.default = self.default
hash
end
end
class NilClass
def to_param
nil
end
end
93 changes: 93 additions & 0 deletions lib/webrat/merb/url_encoded_pair_parser.rb
@@ -0,0 +1,93 @@
class UrlEncodedPairParser < StringScanner #:nodoc:
attr_reader :top, :parent, :result

def initialize(pairs = [])
super('')
@result = {}
pairs.each { |key, value| parse(key, value) }
end

KEY_REGEXP = %r{([^\[\]=&]+)}
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}

# Parse the query string
def parse(key, value)
self.string = key
@top, @parent = result, nil

# First scan the bare key
key = scan(KEY_REGEXP) or return
key = post_key_check(key)

# Then scan as many nestings as present
until eos?
r = scan(BRACKETED_KEY_REGEXP) or return
key = self[1]
key = post_key_check(key)
end

bind(key, value)
end

private
# After we see a key, we must look ahead to determine our next action. Cases:
#
# [] follows the key. Then the value must be an array.
# = follows the key. (A value comes next)
# & or the end of string follows the key. Then the key is a flag.
# otherwise, a hash follows the key.
def post_key_check(key)
if scan(/\[\]/) # a[b][] indicates that b is an array
container(key, Array)
nil
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
container(key, Hash)
nil
else # End of key? We do nothing.
key
end
end

# Add a container to the stack.
def container(key, klass)
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
value = bind(key, klass.new)
type_conflict! klass, value unless value.is_a?(klass)
push(value)
end

# Push a value onto the 'stack', which is actually only the top 2 items.
def push(value)
@parent, @top = @top, value
end

# Bind a key (which may be nil for items in an array) to the provided value.
def bind(key, value)
if top.is_a? Array
if key
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
top[-1][key] = value
else
top << {key => value}.with_indifferent_access
push top.last
value = top[key]
end
else
top << value
end
elsif top.is_a? Hash
key = CGI.unescape(key)
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
top[key] ||= value
return top[key]
else
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
end

return value
end

def type_conflict!(klass, value)
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
end
end
3 changes: 2 additions & 1 deletion spec/api/attaches_file_spec.rb
@@ -1,5 +1,5 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")

unless ENV["TEST_MODE"] == "merb" #TODO - Rob
describe "attaches_file" do
before do
@session = Webrat::TestSession.new
Expand Down Expand Up @@ -70,3 +70,4 @@
@session.clicks_button
end
end
end

0 comments on commit 86879c1

Please sign in to comment.