cucumber 3.2.0 → 4.0.0.rc.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.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +61 -18
  3. data/CONTRIBUTING.md +1 -0
  4. data/bin/cucumber +1 -1
  5. data/lib/autotest/cucumber_mixin.rb +42 -39
  6. data/lib/cucumber/cli/configuration.rb +4 -4
  7. data/lib/cucumber/cli/main.rb +11 -12
  8. data/lib/cucumber/cli/options.rb +56 -69
  9. data/lib/cucumber/cli/profile_loader.rb +32 -20
  10. data/lib/cucumber/configuration.rb +20 -21
  11. data/lib/cucumber/constantize.rb +2 -5
  12. data/lib/cucumber/deprecate.rb +5 -5
  13. data/lib/cucumber/errors.rb +4 -6
  14. data/lib/cucumber/events.rb +1 -0
  15. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  16. data/lib/cucumber/events/step_activated.rb +2 -1
  17. data/lib/cucumber/file_specs.rb +6 -6
  18. data/lib/cucumber/filters/activate_steps.rb +5 -3
  19. data/lib/cucumber/filters/prepare_world.rb +5 -9
  20. data/lib/cucumber/filters/quit.rb +1 -3
  21. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  22. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  23. data/lib/cucumber/formatter/ast_lookup.rb +160 -0
  24. data/lib/cucumber/formatter/backtrace_filter.rb +5 -7
  25. data/lib/cucumber/formatter/console.rb +28 -59
  26. data/lib/cucumber/formatter/console_counts.rb +4 -9
  27. data/lib/cucumber/formatter/console_issues.rb +6 -3
  28. data/lib/cucumber/formatter/duration_extractor.rb +1 -1
  29. data/lib/cucumber/formatter/fanout.rb +2 -0
  30. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  31. data/lib/cucumber/formatter/interceptor.rb +5 -7
  32. data/lib/cucumber/formatter/io.rb +8 -14
  33. data/lib/cucumber/formatter/json.rb +93 -117
  34. data/lib/cucumber/formatter/junit.rb +55 -57
  35. data/lib/cucumber/formatter/pretty.rb +346 -152
  36. data/lib/cucumber/formatter/progress.rb +28 -32
  37. data/lib/cucumber/formatter/rerun.rb +22 -4
  38. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  39. data/lib/cucumber/formatter/steps.rb +2 -3
  40. data/lib/cucumber/formatter/summary.rb +16 -8
  41. data/lib/cucumber/formatter/unicode.rb +15 -17
  42. data/lib/cucumber/formatter/usage.rb +9 -8
  43. data/lib/cucumber/gherkin/data_table_parser.rb +8 -6
  44. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  45. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  46. data/lib/cucumber/gherkin/steps_parser.rb +7 -8
  47. data/lib/cucumber/glue/dsl.rb +1 -1
  48. data/lib/cucumber/glue/hook.rb +16 -9
  49. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  50. data/lib/cucumber/glue/proto_world.rb +14 -16
  51. data/lib/cucumber/glue/registry_and_more.rb +7 -9
  52. data/lib/cucumber/glue/snippet.rb +21 -20
  53. data/lib/cucumber/glue/step_definition.rb +14 -15
  54. data/lib/cucumber/glue/world_factory.rb +1 -1
  55. data/lib/cucumber/hooks.rb +11 -11
  56. data/lib/cucumber/multiline_argument.rb +4 -6
  57. data/lib/cucumber/multiline_argument/data_table.rb +88 -59
  58. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
  59. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  60. data/lib/cucumber/platform.rb +3 -3
  61. data/lib/cucumber/rake/task.rb +13 -16
  62. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  63. data/lib/cucumber/running_test_case.rb +2 -53
  64. data/lib/cucumber/runtime.rb +27 -57
  65. data/lib/cucumber/runtime/after_hooks.rb +3 -3
  66. data/lib/cucumber/runtime/before_hooks.rb +3 -3
  67. data/lib/cucumber/runtime/for_programming_languages.rb +3 -2
  68. data/lib/cucumber/runtime/step_hooks.rb +1 -1
  69. data/lib/cucumber/runtime/support_code.rb +10 -12
  70. data/lib/cucumber/runtime/user_interface.rb +4 -6
  71. data/lib/cucumber/step_definition_light.rb +4 -3
  72. data/lib/cucumber/step_match.rb +12 -11
  73. data/lib/cucumber/step_match_search.rb +2 -1
  74. data/lib/cucumber/term/ansicolor.rb +9 -9
  75. data/lib/cucumber/version +1 -1
  76. metadata +37 -28
  77. data/lib/cucumber/formatter/cucumber.css +0 -286
  78. data/lib/cucumber/formatter/cucumber.sass +0 -247
  79. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  80. data/lib/cucumber/formatter/html.rb +0 -611
  81. data/lib/cucumber/formatter/html_builder.rb +0 -121
  82. data/lib/cucumber/formatter/http_io.rb +0 -146
  83. data/lib/cucumber/formatter/inline-js.js +0 -30
  84. data/lib/cucumber/formatter/jquery-min.js +0 -154
  85. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  86. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  87. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  88. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  89. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  90. data/lib/cucumber/step_argument.rb +0 -25
