cucumber 2.0.2 → 2.1.0
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.
- 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])
|