Added Rspec and Webrat plugins and started porting Selenium on Rails tests to Rspec Plain Text Stories driving Webrat driving Selenium.

This commit is contained in:
Luke Melia 2008-06-18 02:57:57 -04:00
parent 0600756bbf
commit 0f7d6f7a1d
602 changed files with 47788 additions and 29 deletions

View file

@ -0,0 +1,158 @@
module Spec
module Example
class Configuration
# Chooses what mock framework to use. Example:
#
# Spec::Runner.configure do |config|
# config.mock_with :rspec, :mocha, :flexmock, or :rr
# end
#
# To use any other mock framework, you'll have to provide your own
# adapter. This is simply a module that responds to the following
# methods:
#
# setup_mocks_for_rspec
# verify_mocks_for_rspec
# teardown_mocks_for_rspec.
#
# These are your hooks into the lifecycle of a given example. RSpec will
# call setup_mocks_for_rspec before running anything else in each
# Example. After executing the #after methods, RSpec will then call
# verify_mocks_for_rspec and teardown_mocks_for_rspec (this is
# guaranteed to run even if there are failures in
# verify_mocks_for_rspec).
#
# Once you've defined this module, you can pass that to mock_with:
#
# Spec::Runner.configure do |config|
# config.mock_with MyMockFrameworkAdapter
# end
#
def mock_with(mock_framework)
@mock_framework = case mock_framework
when Symbol
mock_framework_path(mock_framework.to_s)
else
mock_framework
end
end
def mock_framework # :nodoc:
@mock_framework ||= mock_framework_path("rspec")
end
# :call-seq:
# include(Some::Helpers)
# include(Some::Helpers, More::Helpers)
# include(My::Helpers, :type => :key)
#
# Declares modules to be included in multiple example groups
# (<tt>describe</tt> blocks). With no :type, the modules listed will be
# included in all example groups. Use :type to restrict the inclusion to
# a subset of example groups. The value assigned to :type should be a
# key that maps to a class that is either a subclass of
# Spec::Example::ExampleGroup or extends Spec::Example::ExampleGroupMethods
# and includes Spec::Example::ExampleMethods
#
# config.include(My::Pony, My::Horse, :type => :farm)
#
# Only example groups that have that type will get the modules included:
#
# describe "Downtown", :type => :city do
# # Will *not* get My::Pony and My::Horse included
# end
#
# describe "Old Mac Donald", :type => :farm do
# # *Will* get My::Pony and My::Horse included
# end
#
def include(*args)
args << {} unless Hash === args.last
modules, options = args_and_options(*args)
required_example_group = get_type_from_options(options)
required_example_group = required_example_group.to_sym if required_example_group
modules.each do |mod|
ExampleGroupFactory.get(required_example_group).send(:include, mod)
end
end
# Defines global predicate matchers. Example:
#
# config.predicate_matchers[:swim] = :can_swim?
#
# This makes it possible to say:
#
# person.should swim # passes if person.can_swim? returns true
#
def predicate_matchers
@predicate_matchers ||= {}
end
# Prepends a global <tt>before</tt> block to all example groups.
# See #append_before for filtering semantics.
def prepend_before(*args, &proc)
scope, options = scope_and_options(*args)
example_group = ExampleGroupFactory.get(
get_type_from_options(options)
)
example_group.prepend_before(scope, &proc)
end
# Appends a global <tt>before</tt> block to all example groups.
#
# If you want to restrict the block to a subset of all the example
# groups then specify this in a Hash as the last argument:
#
# config.prepend_before(:all, :type => :farm)
#
# or
#
# config.prepend_before(:type => :farm)
#
def append_before(*args, &proc)
scope, options = scope_and_options(*args)
example_group = ExampleGroupFactory.get(
get_type_from_options(options)
)
example_group.append_before(scope, &proc)
end
alias_method :before, :append_before
# Prepends a global <tt>after</tt> block to all example groups.
# See #append_before for filtering semantics.
def prepend_after(*args, &proc)
scope, options = scope_and_options(*args)
example_group = ExampleGroupFactory.get(
get_type_from_options(options)
)
example_group.prepend_after(scope, &proc)
end
alias_method :after, :prepend_after
# Appends a global <tt>after</tt> block to all example groups.
# See #append_before for filtering semantics.
def append_after(*args, &proc)
scope, options = scope_and_options(*args)
example_group = ExampleGroupFactory.get(
get_type_from_options(options)
)
example_group.append_after(scope, &proc)
end
private
def scope_and_options(*args)
args, options = args_and_options(*args)
scope = (args[0] || :each), options
end
def get_type_from_options(options)
options[:type] || options[:behaviour_type]
end
def mock_framework_path(framework_name)
File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "plugins", "mock_frameworks", framework_name))
end
end
end
end