@@ -9,16 +9,17 @@ module Spec #:nodoc:
9
9
  # will fail when running cucumber)
10
10
  class OptionParser < ::OptionParser #:nodoc:
11
11
  NEUTERED_RSPEC = Object.new
12
- def NEUTERED_RSPEC.method_missing(_m, *_args); self; end
12
+ def NEUTERED_RSPEC.method_missing(_method, *_args) # rubocop:disable Style/MissingRespondToMissing
13
+ self || super
14
+ end
13
15
 
14
- def self.method_added(m)
15
- unless @__neutering_rspec
16
- @__neutering_rspec = true
17
- define_method(m) do |*a|
18
- NEUTERED_RSPEC
19
- end
20
- @__neutering_rspec = false
16
+ def self.method_added(method)
17
+ return if @__neutering_rspec
18
+ @__neutering_rspec = true
19
+ define_method(method) do |*_a|
20
+ NEUTERED_RSPEC
21
21
  end
22
+ @__neutering_rspec = false
22
23
  end
23
24
  end
24
25
  end
@@ -15,45 +15,12 @@ module Cucumber
15
15
  # the passed / failed / undefined / skipped status of
16
16
  # the test case.
17
17
  #
18
- # The test case might come from a regular Scenario or
19
- # a Scenario outline. You can call the `#outline?`
20
- # predicate to find out. If it's from an outline,
21
- # you get a couple of extra methods.
22
18
  module RunningTestCase
23
19
  def self.new(test_case)
24
- Builder.new(test_case).running_test_case
20
+ TestCase.new(test_case)
25
21
  end
26
22
 
27
- class Builder
28
- def initialize(test_case)
29
- @test_case = test_case
30
- test_case.describe_source_to(self)
31
- end
32
-
33
- def feature(feature)
34
- end
35
-
36
- def scenario(_scenario)
37
- @factory = Scenario
38
- end
39
-
40
- def scenario_outline(_scenario)
41
- @factory = ScenarioOutlineExample
42
- end
43
-
44
- def examples_table(examples_table)
45
- end
46
-
47
- def examples_table_row(row)
48
- end
49
-
50
- def running_test_case
51
- @factory.new(@test_case)
52
- end
53
- end
54
- private_constant :Builder
55
-
56
- class Scenario < SimpleDelegator
23
+ class TestCase < SimpleDelegator
57
24
  def initialize(test_case, result = Core::Test::Result::Unknown.new)
58
25
  @test_case = test_case
59
26
  @result = result
@@ -85,27 +52,9 @@ module Cucumber
85
52
  tags.map &:name
86
53
  end
87
54
 
88
- def outline?
89
- false
90
- end
91
-
92
55
  def with_result(result)
93
56
  self.class.new(@test_case, result)
94
57
  end
95
58
  end
96
-
97
- class ScenarioOutlineExample < Scenario
98
- def outline?
99
- true
100
- end
101
-
102
- def scenario_outline
103
- self
104
- end
105
-
106
- def cell_values
107
- source.last.values
108
- end
109
- end
110
59
  end
111
60
  end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  require 'fileutils'
@@ -19,8 +18,8 @@ module Cucumber
19
18
  end
20
19
  end
21
20
 
22
- class FileException < Exception
23
- attr :path
21
+ class FileException < RuntimeError
22
+ attr_reader :path
24
23
 
25
24
  def initialize(original_exception, path)
26
25
  super(original_exception)
@@ -31,7 +30,7 @@ module Cucumber
31
30
  class FileNotFoundException < FileException
32
31
  end
33
32
 
34
- class FeatureFolderNotFoundException < Exception
33
+ class FeatureFolderNotFoundException < RuntimeError
35
34
  def initialize(path)
36
35
  @path = path
37
36
  end
@@ -54,7 +53,6 @@ module Cucumber
54
53
  def initialize(configuration = Configuration.default)
55
54
  @configuration = Configuration.new(configuration)
