aslakhellesoy-cucumber 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +73 -0
- data/README.txt +78 -0
- data/Rakefile +4 -0
- data/bin/cucumber +3 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +15 -0
- data/examples/Rakefile +7 -0
- data/examples/pure_ruby/addition.rb +16 -0
- data/examples/pure_ruby/steps/addition_steps.rb +43 -0
- data/examples/simple/addition.story +12 -0
- data/examples/simple/division.story +17 -0
- data/examples/simple/steps/addition_steps.rb +43 -0
- data/examples/simple_norwegian/steg/matte_steg.rb.rb +31 -0
- data/examples/simple_norwegian/summering.story +10 -0
- data/examples/web/run_stories.story +10 -0
- data/examples/web/steps/stories_steps.rb +39 -0
- data/gem_tasks/deployment.rake +34 -0
- data/gem_tasks/environment.rake +7 -0
- data/gem_tasks/fix_cr_lf.rake +10 -0
- data/gem_tasks/rspec.rake +21 -0
- data/gem_tasks/treetop.rake +33 -0
- data/gem_tasks/website.rake +17 -0
- data/lib/cucumber.rb +17 -0
- data/lib/cucumber/cli.rb +118 -0
- data/lib/cucumber/core_ext/proc.rb +35 -0
- data/lib/cucumber/core_ext/string.rb +17 -0
- data/lib/cucumber/executor.rb +85 -0
- data/lib/cucumber/formatters.rb +1 -0
- data/lib/cucumber/formatters/ansicolor.rb +89 -0
- data/lib/cucumber/formatters/html_formatter.rb +271 -0
- data/lib/cucumber/formatters/pretty_formatter.rb +66 -0
- data/lib/cucumber/formatters/progress_formatter.rb +41 -0
- data/lib/cucumber/parser/languages.yml +35 -0
- data/lib/cucumber/parser/nodes.rb +88 -0
- data/lib/cucumber/parser/story_parser.rb +13 -0
- data/lib/cucumber/parser/story_parser.treetop.erb +41 -0
- data/lib/cucumber/parser/story_parser_en.rb +554 -0
- data/lib/cucumber/parser/story_parser_fr.rb +554 -0
- data/lib/cucumber/parser/story_parser_no.rb +554 -0
- data/lib/cucumber/parser/story_parser_pt.rb +554 -0
- data/lib/cucumber/parser/top_down_visitor.rb +26 -0
- data/lib/cucumber/rails/world.rb +69 -0
- data/lib/cucumber/rake/task.rb +74 -0
- data/lib/cucumber/ruby_tree.rb +14 -0
- data/lib/cucumber/ruby_tree/nodes.rb +68 -0
- data/lib/cucumber/step_methods.rb +39 -0
- data/lib/cucumber/step_mother.rb +38 -0
- data/lib/cucumber/tree.rb +127 -0
- data/lib/cucumber/version.rb +9 -0
- data/script/console +10 -0
- data/script/console.cmd +1 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/txt2html +74 -0
- data/script/txt2html.cmd +1 -0
- data/setup.rb +1585 -0
- data/spec/cucumber/core_ext/string_spec.rb +20 -0
- data/spec/cucumber/executor_spec.rb +55 -0
- data/spec/cucumber/formatters/ansicolor_spec.rb +18 -0
- data/spec/cucumber/formatters/html_formatter_spec.rb +59 -0
- data/spec/cucumber/formatters/stories.html +274 -0
- data/spec/cucumber/sell_cucumbers.story +9 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +7 -0
- data/website/index.html +11 -0
- data/website/index.txt +39 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.html.erb +48 -0
- metadata +157 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Parser
|
3
|
+
class TopDownVisitor
|
4
|
+
def visit_stories(stories)
|
5
|
+
stories.accept(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def visit_story(story)
|
9
|
+
story.accept(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_header(header)
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_narrative(narrative)
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_scenario(scenario)
|
19
|
+
scenario.accept(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit_step(step)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Based on code from Brian Takita, Yurii Rashkovskii and Ben Mabey
|
2
|
+
# Adapted by Aslak Hellesøy
|
3
|
+
|
4
|
+
if defined?(ActiveRecord::Base)
|
5
|
+
require 'test_help'
|
6
|
+
else
|
7
|
+
require 'action_controller/test_process'
|
8
|
+
require 'action_controller/integration'
|
9
|
+
end
|
10
|
+
require 'test/unit/testresult'
|
11
|
+
require 'spec'
|
12
|
+
require 'spec/rails'
|
13
|
+
|
14
|
+
# These allow exceptions to come through as opposed to being caught and hvaing non-helpful responses returned.
|
15
|
+
ActionController::Base.class_eval do
|
16
|
+
def perform_action
|
17
|
+
perform_action_without_rescue
|
18
|
+
end
|
19
|
+
end
|
20
|
+
Dispatcher.class_eval do
|
21
|
+
def self.failsafe_response(output, status, exception = nil)
|
22
|
+
raise exception
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# So that Test::Unit doesn't launch at the end - makes it think it has already been run.
|
27
|
+
Test::Unit.run = true
|
28
|
+
|
29
|
+
# Hack to stop RSpec from dumping the summary
|
30
|
+
Spec::Runner::Options.class_eval do
|
31
|
+
def examples_should_be_run?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
ActionController::Integration::Session.send(:include, Spec::Matchers)
|
37
|
+
ActionController::Integration::Session.send(:include, Spec::Rails::Matchers)
|
38
|
+
|
39
|
+
module Cucumber
|
40
|
+
module Rails
|
41
|
+
class World < ActionController::IntegrationTest
|
42
|
+
if defined?(ActiveRecord::Base)
|
43
|
+
self.use_transactional_fixtures = true
|
44
|
+
else
|
45
|
+
def self.fixture_table_names; []; end # Workaround for projects that don't use ActiveRecord
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize #:nodoc:
|
49
|
+
@_result = Test::Unit::TestResult.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
World do
|
56
|
+
Cucumber::Rails::World.new
|
57
|
+
end
|
58
|
+
|
59
|
+
if defined?(ActiveRecord::Base)
|
60
|
+
Before do
|
61
|
+
ActiveRecord::Base.send :increment_open_transactions
|
62
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
63
|
+
end
|
64
|
+
|
65
|
+
After do
|
66
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
67
|
+
ActiveRecord::Base.send :decrement_open_transactions
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Rake
|
3
|
+
# Defines a task for running stories.
|
4
|
+
# TODO: Base on http://github.com/dchelimsky/rspec/tree/master/lib/spec/rake/spectask.rb
|
5
|
+
class Task
|
6
|
+
LIB = File.expand_path(File.dirname(__FILE__) + '/../..')
|
7
|
+
BINARY = File.expand_path(File.dirname(__FILE__) + '/../../../bin/cucumber')
|
8
|
+
|
9
|
+
attr_accessor :libs
|
10
|
+
attr_accessor :step_list
|
11
|
+
attr_accessor :step_pattern
|
12
|
+
attr_accessor :story_list
|
13
|
+
attr_accessor :story_pattern
|
14
|
+
attr_accessor :cucumber_opts
|
15
|
+
|
16
|
+
# Define a task
|
17
|
+
def initialize(stories_path = "stories")
|
18
|
+
@stories_path = stories_path
|
19
|
+
@libs = [LIB]
|
20
|
+
|
21
|
+
yield self if block_given?
|
22
|
+
|
23
|
+
@story_pattern = "#{@stories_path}/**/*.story" if story_pattern.nil? && story_list.nil?
|
24
|
+
@step_pattern = "#{@stories_path}/**/*.rb" if step_pattern.nil? && step_list.nil?
|
25
|
+
define_tasks
|
26
|
+
end
|
27
|
+
|
28
|
+
def define_tasks
|
29
|
+
namespace :cucumber do
|
30
|
+
desc "Run Cucumber Stories under #{@stories_path}"
|
31
|
+
task @stories_path do
|
32
|
+
args = []
|
33
|
+
args << '-I'
|
34
|
+
args << '"%s"' % libs.join(File::PATH_SEPARATOR)
|
35
|
+
args << '"%s"' % BINARY
|
36
|
+
args << (ENV['CUCUMBER_OPTS'] || cucumber_opts)
|
37
|
+
|
38
|
+
step_files.each do |step_file|
|
39
|
+
args << '--require'
|
40
|
+
args << step_file
|
41
|
+
end
|
42
|
+
args << story_files
|
43
|
+
args.flatten!
|
44
|
+
args.compact!
|
45
|
+
ruby(args.join(" ")) # ruby(*args) is broken on Windows
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def story_files # :nodoc:
|
52
|
+
if ENV['STORY']
|
53
|
+
FileList[ ENV['STORY'] ]
|
54
|
+
else
|
55
|
+
result = []
|
56
|
+
result += story_list.to_a if story_list
|
57
|
+
result += FileList[story_pattern].to_a if story_pattern
|
58
|
+
FileList[result]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def step_files # :nodoc:
|
63
|
+
if ENV['STEPS']
|
64
|
+
FileList[ ENV['STEPS'] ]
|
65
|
+
else
|
66
|
+
result = []
|
67
|
+
result += step_list.to_a if step_list
|
68
|
+
result += FileList[step_pattern].to_a if step_pattern
|
69
|
+
FileList[result]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'cucumber/ruby_tree/nodes'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
# Classes in this module implement the pure ruby stories in Cucumber.
|
5
|
+
module RubyTree
|
6
|
+
def Story(header, narrative, &proc)
|
7
|
+
stories << RubyStory.new(header, narrative, &proc)
|
8
|
+
end
|
9
|
+
|
10
|
+
def stories #:nodoc:
|
11
|
+
@stories ||= Tree::Stories.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'cucumber/tree'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module RubyTree
|
5
|
+
class RubyStory
|
6
|
+
include Tree::Story
|
7
|
+
|
8
|
+
def initialize(header, narrative, &proc)
|
9
|
+
@header, @narrative = header, narrative
|
10
|
+
@scenarios = []
|
11
|
+
instance_eval(&proc)
|
12
|
+
end
|
13
|
+
|
14
|
+
def Scenario(name, &proc)
|
15
|
+
@scenarios << RubyScenario.new(name, &proc)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
attr_reader :header, :narrative, :scenarios
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class RubyScenario
|
25
|
+
include Tree::Scenario
|
26
|
+
|
27
|
+
def initialize(name, &proc)
|
28
|
+
@name = name
|
29
|
+
@steps = []
|
30
|
+
@line = *caller[2].split(':')[1].to_i
|
31
|
+
instance_eval(&proc)
|
32
|
+
end
|
33
|
+
|
34
|
+
def Given(name)
|
35
|
+
@steps << RubyStep.new('Given', name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def When(name)
|
39
|
+
@steps << RubyStep.new('When', name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def Then(name)
|
43
|
+
@steps << RubyStep.new('Then', name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def And(name)
|
47
|
+
@steps << RubyStep.new('And', name)
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :name, :steps, :line
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class RubyStep
|
55
|
+
include Tree::Step
|
56
|
+
attr_accessor :error
|
57
|
+
|
58
|
+
def initialize(keyword, name)
|
59
|
+
@keyword, @name = keyword, name
|
60
|
+
@file, @line, _ = *caller[2].split(':')
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_reader :keyword, :name, :file, :line
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'cucumber/step_mother'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module StepMethods
|
5
|
+
def World(&proc)
|
6
|
+
$executor.register_world_proc(&proc)
|
7
|
+
end
|
8
|
+
|
9
|
+
def Before(&proc)
|
10
|
+
$executor.register_before_proc(&proc)
|
11
|
+
end
|
12
|
+
|
13
|
+
def After(&proc)
|
14
|
+
$executor.register_after_proc(&proc)
|
15
|
+
end
|
16
|
+
|
17
|
+
def Given(key, &proc)
|
18
|
+
step_mother.register_step_proc(key, &proc)
|
19
|
+
end
|
20
|
+
|
21
|
+
def When(key, &proc)
|
22
|
+
step_mother.register_step_proc(key, &proc)
|
23
|
+
end
|
24
|
+
|
25
|
+
def Then(key, &proc)
|
26
|
+
step_mother.register_step_proc(key, &proc)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Simple workaround for old skool steps
|
30
|
+
def steps_for(*_)
|
31
|
+
STDERR.puts "WARNING: In Cucumber the steps_for method is obsolete"
|
32
|
+
yield
|
33
|
+
end
|
34
|
+
|
35
|
+
def step_mother #:nodoc:
|
36
|
+
@step_mother ||= StepMother.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'cucumber/parser/top_down_visitor'
|
2
|
+
require 'cucumber/core_ext/proc'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
# A StepMother keeps track of step procs and assigns them
|
6
|
+
# to each step when visiting the tree.
|
7
|
+
class StepMother < Parser::TopDownVisitor
|
8
|
+
def initialize
|
9
|
+
@step_procs = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def register_step_proc(key, &proc)
|
13
|
+
regexp = case(key)
|
14
|
+
when String
|
15
|
+
Regexp.new("^#{key}$") # TODO: replace $variable with (.*)
|
16
|
+
when Regexp
|
17
|
+
key
|
18
|
+
else
|
19
|
+
raise "Step patterns must be Regexp or String, but was: #{key.inspect}"
|
20
|
+
end
|
21
|
+
proc.extend(CoreExt::CallIn)
|
22
|
+
proc.name = key.inspect
|
23
|
+
@step_procs[regexp] = proc
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_step(step)
|
27
|
+
# Maybe we shouldn't attach the regexp etc to
|
28
|
+
# the step? Maybe steps pull them out as needed?
|
29
|
+
# Do we then have to attach ourself to the step instead?
|
30
|
+
# What would we gain from a pull design?
|
31
|
+
@step_procs.each do |regexp, proc|
|
32
|
+
if step.name =~ regexp
|
33
|
+
step.attach(regexp, proc, $~.captures)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'cucumber/core_ext/proc'
|
2
|
+
require 'cucumber/core_ext/string'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Tree
|
6
|
+
class Stories
|
7
|
+
def initialize
|
8
|
+
@stories = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def length
|
12
|
+
@stories.length
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(story)
|
16
|
+
@stories << story
|
17
|
+
end
|
18
|
+
|
19
|
+
def accept(visitor)
|
20
|
+
@stories.each{|story| visitor.visit_story(story)}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Story
|
25
|
+
def accept(visitor)
|
26
|
+
visitor.visit_header(header)
|
27
|
+
visitor.visit_narrative(narrative)
|
28
|
+
scenarios.each do |scenario|
|
29
|
+
visitor.visit_scenario(scenario)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Scenario
|
35
|
+
def accept(visitor)
|
36
|
+
steps.each do |step|
|
37
|
+
visitor.visit_step(step)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def at_line?(l)
|
42
|
+
line == l || steps.map{|s| s.line}.index(l)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Step
|
47
|
+
def self.included(base)
|
48
|
+
base.class_eval do
|
49
|
+
def self.new_id!
|
50
|
+
@next_id ||= -1
|
51
|
+
@next_id += 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :error
|
57
|
+
|
58
|
+
def regexp
|
59
|
+
@regexp || //
|
60
|
+
end
|
61
|
+
|
62
|
+
PENDING = lambda do |*_|
|
63
|
+
raise Pending
|
64
|
+
end
|
65
|
+
PENDING.extend(CoreExt::CallIn)
|
66
|
+
PENDING.name = "PENDING"
|
67
|
+
|
68
|
+
def proc
|
69
|
+
@proc || PENDING
|
70
|
+
end
|
71
|
+
|
72
|
+
def attach(regexp, proc, args)
|
73
|
+
if @regexp
|
74
|
+
raise <<-EOM
|
75
|
+
"#{name}" matches several step definitions:
|
76
|
+
|
77
|
+
#{@proc.backtrace_line}
|
78
|
+
#{proc.backtrace_line}
|
79
|
+
|
80
|
+
Please give your steps unambiguous names
|
81
|
+
EOM
|
82
|
+
end
|
83
|
+
@regexp, @proc, @args = regexp, proc, args
|
84
|
+
end
|
85
|
+
|
86
|
+
def execute_in(world)
|
87
|
+
strip_pos = nil
|
88
|
+
begin
|
89
|
+
proc.call_in(world, *@args)
|
90
|
+
rescue ArgCountError => e
|
91
|
+
e.backtrace[0] = @proc.backtrace_line
|
92
|
+
strip_pos = e.backtrace.index("#{__FILE__}:#{__LINE__-3}:in `execute_in'")
|
93
|
+
format_error(strip_pos, e)
|
94
|
+
rescue => e
|
95
|
+
method_line = "#{__FILE__}:#{__LINE__-6}:in `execute_in'"
|
96
|
+
method_line_pos = e.backtrace.index(method_line)
|
97
|
+
if method_line_pos
|
98
|
+
strip_pos = method_line_pos - (Pending === e ? 3 : 2)
|
99
|
+
else
|
100
|
+
# This happens with rails, because they screw up the backtrace
|
101
|
+
# before we get here (injecting erb stactrace and such)
|
102
|
+
end
|
103
|
+
format_error(strip_pos, e)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def format_error(strip_pos, e)
|
108
|
+
@error = e
|
109
|
+
# Remove lines underneath the plain text step
|
110
|
+
e.backtrace[strip_pos..-1] = nil unless strip_pos.nil?
|
111
|
+
e.backtrace.flatten
|
112
|
+
# Replace the step line with something more readable
|
113
|
+
e.backtrace.replace(e.backtrace.map{|l| l.gsub(/`#{proc.meth}'/, "`#{keyword} #{proc.name}'")})
|
114
|
+
e.backtrace << "#{file}:#{line}:in `#{keyword} #{name}'"
|
115
|
+
raise e
|
116
|
+
end
|
117
|
+
|
118
|
+
def gzub(format=nil, &proc)
|
119
|
+
name.gzub(regexp, format, &proc)
|
120
|
+
end
|
121
|
+
|
122
|
+
def id
|
123
|
+
@id ||= self.class.new_id!
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|