View file

@ -0,0 +1,9 @@
module Spec
module Example
class ExamplePendingError < StandardError
end
class PendingExampleFixedError < StandardError
end
end
end

View file

@ -0,0 +1,17 @@
module Spec
module Example
# Base class for customized example groups. Use this if you
# want to make a custom example group.
class ExampleGroup
extend Spec::Example::ExampleGroupMethods
include Spec::Example::ExampleMethods
def initialize(defined_description, &implementation)
@_defined_description = defined_description
@_implementation = implementation
end
end
end
end
Spec::ExampleGroup = Spec::Example::ExampleGroup

View file

@ -0,0 +1,64 @@
module Spec
module Example
class ExampleGroupFactory
class << self
def reset
@example_group_types = nil
default(ExampleGroup)
end
# Registers an example group class +klass+ with the symbol +type+. For
# example:
#
# Spec::Example::ExampleGroupFactory.register(:farm, FarmExampleGroup)
#
# With that you can append a hash with :type => :farm to the describe
# method and it will load an instance of FarmExampleGroup.
#
# describe Pig, :type => :farm do
# ...
#
# If you don't use the hash explicitly, <tt>describe</tt> will
# implicitly use an instance of FarmExampleGroup for any file loaded
# from the <tt>./spec/farm</tt> directory.
def register(key, example_group_class)
@example_group_types[key] = example_group_class
end
# Sets the default ExampleGroup class
def default(example_group_class)
old = @example_group_types
@example_group_types = Hash.new(example_group_class)
@example_group_types.merge!(old) if old
end
def get(key=nil)
if @example_group_types.values.include?(key)
key
else
@example_group_types[key]
end
end
def create_example_group(*args, &block)
opts = Hash === args.last ? args.last : {}
superclass = determine_superclass(opts)
superclass.describe(*args, &block)
end
protected
def determine_superclass(opts)
key = if opts[:type]
opts[:type]
elsif opts[:spec_path] =~ /spec(\\|\/)(#{@example_group_types.keys.join('|')})/
$2 == '' ? nil : $2.to_sym
end
get(key)
end
end
self.reset
end
end
end

View file

@ -0,0 +1,440 @@
module Spec
module Example
module ExampleGroupMethods
class << self
attr_accessor :matcher_class
def description_text(*args)
args.inject("") do |result, arg|
result << " " unless (result == "" || arg.to_s =~ /^(\s|\.|#)/)
result << arg.to_s
end
end
end
attr_reader :description_text, :description_args, :description_options, :spec_path, :registration_binding_block
def inherited(klass)
super
klass.register {}
Spec::Runner.register_at_exit_hook
end
# Makes the describe/it syntax available from a class. For example:
#
# class StackSpec < Spec::ExampleGroup
# describe Stack, "with no elements"
#
# before
# @stack = Stack.new
# end
#
# it "should raise on pop" do
# lambda{ @stack.pop }.should raise_error
# end
# end
#
def describe(*args, &example_group_block)
args << {} unless Hash === args.last
if example_group_block
params = args.last
params[:spec_path] = eval("caller(0)[1]", example_group_block) unless params[:spec_path]
if params[:shared]
SharedExampleGroup.new(*args, &example_group_block)
else
self.subclass("Subclass") do
describe(*args)
module_eval(&example_group_block)
end
end
else
set_description(*args)
before_eval
self
end
end
alias :context :describe
# Use this to pull in examples from shared example groups.
# See Spec::Runner for information about shared example groups.
def it_should_behave_like(shared_example_group)
case shared_example_group
when SharedExampleGroup
include shared_example_group
else
example_group = SharedExampleGroup.find_shared_example_group(shared_example_group)
unless example_group
raise RuntimeError.new("Shared Example Group '#{shared_example_group}' can not be found")
end
include(example_group)
end
end
# :call-seq:
# predicate_matchers[matcher_name] = method_on_object
# predicate_matchers[matcher_name] = [method1_on_object, method2_on_object]
#
# Dynamically generates a custom matcher that will match
# a predicate on your class. RSpec provides a couple of these
# out of the box:
#
# exist (or state expectations)
# File.should exist("path/to/file")
#
# an_instance_of (for mock argument constraints)
# mock.should_receive(:message).with(an_instance_of(String))
#
# == Examples
#
# class Fish
# def can_swim?
# true
# end
# end
#
# describe Fish do
# predicate_matchers[:swim] = :can_swim?
# it "should swim" do
# Fish.new.should swim
# end
# end
def predicate_matchers
@predicate_matchers ||= {:an_instance_of => :is_a?}
end
# Creates an instance of Spec::Example::Example and adds
# it to a collection of examples of the current example group.
def it(description=nil, &implementation)
e = new(description, &implementation)
example_objects << e
e
end
alias_method :specify, :it
# Use this to temporarily disable an example.
def xit(description=nil, opts={}, &block)
Kernel.warn("Example disabled: #{description}")
end
alias_method :xspecify, :xit
def run
examples = examples_to_run
reporter.add_example_group(self) unless examples_to_run.empty?
return true if examples.empty?
return dry_run(examples) if dry_run?
plugin_mock_framework
define_methods_from_predicate_matchers
success, before_all_instance_variables = run_before_all
success, after_all_instance_variables = execute_examples(success, before_all_instance_variables, examples)
success = run_after_all(success, after_all_instance_variables)
end
def description
result = ExampleGroupMethods.description_text(*description_parts)
if result.nil? || result == ""
return to_s
else
result
end
end
def described_type
description_parts.find {|part| part.is_a?(Module)}
end
def description_parts #:nodoc:
parts = []
execute_in_class_hierarchy do |example_group|
parts << example_group.description_args
end
parts.flatten.compact
end
def set_description(*args)
args, options = args_and_options(*args)
@description_args = args
@description_options = options
@description_text = ExampleGroupMethods.description_text(*args)
@spec_path = File.expand_path(options[:spec_path]) if options[:spec_path]
if described_type.class == Module
@described_module = described_type
end
self
end
attr_reader :described_module
def examples #:nodoc:
examples = example_objects.dup
add_method_examples(examples)
rspec_options.reverse ? examples.reverse : examples
end
def number_of_examples #:nodoc:
examples.length
end
# Registers a block to be executed before each example.
# This method prepends +block+ to existing before blocks.
def prepend_before(*args, &block)
scope, options = scope_and_options(*args)
parts = before_parts_from_scope(scope)
parts.unshift(block)
end
# Registers a block to be executed before each example.
# This method appends +block+ to existing before blocks.
def append_before(*args, &block)
scope, options = scope_and_options(*args)
parts = before_parts_from_scope(scope)
parts << block
end
alias_method :before, :append_before
# Registers a block to be executed after each example.
# This method prepends +block+ to existing after blocks.
def prepend_after(*args, &block)
scope, options = scope_and_options(*args)
parts = after_parts_from_scope(scope)
parts.unshift(block)
end
alias_method :after, :prepend_after
# Registers a block to be executed after each example.
# This method appends +block+ to existing after blocks.
def append_after(*args, &block)
scope, options = scope_and_options(*args)
parts = after_parts_from_scope(scope)
parts << block
end
def remove_after(scope, &block)
after_each_parts.delete(block)
end
# Deprecated. Use before(:each)
def setup(&block)
before(:each, &block)
end
# Deprecated. Use after(:each)
def teardown(&block)
after(:each, &block)
end
def before_all_parts # :nodoc:
@before_all_parts ||= []
end
def after_all_parts # :nodoc:
@after_all_parts ||= []
end
def before_each_parts # :nodoc:
@before_each_parts ||= []
end
def after_each_parts # :nodoc:
@after_each_parts ||= []
end
# Only used from RSpec's own examples
def reset # :nodoc:
@before_all_parts = nil
@after_all_parts = nil
@before_each_parts = nil
@after_each_parts = nil
end
def register(&registration_binding_block)
@registration_binding_block = registration_binding_block
rspec_options.add_example_group self
end
def unregister #:nodoc:
rspec_options.remove_example_group self
end
def registration_backtrace
eval("caller", registration_binding_block)
end
def run_before_each(example)
execute_in_class_hierarchy do |example_group|
example.eval_each_fail_fast(example_group.before_each_parts)
end
end
def run_after_each(example)
execute_in_class_hierarchy(:superclass_first) do |example_group|
example.eval_each_fail_slow(example_group.after_each_parts)
end
end
private
def dry_run(examples)
examples.each do |example|
rspec_options.reporter.example_started(example)
rspec_options.reporter.example_finished(example)
end
return true
end
def run_before_all
before_all = new("before(:all)")
begin
execute_in_class_hierarchy do |example_group|
before_all.eval_each_fail_fast(example_group.before_all_parts)
end
return [true, before_all.instance_variable_hash]
rescue Exception => e
reporter.failure(before_all, e)
return [false, before_all.instance_variable_hash]
end
end
def execute_examples(success, instance_variables, examples)
return [success, instance_variables] unless success
after_all_instance_variables = instance_variables
examples.each do |example_group_instance|
success &= example_group_instance.execute(rspec_options, instance_variables)
after_all_instance_variables = example_group_instance.instance_variable_hash
end
return [success, after_all_instance_variables]
end
def run_after_all(success, instance_variables)
after_all = new("after(:all)")
after_all.set_instance_variables_from_hash(instance_variables)
execute_in_class_hierarchy(:superclass_first) do |example_group|
after_all.eval_each_fail_slow(example_group.after_all_parts)
end
return success
rescue Exception => e
reporter.failure(after_all, e)
return false
end
def examples_to_run
all_examples = examples
return all_examples unless specified_examples?
all_examples.reject do |example|
matcher = ExampleGroupMethods.matcher_class.
new(description.to_s, example.description)
!matcher.matches?(specified_examples)
end
end
def specified_examples?
specified_examples && !specified_examples.empty?
end
def specified_examples
rspec_options.examples
end
def reporter
rspec_options.reporter
end
def dry_run?
rspec_options.dry_run
end
def example_objects
@example_objects ||= []
end
def execute_in_class_hierarchy(superclass_last=false)
classes = []
current_class = self
while is_example_group?(current_class)
superclass_last ? classes << current_class : classes.unshift(current_class)
current_class = current_class.superclass
end
superclass_last ? classes << ExampleMethods : classes.unshift(ExampleMethods)
classes.each do |example_group|
yield example_group
end
end
def is_example_group?(klass)
Module === klass && klass.kind_of?(ExampleGroupMethods)
end
def plugin_mock_framework
case mock_framework = Spec::Runner.configuration.mock_framework
when Module
include mock_framework
else
require Spec::Runner.configuration.mock_framework
include Spec::Plugins::MockFramework
end
end
def define_methods_from_predicate_matchers # :nodoc:
all_predicate_matchers = predicate_matchers.merge(
Spec::Runner.configuration.predicate_matchers
)
all_predicate_matchers.each_pair do |matcher_method, method_on_object|
define_method matcher_method do |*args|
eval("be_#{method_on_object.to_s.gsub('?','')}(*args)")
end
end
end
def scope_and_options(*args)
args, options = args_and_options(*args)
scope = (args[0] || :each), options
end
def before_parts_from_scope(scope)
case scope
when :each; before_each_parts
when :all; before_all_parts
when :suite; rspec_options.before_suite_parts
end
end
def after_parts_from_scope(scope)
case scope
when :each; after_each_parts
when :all; after_all_parts
when :suite; rspec_options.after_suite_parts
end
end
def before_eval
end
def add_method_examples(examples)
instance_methods.sort.each do |method_name|
if example_method?(method_name)
examples << new(method_name) do
__send__(method_name)
end
end
end
end
def example_method?(method_name)
should_method?(method_name)
end
def should_method?(method_name)
!(method_name =~ /^should(_not)?$/) &&
method_name =~ /^should/ && (
instance_method(method_name).arity == 0 ||
instance_method(method_name).arity == -1
)
end
end
end
end

View file

@ -0,0 +1,44 @@
module Spec
module Example
class ExampleMatcher
def initialize(example_group_description, example_name)
@example_group_description = example_group_description
@example_name = example_name
end
def matches?(specified_examples)
specified_examples.each do |specified_example|
return true if matches_literal_example?(specified_example) || matches_example_not_considering_modules?(specified_example)
end
false
end
protected
def matches_literal_example?(specified_example)
specified_example =~ /(^#{example_group_regex} #{example_regexp}$|^#{example_group_regex}$|^#{example_group_with_before_all_regexp}$|^#{example_regexp}$)/
end
def matches_example_not_considering_modules?(specified_example)
specified_example =~ /(^#{example_group_regex_not_considering_modules} #{example_regexp}$|^#{example_group_regex_not_considering_modules}$|^#{example_regexp}$)/
end
def example_group_regex
Regexp.escape(@example_group_description)
end
def example_group_with_before_all_regexp
Regexp.escape("#{@example_group_description} before(:all)")
end
def example_group_regex_not_considering_modules
Regexp.escape(@example_group_description.split('::').last)
end
def example_regexp
Regexp.escape(@example_name)
end
end
ExampleGroupMethods.matcher_class = ExampleMatcher
end
end

View file

@ -0,0 +1,112 @@
module Spec
module Example
module ExampleMethods
extend ExampleGroupMethods
extend ModuleReopeningFix
include ModuleInclusionWarnings
PENDING_EXAMPLE_BLOCK = lambda {
raise Spec::Example::ExamplePendingError.new("Not Yet Implemented")
}
def execute(options, instance_variables)
options.reporter.example_started(self)
set_instance_variables_from_hash(instance_variables)
execution_error = nil
Timeout.timeout(options.timeout) do
begin
before_example
run_with_description_capturing
rescue Exception => e
execution_error ||= e
end
begin
after_example
rescue Exception => e
execution_error ||= e
end
end
options.reporter.example_finished(self, execution_error)
success = execution_error.nil? || ExamplePendingError === execution_error
end
def instance_variable_hash
instance_variables.inject({}) do |variable_hash, variable_name|
variable_hash[variable_name] = instance_variable_get(variable_name)
variable_hash
end
end
def violated(message="")
raise Spec::Expectations::ExpectationNotMetError.new(message)
end
def eval_each_fail_fast(procs) #:nodoc:
procs.each do |proc|
instance_eval(&proc)
end
end
def eval_each_fail_slow(procs) #:nodoc:
first_exception = nil
procs.each do |proc|
begin
instance_eval(&proc)
rescue Exception => e
first_exception ||= e
end
end
raise first_exception if first_exception
end
def description
@_defined_description || @_matcher_description || "NO NAME"
end
def __full_description
"#{self.class.description} #{self.description}"
end
def set_instance_variables_from_hash(ivars)
ivars.each do |variable_name, value|
# Ruby 1.9 requires variable.to_s on the next line
unless ['@_implementation', '@_defined_description', '@_matcher_description', '@method_name'].include?(variable_name.to_s)
instance_variable_set variable_name, value
end
end
end
def run_with_description_capturing
begin
return instance_eval(&(@_implementation || PENDING_EXAMPLE_BLOCK))
ensure
@_matcher_description = Spec::Matchers.generated_description
Spec::Matchers.clear_generated_description
end
end
def implementation_backtrace
eval("caller", @_implementation)
end
protected
include Matchers
include Pending
def before_example
setup_mocks_for_rspec
self.class.run_before_each(self)
end
def after_example
self.class.run_after_each(self)
verify_mocks_for_rspec
ensure
teardown_mocks_for_rspec
end
end
end
end

View file

@ -0,0 +1,37 @@
module Spec
module Example
# In the future, modules will no longer be automatically included
# in the Example Group (based on the description name); when that
# time comes, this code should be removed.
module ModuleInclusionWarnings
# Thanks, Francis Hwang.
class MethodDispatcher
def initialize(mod, target=nil)
@mod = mod
@target = target
end
def respond_to?(sym)
@mod && @mod.instance_methods.include?(sym.to_s)
end
def call(sym, *args, &blk)
Kernel.warn("Modules will no longer be automatically included in RSpec version 1.1.4. Called from #{caller[2]}")
@target.extend @mod
@target.send(sym, *args, &blk)
end
end
def respond_to?(sym)
MethodDispatcher.new(self.class.described_module).respond_to?(sym) ? true : super
end
private
def method_missing(sym, *args, &blk)
md = MethodDispatcher.new(self.class.described_module, self)
self.respond_to?(sym) ? md.call(sym, *args, &blk) : super
end
end
end
end

View file

@ -0,0 +1,21 @@
module Spec
module Example
# This is a fix for ...Something in Ruby 1.8.6??... (Someone fill in here please - Aslak)
module ModuleReopeningFix
def child_modules
@child_modules ||= []
end
def included(mod)
child_modules << mod
end
def include(mod)
super
child_modules.each do |child_module|
child_module.__send__(:include, mod)
end
end
end
end
end

View file

@ -0,0 +1,18 @@
module Spec
module Example
module Pending
def pending(message = "TODO")
if block_given?
begin
yield
rescue Exception => e
raise Spec::Example::ExamplePendingError.new(message)
end
raise Spec::Example::PendingExampleFixedError.new("Expected pending '#{message}' to fail. No Error was raised.")
else
raise Spec::Example::ExamplePendingError.new(message)
end
end
end
end
end

View file

@ -0,0 +1,58 @@
module Spec
module Example
class SharedExampleGroup < Module
class << self
def add_shared_example_group(new_example_group)
guard_against_redefining_existing_example_group(new_example_group)
shared_example_groups << new_example_group
end
def find_shared_example_group(example_group_description)
shared_example_groups.find do |b|
b.description == example_group_description
end
end
def shared_example_groups
# TODO - this needs to be global, or at least accessible from
# from subclasses of Example in a centralized place. I'm not loving
# this as a solution, but it works for now.
$shared_example_groups ||= []
end
private
def guard_against_redefining_existing_example_group(new_example_group)
existing_example_group = find_shared_example_group(new_example_group.description)
return unless existing_example_group
return if new_example_group.equal?(existing_example_group)
return if spec_path(new_example_group) == spec_path(existing_example_group)
raise ArgumentError.new("Shared Example '#{existing_example_group.description}' already exists")
end
def spec_path(example_group)
File.expand_path(example_group.spec_path)
end
end
include ExampleGroupMethods
public :include
def initialize(*args, &example_group_block)
describe(*args)
@example_group_block = example_group_block
self.class.add_shared_example_group(self)
end
def included(mod) # :nodoc:
mod.module_eval(&@example_group_block)
end
def execute_in_class_hierarchy(superclass_last=false)
classes = [self]
superclass_last ? classes << ExampleMethods : classes.unshift(ExampleMethods)
classes.each do |example_group|
yield example_group
end
end
end
end
end