cucumber 0.2.0 → 0.2.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 +19 -0
- data/Manifest.txt +5 -1
- data/examples/self_test/features/tons_of_cukes.feature +52 -0
- data/examples/sinatra/features/support/env.rb +6 -2
- data/examples/tickets/features/248.feature +11 -0
- data/examples/tickets/features/step_definitons/248_steps.rb +15 -0
- data/features/cucumber_cli.feature +1 -1
- data/features/custom_formatter.feature +2 -2
- data/features/usage.feature +108 -0
- data/lib/autotest/cucumber_mixin.rb +1 -1
- data/lib/cucumber/ast/feature.rb +1 -1
- data/lib/cucumber/ast/features.rb +6 -0
- data/lib/cucumber/ast/step.rb +2 -13
- data/lib/cucumber/ast/step_invocation.rb +11 -3
- data/lib/cucumber/ast/table.rb +4 -0
- data/lib/cucumber/cli/configuration.rb +11 -14
- data/lib/cucumber/cli/main.rb +14 -21
- data/lib/cucumber/formatter.rb +1 -1
- data/lib/cucumber/formatter/html.rb +47 -8
- data/lib/cucumber/formatter/pretty.rb +1 -2
- data/lib/cucumber/formatter/rerun.rb +8 -0
- data/lib/cucumber/formatter/usage.rb +69 -0
- data/lib/cucumber/jbehave.rb +1 -0
- data/lib/cucumber/rails/world.rb +22 -21
- data/lib/cucumber/step_definition.rb +65 -54
- data/lib/cucumber/step_match.rb +10 -2
- data/lib/cucumber/step_mother.rb +1 -8
- data/lib/cucumber/version.rb +1 -1
- data/rails_generators/cucumber/templates/env.rb +2 -0
- data/rails_generators/feature/templates/steps.erb +1 -1
- data/spec/cucumber/ast/feature_spec.rb +2 -1
- data/spec/cucumber/ast/step_collection_spec.rb +8 -0
- data/spec/cucumber/cli/configuration_spec.rb +18 -6
- data/spec/cucumber/cli/main_spec.rb +1 -13
- data/spec/cucumber/parser/feature_parser_spec.rb +15 -15
- data/spec/cucumber/step_definition_spec.rb +21 -9
- metadata +7 -3
- data/gem_tasks/jar.rake +0 -67
data/lib/cucumber/cli/main.rb
CHANGED
@@ -32,13 +32,6 @@ module Cucumber
|
|
32
32
|
step_mother.options = configuration.options
|
33
33
|
|
34
34
|
require_files
|
35
|
-
|
36
|
-
if(configuration.print_step_definitions?)
|
37
|
-
step_mother.print_step_definitions(@out_stream)
|
38
|
-
Kernel.exit(0)
|
39
|
-
return # In specs, exit is stubbed
|
40
|
-
end
|
41
|
-
|
42
35
|
enable_diffing
|
43
36
|
|
44
37
|
features = load_plain_text_features
|
@@ -53,7 +46,18 @@ module Cucumber
|
|
53
46
|
Kernel.exit(failure ? 1 : 0)
|
54
47
|
end
|
55
48
|
|
56
|
-
|
49
|
+
def load_plain_text_features
|
50
|
+
features = Ast::Features.new
|
51
|
+
parser = Parser::FeatureParser.new
|
52
|
+
|
53
|
+
verbose_log("Features:")
|
54
|
+
configuration.feature_files.each do |f|
|
55
|
+
features.add_feature(parser.parse_file(f))
|
56
|
+
verbose_log(" * #{f}")
|
57
|
+
end
|
58
|
+
verbose_log("\n"*2)
|
59
|
+
features
|
60
|
+
end
|
57
61
|
|
58
62
|
def configuration
|
59
63
|
return @configuration if @configuration
|
@@ -62,6 +66,8 @@ module Cucumber
|
|
62
66
|
@configuration.parse!(@args)
|
63
67
|
@configuration
|
64
68
|
end
|
69
|
+
|
70
|
+
private
|
65
71
|
|
66
72
|
def require_files
|
67
73
|
verbose_log("Ruby files required:")
|
@@ -77,19 +83,6 @@ module Cucumber
|
|
77
83
|
verbose_log("\n")
|
78
84
|
end
|
79
85
|
|
80
|
-
def load_plain_text_features
|
81
|
-
features = Ast::Features.new
|
82
|
-
parser = Parser::FeatureParser.new
|
83
|
-
|
84
|
-
verbose_log("Features:")
|
85
|
-
configuration.feature_files.each do |f|
|
86
|
-
features.add_feature(parser.parse_file(f))
|
87
|
-
verbose_log(" * #{f}")
|
88
|
-
end
|
89
|
-
verbose_log("\n"*2)
|
90
|
-
features
|
91
|
-
end
|
92
|
-
|
93
86
|
def verbose_log(string)
|
94
87
|
@out_stream.puts(string) if configuration.verbose?
|
95
88
|
end
|
data/lib/cucumber/formatter.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
%w{color_io pretty progress profile rerun html}.each{|n| require "cucumber/formatter/#{n}"}
|
1
|
+
%w{color_io pretty progress profile rerun html usage}.each{|n| require "cucumber/formatter/#{n}"}
|
@@ -83,37 +83,73 @@ module Cucumber
|
|
83
83
|
@builder.h4("#{keyword} #{name}")
|
84
84
|
end
|
85
85
|
|
86
|
-
def visit_steps(
|
86
|
+
def visit_steps(steps)
|
87
87
|
@builder.ol do
|
88
88
|
super
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
def visit_step(step)
|
93
|
+
@step_id = step.dom_id
|
94
|
+
@builder.li(:id => @step_id) do
|
95
|
+
super
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
92
99
|
def visit_step_name(keyword, step_match, status, source_indent, background)
|
93
|
-
|
94
|
-
@
|
100
|
+
@step_matches ||= []
|
101
|
+
@skip_step = @step_matches.index(step_match)
|
102
|
+
@step_matches << step_match
|
103
|
+
|
104
|
+
unless @skip_step
|
105
|
+
step_name = step_match.format_args(lambda{|param| "<span>#{param}</span>"})
|
106
|
+
@builder.div(:class => status) do |div|
|
107
|
+
div << "#{keyword} #{step_name}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def visit_exception(exception, status)
|
113
|
+
@builder.pre(format_exception(exception), :class => status)
|
95
114
|
end
|
96
115
|
|
97
116
|
def visit_multiline_arg(multiline_arg)
|
117
|
+
return if @skip_step
|
98
118
|
if Ast::Table === multiline_arg
|
99
119
|
@builder.table do
|
100
120
|
super
|
101
121
|
end
|
102
122
|
else
|
103
|
-
|
104
|
-
|
105
|
-
|
123
|
+
super
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def visit_py_string(string, status)
|
128
|
+
@builder.pre(:class => status) do |pre|
|
129
|
+
pre << string
|
106
130
|
end
|
107
131
|
end
|
108
132
|
|
109
133
|
def visit_table_row(table_row)
|
110
|
-
@
|
134
|
+
@row_id = table_row.dom_id
|
135
|
+
@col_index = 0
|
136
|
+
@builder.tr(:id => @row_id) do
|
111
137
|
super
|
112
138
|
end
|
139
|
+
if table_row.exception
|
140
|
+
@builder.tr do
|
141
|
+
@builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
142
|
+
@builder.pre do |pre|
|
143
|
+
pre << format_exception(table_row.exception)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
113
148
|
end
|
114
149
|
|
115
150
|
def visit_table_cell_value(value, width, status)
|
116
|
-
@builder.td(value, :class => status)
|
151
|
+
@builder.td(value, :class => status, :id => "#{@row_id}_#{@col_index}")
|
152
|
+
@col_index += 1
|
117
153
|
end
|
118
154
|
|
119
155
|
def announce(announcement)
|
@@ -128,6 +164,9 @@ module Cucumber
|
|
128
164
|
end
|
129
165
|
end
|
130
166
|
|
167
|
+
def format_exception(exception)
|
168
|
+
(["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n")
|
169
|
+
end
|
131
170
|
end
|
132
171
|
end
|
133
172
|
end
|
@@ -121,16 +121,15 @@ module Cucumber
|
|
121
121
|
|
122
122
|
def visit_step_name(keyword, step_match, status, source_indent, background)
|
123
123
|
@step_matches ||= []
|
124
|
-
|
125
124
|
non_failed_background_step_outside_background = !@in_background && background && (status != :failed)
|
126
125
|
@skip_step = @step_matches.index(step_match) || non_failed_background_step_outside_background
|
126
|
+
@step_matches << step_match
|
127
127
|
|
128
128
|
unless(@skip_step)
|
129
129
|
source_indent = nil unless @options[:source]
|
130
130
|
formatted_step_name = format_step(keyword, step_match, status, source_indent)
|
131
131
|
@io.puts(" " + formatted_step_name)
|
132
132
|
end
|
133
|
-
@step_matches << step_match
|
134
133
|
end
|
135
134
|
|
136
135
|
def visit_multiline_arg(multiline_arg)
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module Cucumber
|
2
2
|
module Formatter
|
3
|
+
# This formatter keeps track of all failing features and print out their location.
|
4
|
+
# Example:
|
5
|
+
#
|
6
|
+
# features/foo.feature:34 features/bar.feature:11:76:81
|
7
|
+
#
|
8
|
+
# This formatter is used by AutoTest - it will use the output to decide what
|
9
|
+
# to run the next time, simply passing the output string on the command line.
|
10
|
+
#
|
3
11
|
class Rerun < Ast::Visitor
|
4
12
|
def initialize(step_mother, io, options)
|
5
13
|
super(step_mother)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'cucumber/formatter/progress'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module Formatter
|
5
|
+
class Usage < Ast::Visitor
|
6
|
+
include Console
|
7
|
+
|
8
|
+
def initialize(step_mother, io, options)
|
9
|
+
super(step_mother)
|
10
|
+
@io = io
|
11
|
+
@options = options
|
12
|
+
@step_definitions = Hash.new { |h,step_definition| h[step_definition] = [] }
|
13
|
+
@locations = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_features(features)
|
17
|
+
super
|
18
|
+
print_summary
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_step(step)
|
22
|
+
@step = step
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_step_name(keyword, step_match, status, source_indent, background)
|
27
|
+
if step_match.step_definition
|
28
|
+
location = @step.file_colon_line
|
29
|
+
return if @locations.index(location)
|
30
|
+
@locations << location
|
31
|
+
|
32
|
+
description = format_step(keyword, step_match, status, nil)
|
33
|
+
length = (keyword + step_match.format_args).jlength
|
34
|
+
@step_definitions[step_match.step_definition] << [step_match, description, length, location]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def print_summary
|
39
|
+
sorted_defs = @step_definitions.keys.sort_by{|step_definition| step_definition.backtrace_line}
|
40
|
+
|
41
|
+
sorted_defs.each do |step_definition|
|
42
|
+
step_matches_and_descriptions = @step_definitions[step_definition].sort_by do |step_match_and_description|
|
43
|
+
step_match = step_match_and_description[0]
|
44
|
+
step_match.step_definition.regexp.inspect
|
45
|
+
end
|
46
|
+
|
47
|
+
step_matches = step_matches_and_descriptions.map{|step_match_and_description| step_match_and_description[0]}
|
48
|
+
|
49
|
+
lengths = step_matches_and_descriptions.map do |step_match_and_description|
|
50
|
+
step_match_and_description[2]
|
51
|
+
end
|
52
|
+
lengths << step_definition.text_length
|
53
|
+
max_length = lengths.max
|
54
|
+
|
55
|
+
@io.print step_definition.regexp.inspect
|
56
|
+
@io.puts format_string(" # #{step_definition.file_colon_line}".indent(max_length - step_definition.text_length), :comment)
|
57
|
+
step_matches_and_descriptions.each do |step_match_and_description|
|
58
|
+
step_match = step_match_and_description[0]
|
59
|
+
description = step_match_and_description[1]
|
60
|
+
length = step_match_and_description[2]
|
61
|
+
file_colon_line = step_match_and_description[3]
|
62
|
+
@io.print " #{description}"
|
63
|
+
@io.puts format_string(" # #{file_colon_line}".indent(max_length - length), :comment)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/cucumber/jbehave.rb
CHANGED
data/lib/cucumber/rails/world.rb
CHANGED
@@ -9,22 +9,10 @@ else
|
|
9
9
|
end
|
10
10
|
require 'test/unit/testresult'
|
11
11
|
|
12
|
-
# These allow exceptions to come through as opposed to being caught and having non-helpful responses returned.
|
13
|
-
ActionController::Base.class_eval do
|
14
|
-
def rescue_action(exception)
|
15
|
-
raise exception
|
16
|
-
end
|
17
|
-
end
|
18
|
-
ActionController::Dispatcher.class_eval do
|
19
|
-
def self.failsafe_response(output, status, exception = nil)
|
20
|
-
raise exception
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
12
|
# So that Test::Unit doesn't launch at the end - makes it think it has already been run.
|
25
13
|
Test::Unit.run = true if Test::Unit.respond_to?(:run=)
|
26
14
|
|
27
|
-
$
|
15
|
+
$__cucumber_toplevel = self
|
28
16
|
|
29
17
|
module Cucumber #:nodoc:
|
30
18
|
module Rails
|
@@ -44,20 +32,21 @@ module Cucumber #:nodoc:
|
|
44
32
|
def self.use_transactional_fixtures
|
45
33
|
World.use_transactional_fixtures = true
|
46
34
|
if defined?(ActiveRecord::Base)
|
47
|
-
$
|
48
|
-
|
49
|
-
|
35
|
+
$__cucumber_toplevel.Before do
|
36
|
+
@__cucumber_ar_connection = ActiveRecord::Base.connection
|
37
|
+
if @__cucumber_ar_connection.respond_to?(:increment_open_transactions)
|
38
|
+
@__cucumber_ar_connection.increment_open_transactions
|
50
39
|
else
|
51
40
|
ActiveRecord::Base.__send__(:increment_open_transactions)
|
52
41
|
end
|
53
|
-
|
42
|
+
@__cucumber_ar_connection.begin_db_transaction
|
54
43
|
ActionMailer::Base.deliveries = [] if defined?(ActionMailer::Base)
|
55
44
|
end
|
56
45
|
|
57
|
-
$
|
58
|
-
|
59
|
-
if
|
60
|
-
|
46
|
+
$__cucumber_toplevel.After do
|
47
|
+
@__cucumber_ar_connection.rollback_db_transaction
|
48
|
+
if @__cucumber_ar_connection.respond_to?(:decrement_open_transactions)
|
49
|
+
@__cucumber_ar_connection.decrement_open_transactions
|
61
50
|
else
|
62
51
|
ActiveRecord::Base.__send__(:decrement_open_transactions)
|
63
52
|
end
|
@@ -65,6 +54,18 @@ module Cucumber #:nodoc:
|
|
65
54
|
end
|
66
55
|
end
|
67
56
|
|
57
|
+
def self.bypass_rescue
|
58
|
+
ActionController::Base.class_eval do
|
59
|
+
def rescue_action(exception)
|
60
|
+
raise exception
|
61
|
+
end
|
62
|
+
end
|
63
|
+
ActionController::Dispatcher.class_eval do
|
64
|
+
def self.failsafe_response(output, status, exception = nil)
|
65
|
+
raise exception
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
@@ -3,6 +3,50 @@ require 'cucumber/core_ext/string'
|
|
3
3
|
require 'cucumber/core_ext/proc'
|
4
4
|
|
5
5
|
module Cucumber
|
6
|
+
module StepDefinitionMethods
|
7
|
+
def step_match(name_to_match, name_to_report)
|
8
|
+
if(match = name_to_match.match(regexp))
|
9
|
+
StepMatch.new(self, name_to_match, name_to_report, match.captures)
|
10
|
+
else
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Formats the matched arguments of the associated Step. This method
|
16
|
+
# is usually called from visitors, which render output.
|
17
|
+
#
|
18
|
+
# The +format+ can either be a String or a Proc.
|
19
|
+
#
|
20
|
+
# If it is a String it should be a format string according to
|
21
|
+
# <tt>Kernel#sprinf</tt>, for example:
|
22
|
+
#
|
23
|
+
# '<span class="param">%s</span></tt>'
|
24
|
+
#
|
25
|
+
# If it is a Proc, it should take one argument and return the formatted
|
26
|
+
# argument, for example:
|
27
|
+
#
|
28
|
+
# lambda { |param| "[#{param}]" }
|
29
|
+
#
|
30
|
+
def format_args(step_name, format)
|
31
|
+
step_name.gzub(regexp, format)
|
32
|
+
end
|
33
|
+
|
34
|
+
def match(step_name)
|
35
|
+
case step_name
|
36
|
+
when String then regexp.match(step_name)
|
37
|
+
when Regexp then regexp == step_name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def backtrace_line
|
42
|
+
"#{file_colon_line}:in `#{regexp.inspect}'"
|
43
|
+
end
|
44
|
+
|
45
|
+
def text_length
|
46
|
+
regexp.inspect.jlength
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
6
50
|
# A Step Definition holds a Regexp and a Proc, and is created
|
7
51
|
# by calling <tt>Given</tt>, <tt>When</tt> or <tt>Then</tt>
|
8
52
|
# in the <tt>step_definitions</tt> ruby files - for example:
|
@@ -14,7 +58,22 @@ module Cucumber
|
|
14
58
|
class StepDefinition
|
15
59
|
def self.snippet_text(step_keyword, step_name)
|
16
60
|
escaped = Regexp.escape(step_name).gsub('\ ', ' ').gsub('/', '\/')
|
17
|
-
|
61
|
+
param_pattern = /"([^\"]*)"/
|
62
|
+
|
63
|
+
match = escaped.match(param_pattern)
|
64
|
+
if match
|
65
|
+
n = 0
|
66
|
+
block_args = match.captures.map do |a|
|
67
|
+
n += 1
|
68
|
+
"arg#{n}"
|
69
|
+
end
|
70
|
+
block_arg_string = " |#{block_args.join(", ")}|"
|
71
|
+
else
|
72
|
+
block_arg_string = ""
|
73
|
+
end
|
74
|
+
|
75
|
+
escaped = escaped.gsub(param_pattern, '"([^\\"]*)"')
|
76
|
+
"#{step_keyword} /^#{escaped}$/ do#{block_arg_string}\n pending\nend"
|
18
77
|
end
|
19
78
|
|
20
79
|
class MissingProc < StandardError
|
@@ -23,7 +82,7 @@ module Cucumber
|
|
23
82
|
end
|
24
83
|
end
|
25
84
|
|
26
|
-
|
85
|
+
include StepDefinitionMethods
|
27
86
|
|
28
87
|
def initialize(pattern, &proc)
|
29
88
|
raise MissingProc if proc.nil?
|
@@ -34,70 +93,22 @@ module Cucumber
|
|
34
93
|
@regexp, @proc = pattern, proc
|
35
94
|
end
|
36
95
|
|
37
|
-
def
|
38
|
-
|
39
|
-
StepMatch.new(self, name_to_match, name_to_report, match.captures)
|
40
|
-
else
|
41
|
-
nil
|
42
|
-
end
|
96
|
+
def regexp
|
97
|
+
@regexp
|
43
98
|
end
|
44
99
|
|
45
|
-
def invoke(world, args
|
100
|
+
def invoke(world, args)
|
46
101
|
args = args.map{|arg| Ast::PyString === arg ? arg.to_s : arg}
|
47
102
|
begin
|
48
|
-
world.cucumber_instance_exec(true,
|
103
|
+
world.cucumber_instance_exec(true, regexp.inspect, *args, &@proc)
|
49
104
|
rescue Cucumber::ArityMismatchError => e
|
50
105
|
e.backtrace.unshift(self.backtrace_line)
|
51
106
|
raise e
|
52
107
|
end
|
53
108
|
end
|
54
109
|
|
55
|
-
#:stopdoc:
|
56
|
-
|
57
|
-
def match(step_name)
|
58
|
-
case step_name
|
59
|
-
when String then @regexp.match(step_name)
|
60
|
-
when Regexp then @regexp == step_name
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Formats the matched arguments of the associated Step. This method
|
65
|
-
# is usually called from visitors, which render output.
|
66
|
-
#
|
67
|
-
# The +format+ either be a String or a Proc.
|
68
|
-
#
|
69
|
-
# If it is a String it should be a format string according to
|
70
|
-
# <tt>Kernel#sprinf</tt>, for example:
|
71
|
-
#
|
72
|
-
# '<span class="param">%s</span></tt>'
|
73
|
-
#
|
74
|
-
# If it is a Proc, it should take one argument and return the formatted
|
75
|
-
# argument, for example:
|
76
|
-
#
|
77
|
-
# lambda { |param| "[#{param}]" }
|
78
|
-
#
|
79
|
-
def format_args(step_name, format)
|
80
|
-
step_name.gzub(@regexp, format)
|
81
|
-
end
|
82
|
-
|
83
|
-
def matched_args(step_name)
|
84
|
-
step_name.match(@regexp).captures
|
85
|
-
end
|
86
|
-
|
87
|
-
def backtrace_line
|
88
|
-
"#{file_colon_line}:in `#{@regexp.inspect}'"
|
89
|
-
end
|
90
|
-
|
91
110
|
def file_colon_line
|
92
111
|
@proc.file_colon_line
|
93
112
|
end
|
94
|
-
|
95
|
-
def text_length
|
96
|
-
@regexp.inspect.jlength
|
97
|
-
end
|
98
|
-
|
99
|
-
def to_s(indent = 0)
|
100
|
-
@regexp.inspect + (' # ').indent(indent) + file_colon_line
|
101
|
-
end
|
102
113
|
end
|
103
114
|
end
|