cucumber 2.0.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -6
- data/CONTRIBUTING.md +3 -1
- data/Gemfile +1 -1
- data/History.md +17 -0
- data/README.md +3 -3
- data/bin/cucumber +1 -2
- data/cucumber.gemspec +2 -2
- data/examples/i18n/ht/features/adisyon.feature +7 -7
- data/features/docs/api/listen_for_events.feature +58 -0
- data/features/docs/cli/fail_fast.feature +46 -0
- data/features/docs/defining_steps/nested_steps_with_second_arg.feature +3 -22
- data/features/docs/extending_cucumber/custom_formatter.feature +40 -4
- data/features/docs/gherkin/doc_strings.feature +5 -5
- data/features/docs/gherkin/language_help.feature +15 -15
- data/features/docs/gherkin/using_descriptions.feature +0 -5
- data/lib/cucumber/cli/configuration.rb +10 -92
- data/lib/cucumber/cli/main.rb +1 -7
- data/lib/cucumber/cli/options.rb +47 -12
- data/lib/cucumber/configuration.rb +195 -7
- data/lib/cucumber/events.rb +20 -0
- data/lib/cucumber/events/after_test_case.rb +25 -0
- data/lib/cucumber/events/after_test_step.rb +30 -0
- data/lib/cucumber/events/before_test_case.rb +18 -0
- data/lib/cucumber/events/before_test_step.rb +23 -0
- data/lib/cucumber/events/bus.rb +86 -0
- data/lib/cucumber/events/step_match.rb +23 -0
- data/lib/cucumber/filters/prepare_world.rb +2 -2
- data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
- data/lib/cucumber/formatter/console.rb +1 -1
- data/lib/cucumber/formatter/event_bus_report.rb +37 -0
- data/lib/cucumber/formatter/fail_fast.rb +18 -0
- data/lib/cucumber/formatter/html.rb +1 -1
- data/lib/cucumber/formatter/io.rb +3 -1
- data/lib/cucumber/formatter/json.rb +19 -1
- data/lib/cucumber/formatter/legacy_api/adapter.rb +5 -13
- data/lib/cucumber/formatter/legacy_api/ast.rb +2 -2
- data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +3 -1
- data/lib/cucumber/formatter/pretty.rb +5 -7
- data/lib/cucumber/formatter/progress.rb +1 -1
- data/lib/cucumber/formatter/rerun.rb +1 -1
- data/lib/cucumber/formatter/steps.rb +1 -1
- data/lib/cucumber/formatter/usage.rb +12 -8
- data/lib/cucumber/gherkin/data_table_parser.rb +23 -0
- data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +99 -0
- data/lib/cucumber/gherkin/formatter/argument.rb +17 -0
- data/lib/cucumber/gherkin/formatter/escaping.rb +17 -0
- data/lib/cucumber/gherkin/formatter/hashable.rb +27 -0
- data/lib/cucumber/gherkin/i18n.rb +15 -0
- data/lib/cucumber/gherkin/steps_parser.rb +41 -0
- data/lib/cucumber/language_support/language_methods.rb +6 -5
- data/lib/cucumber/multiline_argument.rb +0 -3
- data/lib/cucumber/multiline_argument/data_table.rb +6 -5
- data/lib/cucumber/multiline_argument/doc_string.rb +1 -2
- data/lib/cucumber/platform.rb +1 -1
- data/lib/cucumber/rake/task.rb +2 -2
- data/lib/cucumber/rb_support/rb_hook.rb +1 -6
- data/lib/cucumber/rb_support/rb_language.rb +15 -5
- data/lib/cucumber/rb_support/rb_step_definition.rb +11 -17
- data/lib/cucumber/rb_support/rb_world.rb +6 -4
- data/lib/cucumber/rb_support/regexp_argument_matcher.rb +2 -2
- data/lib/cucumber/runtime.rb +36 -16
- data/lib/cucumber/runtime/support_code.rb +19 -15
- data/lib/cucumber/step_definition_light.rb +5 -5
- data/lib/cucumber/step_definitions.rb +2 -2
- data/lib/cucumber/step_match.rb +11 -2
- data/lib/cucumber/wire_support/wire_protocol/requests.rb +2 -2
- data/lib/cucumber/wire_support/wire_step_definition.rb +4 -2
- data/{spec → lib}/simplecov_setup.rb +0 -0
- data/spec/cucumber/cli/configuration_spec.rb +2 -104
- data/spec/cucumber/cli/main_spec.rb +0 -22
- data/spec/cucumber/cli/options_spec.rb +3 -1
- data/spec/cucumber/configuration_spec.rb +123 -0
- data/spec/cucumber/events/bus_spec.rb +94 -0
- data/spec/cucumber/formatter/event_bus_report_spec.rb +79 -0
- data/spec/cucumber/formatter/fail_fast_spec.rb +88 -0
- data/spec/cucumber/formatter/json_spec.rb +43 -1
- data/spec/cucumber/formatter/rerun_spec.rb +4 -20
- data/spec/cucumber/rb_support/rb_step_definition_spec.rb +29 -0
- data/spec/cucumber/runtime_spec.rb +2 -28
- data/spec/spec_helper.rb +1 -1
- data/spec/support/standard_step_actions.rb +18 -0
- metadata +37 -13
- data/lib/cucumber/core_ext/proc.rb +0 -36
- data/spec/cucumber/core_ext/proc_spec.rb +0 -69
@@ -0,0 +1,20 @@
|
|
1
|
+
module Cucumber
|
2
|
+
|
3
|
+
# Events tell you what's happening while Cucumber runs your features.
|
4
|
+
#
|
5
|
+
# They're designed to be read-only, appropriate for writing formatters and other
|
6
|
+
# output tools. If you need to be able to influence the result of a scenario, use a hook instead.
|
7
|
+
#
|
8
|
+
# To subscribe to an event, use {Cucumber::Configuration#on_event}
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# AfterConfiguration do |config|
|
12
|
+
# config.on_event :after_test_step do |event|
|
13
|
+
# puts event.result
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
module Events
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Dir[File.dirname(__FILE__) + '/events/*.rb'].map(&method(:require))
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Events
|
3
|
+
|
4
|
+
# Event fired after a test case has finished executing
|
5
|
+
class AfterTestCase
|
6
|
+
|
7
|
+
# The test case that was just executed.
|
8
|
+
#
|
9
|
+
# @return [Cucumber::Core::Test::Case]
|
10
|
+
attr_reader :test_case
|
11
|
+
|
12
|
+
# The result of executing the test case.
|
13
|
+
#
|
14
|
+
# @return [Cucumber::Core::Test::Result]
|
15
|
+
attr_reader :result
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(test_case, result)
|
19
|
+
@test_case, @result = test_case, result
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Events
|
3
|
+
|
4
|
+
# Event fired after each test step has been executed
|
5
|
+
class AfterTestStep
|
6
|
+
|
7
|
+
# The test case currently being executed.
|
8
|
+
#
|
9
|
+
# @return [Cucumber::Core::Test::Case]
|
10
|
+
attr_reader :test_case
|
11
|
+
|
12
|
+
# The test step that was just executed.
|
13
|
+
#
|
14
|
+
# @return [Cucumber::Core::Test::Step]
|
15
|
+
attr_reader :test_step
|
16
|
+
|
17
|
+
# The result of executing the test step.
|
18
|
+
#
|
19
|
+
# @return [Cucumber::Core::Test::Result]
|
20
|
+
attr_reader :result
|
21
|
+
|
22
|
+
# @private
|
23
|
+
def initialize(test_case, test_step, result)
|
24
|
+
@test_case, @test_step, @result = test_case, test_step, result
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Events
|
3
|
+
|
4
|
+
# Event fired before a test case is executed
|
5
|
+
class BeforeTestCase
|
6
|
+
|
7
|
+
# The test case about to be executed.
|
8
|
+
#
|
9
|
+
# @return [Cucumber::Core::Test::Case]
|
10
|
+
attr_reader :test_case
|
11
|
+
|
12
|
+
# @private
|
13
|
+
def initialize(test_case)
|
14
|
+
@test_case = test_case
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Events
|
3
|
+
|
4
|
+
# Event fired before a test step is executed
|
5
|
+
class BeforeTestStep
|
6
|
+
|
7
|
+
# The test case currently being executed.
|
8
|
+
#
|
9
|
+
# @return [Cucumber::Core::Test::Case]
|
10
|
+
attr_reader :test_case
|
11
|
+
|
12
|
+
# The test step about to be executed.
|
13
|
+
#
|
14
|
+
# @return [Cucumber::Core::Test::Step]
|
15
|
+
attr_reader :test_step
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(test_case, test_step)
|
19
|
+
@test_case, @test_step = test_case, test_step
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Events
|
3
|
+
|
4
|
+
# Event bus
|
5
|
+
#
|
6
|
+
# Implements and in-process pub-sub events broadcaster allowing multiple observers
|
7
|
+
# to subscribe to different events that fire as your tests are executed.
|
8
|
+
#
|
9
|
+
# @private
|
10
|
+
class Bus
|
11
|
+
|
12
|
+
def initialize(default_namespace)
|
13
|
+
@default_namespace = default_namespace.to_s
|
14
|
+
@handlers = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Register for an event
|
18
|
+
def register(event_id, handler_object = nil, &handler_proc)
|
19
|
+
handler = handler_proc || handler_object
|
20
|
+
raise ArgumentError.new("Please pass either an object or a handler block") unless handler
|
21
|
+
event_class = parse_event_id(event_id)
|
22
|
+
handlers_for(event_class) << handler
|
23
|
+
end
|
24
|
+
|
25
|
+
# Broadcast an event
|
26
|
+
def notify(event)
|
27
|
+
handlers_for(event.class).each { |handler| handler.call(event) }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def handlers_for(event_class)
|
33
|
+
@handlers[event_class.to_s] ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_event_id(event_id)
|
37
|
+
case event_id
|
38
|
+
when Class
|
39
|
+
return event_id
|
40
|
+
when String
|
41
|
+
constantize(event_id)
|
42
|
+
else
|
43
|
+
constantize("#{@default_namespace}::#{camel_case(event_id)}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def camel_case(underscored_name)
|
48
|
+
underscored_name.to_s.split("_").map { |word| word.upcase[0] + word[1..-1] }.join
|
49
|
+
end
|
50
|
+
|
51
|
+
# Thanks ActiveSupport
|
52
|
+
# (Only needed to support Ruby 1.9.3 and JRuby)
|
53
|
+
def constantize(camel_cased_word)
|
54
|
+
names = camel_cased_word.split('::')
|
55
|
+
|
56
|
+
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
57
|
+
Object.const_get(camel_cased_word) if names.empty?
|
58
|
+
|
59
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
60
|
+
names.shift if names.size > 1 && names.first.empty?
|
61
|
+
|
62
|
+
names.inject(Object) do |constant, name|
|
63
|
+
if constant == Object
|
64
|
+
constant.const_get(name)
|
65
|
+
else
|
66
|
+
candidate = constant.const_get(name)
|
67
|
+
next candidate if constant.const_defined?(name, false)
|
68
|
+
next candidate unless Object.const_defined?(name)
|
69
|
+
|
70
|
+
# Go down the ancestors to check if it is owned directly. The check
|
71
|
+
# stops when we reach Object or the end of ancestors tree.
|
72
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
73
|
+
break const if ancestor == Object
|
74
|
+
break ancestor if ancestor.const_defined?(name, false)
|
75
|
+
const
|
76
|
+
end
|
77
|
+
|
78
|
+
# owner is in Object, so raise
|
79
|
+
constant.const_get(name, false)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Events
|
3
|
+
|
4
|
+
# Event fired when a step is matched to a definition
|
5
|
+
class StepMatch
|
6
|
+
|
7
|
+
# The test step that was matched.
|
8
|
+
#
|
9
|
+
# @return [Cucumber::Core::Test::Step]
|
10
|
+
attr_reader :test_step
|
11
|
+
|
12
|
+
# Information about the matching definition.
|
13
|
+
#
|
14
|
+
# @return [Cucumber::StepMatch]
|
15
|
+
attr_reader :step_match
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(test_step, step_match)
|
19
|
+
@test_step, @step_match = test_step, step_match
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -25,8 +25,8 @@ module Cucumber
|
|
25
25
|
around_hooks = [init_scenario] + @original_test_case.around_hooks
|
26
26
|
|
27
27
|
empty_hook = proc {} #no op - legacy format adapter expects a before hooks
|
28
|
-
|
29
|
-
default_hook = Cucumber::Hooks.before_hook(@original_test_case.source,
|
28
|
+
empty_hook_location = Cucumber::Core::Ast::Location.from_source_location(*empty_hook.source_location)
|
29
|
+
default_hook = Cucumber::Hooks.before_hook(@original_test_case.source, empty_hook_location, &empty_hook)
|
30
30
|
steps = [default_hook] + @original_test_case.test_steps
|
31
31
|
|
32
32
|
@original_test_case.with_around_hooks(around_hooks).with_steps(steps)
|
@@ -3,22 +3,23 @@ require 'cucumber/platform'
|
|
3
3
|
|
4
4
|
module Cucumber
|
5
5
|
module Formatter
|
6
|
-
|
7
|
-
class BacktraceFilter
|
8
|
-
BACKTRACE_FILTER_PATTERNS = \
|
6
|
+
BACKTRACE_FILTER_PATTERNS = \
|
9
7
|
[/vendor\/rails|lib\/cucumber|bin\/cucumber:|lib\/rspec|gems\/|minitest|test\/unit|.gem\/ruby|lib\/ruby/]
|
10
|
-
if(::Cucumber::JRUBY)
|
11
|
-
BACKTRACE_FILTER_PATTERNS << /org\/jruby/
|
12
|
-
end
|
13
|
-
PWD_PATTERN = /#{::Regexp.escape(::Dir.pwd)}\//m
|
14
8
|
|
9
|
+
if(::Cucumber::JRUBY)
|
10
|
+
BACKTRACE_FILTER_PATTERNS << /org\/jruby/
|
11
|
+
end
|
12
|
+
|
13
|
+
class BacktraceFilter
|
15
14
|
def initialize(exception)
|
16
15
|
@exception = exception
|
17
16
|
end
|
18
17
|
|
19
18
|
def exception
|
20
19
|
return @exception if ::Cucumber.use_full_backtrace
|
21
|
-
|
20
|
+
|
21
|
+
pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m
|
22
|
+
@exception.backtrace.each { |line| line.gsub!(pwd_pattern, "./") }
|
22
23
|
|
23
24
|
filtered = (@exception.backtrace || []).reject do |line|
|
24
25
|
BACKTRACE_FILTER_PATTERNS.detect { |p| line =~ p }
|
@@ -32,7 +32,7 @@ module Cucumber
|
|
32
32
|
|
33
33
|
def format_step(keyword, step_match, status, source_indent)
|
34
34
|
comment = if source_indent
|
35
|
-
c = ('# ' + step_match.
|
35
|
+
c = ('# ' + step_match.location.to_s).indent(source_indent)
|
36
36
|
format_string(c, :comment)
|
37
37
|
else
|
38
38
|
''
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Formatter
|
3
|
+
|
4
|
+
# Adapter between Cucumber::Core::Test::Runner's Report API and
|
5
|
+
# Cucumber's event bus
|
6
|
+
class EventBusReport
|
7
|
+
attr_reader :config
|
8
|
+
private :config
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_test_case(test_case)
|
15
|
+
@config.notify Events::BeforeTestCase.new(test_case)
|
16
|
+
@test_case = test_case
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_test_step(test_step)
|
20
|
+
@config.notify Events::BeforeTestStep.new(@test_case, test_step)
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_test_step(test_step, result)
|
24
|
+
@config.notify Events::AfterTestStep.new(@test_case, test_step, result)
|
25
|
+
end
|
26
|
+
|
27
|
+
def after_test_case(test_case, result)
|
28
|
+
@config.notify Events::AfterTestCase.new(test_case, result)
|
29
|
+
end
|
30
|
+
|
31
|
+
def done
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'cucumber/formatter/io'
|
2
|
+
require 'cucumber/formatter/console'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Formatter
|
6
|
+
|
7
|
+
class FailFast
|
8
|
+
|
9
|
+
def initialize(configuration)
|
10
|
+
configuration.on_event :after_test_case do |event|
|
11
|
+
Cucumber.wants_to_quit = true unless event.result.ok?(configuration.strict?)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module Cucumber
|
2
2
|
module Formatter
|
3
3
|
module Io
|
4
|
-
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def ensure_io(path_or_io)
|
5
7
|
return nil if path_or_io.nil?
|
6
8
|
return path_or_io if path_or_io.respond_to?(:write)
|
7
9
|
file = File.open(path_or_io, Cucumber.file_mode('w'))
|
@@ -11,7 +11,7 @@ module Cucumber
|
|
11
11
|
|
12
12
|
def initialize(runtime, io, _options)
|
13
13
|
@runtime = runtime
|
14
|
-
@io = ensure_io(io
|
14
|
+
@io = ensure_io(io)
|
15
15
|
@feature_hashes = []
|
16
16
|
end
|
17
17
|
|
@@ -29,6 +29,7 @@ module Cucumber
|
|
29
29
|
feature_elements << @test_case_hash
|
30
30
|
@element_hash = @test_case_hash
|
31
31
|
end
|
32
|
+
@any_step_failed = false
|
32
33
|
end
|
33
34
|
|
34
35
|
def before_test_step(test_step)
|
@@ -51,6 +52,11 @@ module Cucumber
|
|
51
52
|
def after_test_step(test_step, result)
|
52
53
|
return if internal_hook?(test_step)
|
53
54
|
add_match_and_result(test_step, result)
|
55
|
+
@any_step_failed = true if result.failed?
|
56
|
+
end
|
57
|
+
|
58
|
+
def after_test_case(test_case, result)
|
59
|
+
add_failed_around_hook(result) if result.failed? && !@any_step_failed
|
54
60
|
end
|
55
61
|
|
56
62
|
def done
|
@@ -123,6 +129,10 @@ module Cucumber
|
|
123
129
|
@element_hash[:after] ||= []
|
124
130
|
end
|
125
131
|
|
132
|
+
def around_hooks
|
133
|
+
@element_hash[:around] ||= []
|
134
|
+
end
|
135
|
+
|
126
136
|
def after_step_hooks
|
127
137
|
@step_hash[:after] ||= []
|
128
138
|
end
|
@@ -159,6 +169,14 @@ module Cucumber
|
|
159
169
|
@step_or_hook_hash[:result] = create_result_hash(result)
|
160
170
|
end
|
161
171
|
|
172
|
+
def add_failed_around_hook(result)
|
173
|
+
@step_or_hook_hash = {}
|
174
|
+
around_hooks << @step_or_hook_hash
|
175
|
+
@step_or_hook_hash[:match] = { location: "unknown_hook_location:1" }
|
176
|
+
|
177
|
+
@step_or_hook_hash[:result] = create_result_hash(result)
|
178
|
+
end
|
179
|
+
|
162
180
|
def create_match_hash(test_step, result)
|
163
181
|
{ location: test_step.action_location }
|
164
182
|
end
|
@@ -207,9 +207,10 @@ module Cucumber
|
|
207
207
|
|
208
208
|
def before
|
209
209
|
formatter.before_feature(node)
|
210
|
-
|
210
|
+
language_comment = node.language.iso_code != 'en' ? ["# language: #{node.language.iso_code}"] : []
|
211
|
+
Ast::Comments.new(language_comment + node.comments).accept(formatter)
|
211
212
|
Ast::Tags.new(node.tags).accept(formatter)
|
212
|
-
formatter.feature_name node.keyword,
|
213
|
+
formatter.feature_name node.keyword, node.legacy_conflated_name_and_description
|
213
214
|
@delayed_messages = []
|
214
215
|
@delayed_embeddings = []
|
215
216
|
self
|
@@ -413,15 +414,6 @@ module Cucumber
|
|
413
414
|
to.class.name == ScenarioOutlinePrinter.name
|
414
415
|
end
|
415
416
|
|
416
|
-
def indented(nasty_old_conflation_of_name_and_description)
|
417
|
-
indent = ""
|
418
|
-
nasty_old_conflation_of_name_and_description.split("\n").map do |l|
|
419
|
-
s = "#{indent}#{l}"
|
420
|
-
indent = " "
|
421
|
-
s
|
422
|
-
end.join("\n")
|
423
|
-
end
|
424
|
-
|
425
417
|
end
|
426
418
|
|
427
419
|
module PrintsAfterHooks
|
@@ -749,9 +741,9 @@ module Cucumber
|
|
749
741
|
max_width.result
|
750
742
|
end
|
751
743
|
|
752
|
-
require 'gherkin/formatter/escaping'
|
744
|
+
require 'cucumber/gherkin/formatter/escaping'
|
753
745
|
FindMaxWidth = Struct.new(:index) do
|
754
|
-
include ::Gherkin::Formatter::Escaping
|
746
|
+
include ::Cucumber::Gherkin::Formatter::Escaping
|
755
747
|
|
756
748
|
def examples_table(table, &descend)
|
757
749
|
@result = char_length_of(table.header.values[index])
|