56
55
  @support_code = SupportCode.new(self, @configuration)
57
- @results = Formatter::LegacyApi::Results.new
58
56
  end
59
57
 
60
58
  # Allows you to take an existing runtime and change its configuration
@@ -72,7 +70,7 @@ module Cucumber
72
70
  self.visitor = report
73
71
 
74
72
  receiver = Test::Runner.new(@configuration.event_bus)
75
- compile features, receiver, filters
73
+ compile features, receiver, filters, @configuration.event_bus
76
74
  @configuration.notify :test_run_finished
77
75
  end
78
76
 
@@ -84,14 +82,6 @@ module Cucumber
84
82
  @configuration.dry_run?
85
83
  end
86
84
 
87
- def scenarios(status = nil)
88
- @results.scenarios(status)
89
- end
90
-
91
- def steps(status = nil)
92
- @results.steps(status)
93
- end
94
-
95
85
  def unmatched_step_definitions
96
86
  @support_code.unmatched_step_definitions
97
87
  end
@@ -107,13 +97,13 @@ module Cucumber
107
97
  # Returns Ast::DocString for +string_without_triple_quotes+.
108
98
  #
109
99
  def doc_string(string_without_triple_quotes, content_type = '', _line_offset = 0)
110
- location = Core::Ast::Location.of_caller
111
- Core::Ast::DocString.new(string_without_triple_quotes, content_type, location)
100
+ location = Core::Test::Location.of_caller
101
+ Core::Test::DocString.new(string_without_triple_quotes, content_type, location)
112
102
  end
113
103
 
114
104
  private
115
105
 
116
- def fire_after_configuration_hook #:nodoc
106
+ def fire_after_configuration_hook #:nodoc:
117
107
  @support_code.fire_hook(:after_configuration, @configuration)
118
108
  end
119
109
 
@@ -143,14 +133,12 @@ module Cucumber
143
133
  end
144
134
 
145
135
  def initialize(path)
146
- begin
147
- @file = File.new(path)
148
- set_encoding
149
- rescue Errno::EACCES => e
150
- raise FileNotFoundException.new(e, File.expand_path(path))
151
- rescue Errno::ENOENT
152
- raise FeatureFolderNotFoundException.new(path)
153
- end
136
+ @file = File.new(path)
137
+ set_encoding
138
+ rescue Errno::EACCES => e
139
+ raise FileNotFoundException.new(e, File.expand_path(path))
140
+ rescue Errno::ENOENT
141
+ raise FeatureFolderNotFoundException, path
154
142
  end
155
143
 
156
144
  def read
@@ -162,7 +150,7 @@ module Cucumber
162
150
  def set_encoding
163
151
  @file.each do |line|
164
152
  if ENCODING_PATTERN =~ line
165
- @file.set_encoding $1
153
+ @file.set_encoding Regexp.last_match(1)
166
154
  break
167
155
  end
168
156
  break unless COMMENT_OR_EMPTY_LINE_PATTERN =~ line
@@ -171,9 +159,6 @@ module Cucumber
171
159
  end
172
160
  end
173
161
 
174
- require 'cucumber/formatter/legacy_api/adapter'
175
- require 'cucumber/formatter/legacy_api/runtime_facade'
176
- require 'cucumber/formatter/legacy_api/results'
177
162
  require 'cucumber/formatter/ignore_missing_messages'
178
163
  require 'cucumber/formatter/fail_fast'
179
164
  require 'cucumber/core/report/summary'
@@ -194,39 +179,26 @@ module Cucumber
194
179
 
195
180
  def formatters
196
181
  @formatters ||=
197
- @configuration.formatter_factories do |factory, formatter_options, path_or_io, options|
198
- create_formatter(factory, formatter_options, path_or_io, options)
182
+ @configuration.formatter_factories do |factory, formatter_options, path_or_io|
183
+ create_formatter(factory, formatter_options, path_or_io)
199
184
  end
200
185
  end
201
186
 
202
- def create_formatter(factory, formatter_options, path_or_io, cli_options)
203
- if !legacy_formatter?(factory)
204
- if accept_options?(factory)
205
- return factory.new(@configuration, formatter_options) if path_or_io.nil?
206
- return factory.new(@configuration.with_options(out_stream: path_or_io),
207
- formatter_options)
208
- else
209
- return factory.new(@configuration) if path_or_io.nil?
210
- return factory.new(@configuration.with_options(out_stream: path_or_io))
211
- end
187
+ def create_formatter(factory, formatter_options, path_or_io)
188
+ if accept_options?(factory)
189
+ return factory.new(@configuration, formatter_options) if path_or_io.nil?
190
+ factory.new(@configuration.with_options(out_stream: path_or_io),
191
+ formatter_options)
192
+ else
193
+ return factory.new(@configuration) if path_or_io.nil?
194
+ factory.new(@configuration.with_options(out_stream: path_or_io))
212
195
  end
