cucumber 3.2.0 → 4.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
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