213
- results = Formatter::LegacyApi::Results.new
214
- runtime_facade = Formatter::LegacyApi::RuntimeFacade.new(results, @support_code, @configuration)
215
- formatter = factory.new(runtime_facade, path_or_io, cli_options)
216
- Formatter::LegacyApi::Adapter.new(
217
- Formatter::IgnoreMissingMessages.new(formatter),
218
- results, @configuration
219
- )
220
196
  end
221
197
 
222
198
  def accept_options?(factory)
223
199
  factory.instance_method(:initialize).arity > 1
224
200
  end
225
201
 
226
- def legacy_formatter?(factory)
227
- factory.instance_method(:initialize).arity > 2
228
- end
229
-
230
202
  def failure?
231
203
  if @configuration.wip?
232
204
  summary_report.test_cases.total_passed > 0
@@ -237,7 +209,7 @@ module Cucumber
237
209
  public :failure?
238
210
 
239
211
  require 'cucumber/core/test/filters'
240
- def filters
212
+ def filters # rubocop:disable Metrics/AbcSize
241
213
  tag_expressions = @configuration.tag_expressions
242
214
  name_regexps = @configuration.name_regexps
243
215
  tag_limits = @configuration.tag_limits
@@ -250,9 +222,7 @@ module Cucumber
250
222
  # TODO: can we just use Glue::RegistryAndMore's step definitions directly?
251
223
  step_match_search = StepMatchSearch.new(@support_code.registry.method(:step_matches), @configuration)
252
224
  filters << Filters::ActivateSteps.new(step_match_search, @configuration)
253
- @configuration.filters.each do |filter|
254
- filters << filter
255
- end
225
+ @configuration.filters.each { |filter| filters << filter }
256
226
  unless configuration.dry_run?
257
227
  filters << Filters::ApplyAfterStepHooks.new(@support_code)
258
228
  filters << Filters::ApplyBeforeHooks.new(@support_code)
@@ -273,7 +243,7 @@ module Cucumber
273
243
  end
274
244
 
275
245
  def install_wire_plugin
276
- Cucumber::Wire::Plugin.new(@configuration).install if @configuration.all_files_to_load.any? { |f| f =~ %r{\.wire$} }
246
+ Cucumber::Wire::Plugin.new(@configuration, @support_code.registry).install if @configuration.all_files_to_load.any? { |f| f =~ /\.wire$/ }
277
247
  end
278
248
 
279
249
  def log
@@ -10,16 +10,16 @@ module Cucumber
10
10
 
11
11
  def apply_to(test_case)
12
12
  test_case.with_steps(
13
- test_case.test_steps + after_hooks(test_case.source).reverse
13
+ test_case.test_steps + after_hooks.reverse
14
14
  )
15
15
  end
16
16
 
17
17
  private
18
18
 
19
- def after_hooks(source)
19
+ def after_hooks
20
20
  @hooks.map do |hook|
21
21
  action = ->(result) { hook.invoke('After', @scenario.with_result(result)) }
22
- Hooks.after_hook(source, hook.location, &action)
22
+ Hooks.after_hook(hook.location, &action)
23
23
  end
24
24
  end
25
25
  end
@@ -12,16 +12,16 @@ module Cucumber
12
12
 
13
13
  def apply_to(test_case)
14
14
  test_case.with_steps(
15
- before_hooks(test_case.source) + test_case.test_steps
15
+ before_hooks + test_case.test_steps
16
16
  )
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def before_hooks(source)
21
+ def before_hooks
22
22
  @hooks.map do |hook|
23
23
  action_block = ->(result) { hook.invoke('Before', @scenario.with_result(result)) }
24
- Hooks.before_hook(source, hook.location, &action_block)
24
+ Hooks.before_hook(hook.location, &action_block)
25
25
  end
26
26
  end
27
27
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require 'cucumber/core/ast/doc_string'
4
+ require 'cucumber/core/test/doc_string'
5
5
 
6
6
  module Cucumber
7
7
  class Runtime
@@ -15,7 +15,8 @@ module Cucumber
15
15
  attr_reader :support_code
16
16
 
17
17
  def initialize(support_code, user_interface)
18
- @support_code, @user_interface = support_code, user_interface
18
+ @support_code = support_code
19
+ @user_interface = user_interface
19
20
  end
20
21
 
21
22
  def_delegators :@user_interface,
@@ -18,7 +18,7 @@ module Cucumber
18
18
  def after_step_hooks(test_step)
19
19
  @hooks.map do |hook|
20
20
  action = ->(*args) { hook.invoke('AfterStep', [args, test_step]) }
21
- Hooks.after_step_hook(test_step.source, hook.location, &action)
21
+ Hooks.after_step_hook(test_step, hook.location, &action)
22
22
  end
23
23
  end
24
24
  end
@@ -22,19 +22,15 @@ module Cucumber
22
22
  end
23
23
 
24
24
  def step(step)
25
- location = Core::Ast::Location.of_caller
25
+ location = Core::Test::Location.of_caller
26
26
  @support_code.invoke_dynamic_step(step[:text], multiline_arg(step, location))
27
27
  end
28
28
 
29
29
  def multiline_arg(step, location)
30
- argument = step[:argument]
31
-
32
- if argument
33
- if argument[:type] == :DocString
34
- MultilineArgument.from(argument[:content], location, argument[:content_type])
35
- else
36
- MultilineArgument::DataTable.from(argument[:rows].map { |row| row[:cells].map { |cell| cell[:value] } })
37
- end
30
+ if !step[:doc_string].nil?
31
+ MultilineArgument.from(step[:doc_string][:content], location, step[:doc_string][:content_type])
32
+ elsif !step[:data_table].nil?
33
+ MultilineArgument::DataTable.from(step[:data_table][:rows].map { |row| row[:cells].map { |cell| cell[:value] } })
38
34
  else
39
35
  MultilineArgument.from(nil)
40
36
  end
@@ -62,8 +58,8 @@ module Cucumber
62
58
  # Given I have 8 cukes in my belly
63
59
  # Then I should not be thirsty
64
60
  # })
65
- def invoke_dynamic_steps(steps_text, i18n, _location)
66
- parser = Cucumber::Gherkin::StepsParser.new(StepInvoker.new(self), i18n.iso_code)
61
+ def invoke_dynamic_steps(steps_text, iso_code, _location)
62
+ parser = Cucumber::Gherkin::StepsParser.new(StepInvoker.new(self), iso_code)
67
63
  parser.parse(steps_text)
68
64
  end
69
65
 
@@ -112,12 +108,14 @@ module Cucumber
112
108
  end
113
109
 
114
110
  def apply_before_hooks(test_case)
111
+ return test_case if test_case.test_steps.empty?
115
112
  scenario = RunningTestCase.new(test_case)
116
113
  hooks = registry.hooks_for(:before, scenario)
117
114
  BeforeHooks.new(hooks, scenario).apply_to(test_case)
118
115
  end
119
116
 
120
117
  def apply_after_hooks(test_case)
118
+ return test_case if test_case.test_steps.empty?
121
119
  scenario = RunningTestCase.new(test_case)
122
120
  hooks = registry.hooks_for(:after, scenario)
123
121
  AfterHooks.new(hooks, scenario).apply_to(test_case)
@@ -127,7 +125,7 @@ module Cucumber
127
125
  scenario = RunningTestCase.new(test_case)
128
126
 
129
127
  registry.hooks_for(:around, scenario).map do |hook|
130
- Hooks.around_hook(test_case.source) do |run_scenario|
128
+ Hooks.around_hook do |run_scenario|
131
129
  hook.invoke('Around', scenario, &run_scenario)
132
130
  end
133
131
  end
@@ -55,13 +55,11 @@ module Cucumber
55
55
  private
56
56
 
57
57
  def mri_gets(timeout_seconds)
58
- begin
59
- Timeout.timeout(timeout_seconds) do
60
- STDIN.gets
61
- end
62
- rescue Timeout::Error
63
- nil
58
+ Timeout.timeout(timeout_seconds) do
59
+ STDIN.gets
64
60
  end
61
+ rescue Timeout::Error
62
+ nil
65
63
  end
66
64
 
67
65
  def jruby_gets(timeout_seconds)
@@ -9,11 +9,12 @@ module Cucumber
9
9
  attr_reader :regexp_source, :location
10
10
 
11
11
  def initialize(regexp_source, location)
12
- @regexp_source, @location = regexp_source, location
12
+ @regexp_source = regexp_source
13
+ @location = location
13
14
  end
14
15
 
15
- def eql?(o)
16
- regexp_source == o.regexp_source && location == o.location
16
+ def eql?(other)
17
+ regexp_source == other.regexp_source && location == other.location
17
18
  end
18
19
 
19
20
  def hash