cucumber 10.2.0 → 11.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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -8
  3. data/VERSION +1 -1
  4. data/lib/cucumber/cli/configuration.rb +1 -1
  5. data/lib/cucumber/cli/main.rb +22 -0
  6. data/lib/cucumber/cli/options.rb +3 -18
  7. data/lib/cucumber/cli/profile_loader.rb +2 -2
  8. data/lib/cucumber/cli/rerun_file.rb +1 -1
  9. data/lib/cucumber/cli.rb +3 -0
  10. data/lib/cucumber/configuration.rb +33 -24
  11. data/lib/cucumber/events/base.rb +25 -0
  12. data/lib/cucumber/events/envelope.rb +29 -2
  13. data/lib/cucumber/events/gherkin_source_parsed.rb +12 -3
  14. data/lib/cucumber/events/gherkin_source_read.rb +11 -3
  15. data/lib/cucumber/events/hook_test_step_created.rb +12 -2
  16. data/lib/cucumber/events/step_activated.rb +13 -7
  17. data/lib/cucumber/events/step_definition_registered.rb +12 -4
  18. data/lib/cucumber/events/test_case_created.rb +11 -3
  19. data/lib/cucumber/events/test_case_finished.rb +12 -4
  20. data/lib/cucumber/events/test_case_ready.rb +10 -4
  21. data/lib/cucumber/events/test_case_started.rb +11 -2
  22. data/lib/cucumber/events/test_run_finished.rb +10 -3
  23. data/lib/cucumber/events/test_run_hook_finished.rb +19 -0
  24. data/lib/cucumber/events/test_run_hook_started.rb +20 -0
  25. data/lib/cucumber/events/test_run_started.rb +11 -2
  26. data/lib/cucumber/events/test_step_created.rb +12 -2
  27. data/lib/cucumber/events/test_step_finished.rb +12 -2
  28. data/lib/cucumber/events/test_step_started.rb +11 -2
  29. data/lib/cucumber/events/undefined_parameter_type.rb +11 -3
  30. data/lib/cucumber/events.rb +2 -0
  31. data/lib/cucumber/filters/fire_before_all_hooks.rb +36 -0
  32. data/lib/cucumber/filters/randomizer.rb +5 -5
  33. data/lib/cucumber/filters/reverser.rb +41 -0
  34. data/lib/cucumber/filters.rb +1 -12
  35. data/lib/cucumber/formatter/ansicolor.rb +3 -0
  36. data/lib/cucumber/formatter/console.rb +15 -7
  37. data/lib/cucumber/formatter/console_issues.rb +5 -5
  38. data/lib/cucumber/formatter/duration_extractor.rb +2 -0
  39. data/lib/cucumber/formatter/fail_fast.rb +3 -4
  40. data/lib/cucumber/formatter/global_hooks_summary.rb +36 -0
  41. data/lib/cucumber/formatter/html.rb +1 -3
  42. data/lib/cucumber/formatter/json.rb +5 -3
  43. data/lib/cucumber/formatter/junit.rb +5 -34
  44. data/lib/cucumber/formatter/message.rb +8 -4
  45. data/lib/cucumber/formatter/message_builder.rb +249 -143
  46. data/lib/cucumber/formatter/pretty.rb +2 -5
  47. data/lib/cucumber/formatter/progress.rb +1 -2
  48. data/lib/cucumber/formatter/rerun.rb +80 -30
  49. data/lib/cucumber/formatter/usage.rb +2 -3
  50. data/lib/cucumber/formatter.rb +3 -0
  51. data/lib/cucumber/glue/proto_world.rb +6 -4
  52. data/lib/cucumber/glue/registry_and_more.rb +122 -20
  53. data/lib/cucumber/glue.rb +3 -0
  54. data/lib/cucumber/multiline_argument/data_table.rb +0 -11
  55. data/lib/cucumber/platform.rb +2 -2
  56. data/lib/cucumber/query.rb +273 -0
  57. data/lib/cucumber/rake/forked_cucumber_runner.rb +57 -0
  58. data/lib/cucumber/rake/in_process_cucumber_runner.rb +27 -0
  59. data/lib/cucumber/rake/task.rb +36 -106
  60. data/lib/cucumber/rake.rb +3 -0
  61. data/lib/cucumber/repository.rb +136 -0
  62. data/lib/cucumber/rspec/disable_option_parser.rb +3 -3
  63. data/lib/cucumber/runtime/user_interface.rb +2 -2
  64. data/lib/cucumber/runtime.rb +29 -9
  65. data/lib/cucumber/step_match.rb +1 -1
  66. data/lib/cucumber.rb +2 -13
  67. metadata +33 -30
  68. data/lib/cucumber/formatter/errors.rb +0 -9
  69. data/lib/cucumber/formatter/query/hook_by_test_step.rb +0 -34
  70. data/lib/cucumber/formatter/query/pickle_by_test.rb +0 -28
  71. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +0 -28
  72. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +0 -42
  73. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +0 -44
  74. data/lib/cucumber/formatter/query/test_run_started.rb +0 -17
  75. data/lib/cucumber/step_definition_light.rb +0 -29
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/repository'
4
+ require 'cucumber/messages'
5
+
6
+ # Given one Cucumber Message, find another.
7
+ #
8
+ # Queries can be made while the test run is incomplete - and this will naturally return incomplete results
9
+ # see <a href="https://github.com/cucumber/messages?tab=readme-ov-file#message-overview">Cucumber Messages - Message Overview</a>
10
+ #
11
+ module Cucumber
12
+ class Query
13
+ attr_reader :repository
14
+ private :repository
15
+
16
+ include Cucumber::Messages::Helpers::TimeConversion
17
+ include Cucumber::Messages::Helpers::TestStepResultComparator
18
+
19
+ def initialize(repository)
20
+ @repository = repository
21
+ end
22
+
23
+ # TODO: count methods (1/2) Complete
24
+ # Missing: countMostSevereTestStepResultStatus
25
+
26
+ # TODO: findAll methods (11/12) Complete
27
+ # Missing: findAllUndefinedParameterTypes
28
+
29
+ # TODO: find****By methods (16/25) Complete
30
+ # Missing: findSuggestionsBy (2 variants)
31
+ # Missing: findUnambiguousStepDefinitionBy (1 variant)
32
+ # Missing: findTestStepFinishedAndTestStepBy (1 variant)
33
+ # Missing: findAttachmentsBy (2 variants)
34
+ # Missing: findTestCaseDurationBy (2 variant)
35
+ # REDUNDANT: findLineageBy (9 variants!)
36
+ # REDUNDANT: findLocationOf (1 variant) - This strictly speaking isn't a findBy but is located within them
37
+ # To Review: findMostSevereTestStepResultBy (2 variants)
38
+ # To Review: findTestRunDuration (1 variant) - This strictly speaking isn't a findBy but is located within them
39
+
40
+ def count_test_cases_started
41
+ find_all_test_case_started.length
42
+ end
43
+
44
+ def find_all_pickles
45
+ repository.pickle_by_id.values
46
+ end
47
+
48
+ def find_all_pickle_steps
49
+ repository.pickle_step_by_id.values
50
+ end
51
+
52
+ def find_all_step_definitions
53
+ repository.step_definition_by_id.values
54
+ end
55
+
56
+ # This finds all test cases from the following conditions (UNION)
57
+ # -> Test cases that have started, but not yet finished
58
+ # -> Test cases that have started, finished, but that will NOT be retried
59
+ def find_all_test_case_started
60
+ repository.test_case_started_by_id.values.select do |test_case_started|
61
+ test_case_finished = find_test_case_finished_by(test_case_started)
62
+ test_case_finished.nil? || !test_case_finished.will_be_retried
63
+ end
64
+ end
65
+
66
+ # This finds all test cases that have finished AND will not be retried
67
+ def find_all_test_case_finished
68
+ repository.test_case_finished_by_test_case_started_id.values.reject(&:will_be_retried)
69
+ end
70
+
71
+ def find_all_test_cases
72
+ repository.test_case_by_id.values
73
+ end
74
+
75
+ def find_all_test_run_hook_started
76
+ repository.test_run_hook_started_by_id.values
77
+ end
78
+
79
+ def find_all_test_run_hook_finished
80
+ repository.test_run_hook_finished_by_test_run_hook_started_id.values
81
+ end
82
+
83
+ def find_all_test_step_started
84
+ repository.test_steps_started_by_test_case_started_id.values.flatten
85
+ end
86
+
87
+ def find_all_test_step_finished
88
+ repository.test_steps_finished_by_test_case_started_id.values.flatten
89
+ end
90
+
91
+ def find_all_test_steps
92
+ repository.test_step_by_id.values
93
+ end
94
+
95
+ # This method will be called with 1 of these 3 messages
96
+ # [TestStep || TestRunHookStarted || TestRunHookFinished]
97
+ def find_hook_by(message)
98
+ ensure_only_message_types!(message, %i[test_step test_run_hook_started test_run_hook_finished], '#find_hook_by')
99
+
100
+ if message.is_a?(Cucumber::Messages::TestRunHookFinished)
101
+ test_run_hook_started_message = find_test_run_hook_started_by(message)
102
+ test_run_hook_started_message ? find_hook_by(test_run_hook_started_message) : nil
103
+ else
104
+ repository.hook_by_id[message.hook_id]
105
+ end
106
+ end
107
+
108
+ def find_meta
109
+ repository.meta
110
+ end
111
+
112
+ # This method will be called with 1 of these 2 messages
113
+ # [TestCaseStarted || TestCaseFinished]
114
+ def find_most_severe_test_step_result_by(message)
115
+ ensure_only_message_types!(message, %i[test_case_started test_case_finished], '#find_most_severe_test_step_result_by')
116
+
117
+ if message.is_a?(Cucumber::Messages::TestCaseStarted)
118
+ find_test_steps_finished_by(message)
119
+ .map(&:test_step_result)
120
+ .max_by { |test_step_result| test_step_result_rankings[test_step_result.status] }
121
+ # Java code: "PREVIOUS".max(comparing(TestStepResult::getStatus, new TestStepResultStatusComparator()));
122
+ else
123
+ test_case_started_message = find_test_case_started_by(message)
124
+ test_case_started_message && find_most_severe_test_step_result_by(test_case_started_message)
125
+ # Java code: return findTestCaseStartedBy(testCaseFinished).flatMap(this::findMostSevereTestStepResultBy);
126
+ end
127
+ end
128
+
129
+ # This method will be called with 1 of these 5 messages
130
+ # [TestCase || TestCaseStarted || TestCaseFinished || TestStepStarted || TestStepFinished]
131
+ def find_pickle_by(message)
132
+ ensure_only_message_types!(message, %i[test_case test_case_started test_case_finished test_step_started test_step_finished], '#find_pickle_by')
133
+
134
+ test_case_message = message.is_a?(Cucumber::Messages::TestCase) ? message : find_test_case_by(message)
135
+ repository.pickle_by_id[test_case_message.pickle_id]
136
+ end
137
+
138
+ # This method will be called with only 1 message
139
+ # [TestStep]
140
+ def find_pickle_step_by(test_step)
141
+ ensure_only_message_types!(test_step, %i[test_step], '#find_pickle_step_by')
142
+
143
+ repository.pickle_step_by_id[test_step.pickle_step_id]
144
+ end
145
+
146
+ # This method will be called with only 1 message
147
+ # [PickleStep]
148
+ def find_step_by(pickle_step)
149
+ ensure_only_message_types!(pickle_step, %i[pickle_step], '#find_step_by')
150
+
151
+ repository.step_by_id[pickle_step.ast_node_ids.first]
152
+ end
153
+
154
+ # This method will be called with only 1 message
155
+ # [TestStep]
156
+ def find_step_definitions_by(test_step)
157
+ ensure_only_message_types!(test_step, %i[test_step], '#find_step_definitions_by')
158
+
159
+ ids = test_step.step_definition_ids.nil? ? [] : test_step.step_definition_ids
160
+ ids.map { |id| repository.step_definition_by_id[id] }.compact
161
+ end
162
+
163
+ # This method will be called with 1 of these 4 messages
164
+ # [TestCaseStarted || TestCaseFinished || TestStepStarted || TestStepFinished]
165
+ def find_test_case_by(message)
166
+ ensure_only_message_types!(message, %i[test_case_started test_case_finished test_step_started test_step_finished], '#find_test_case_by')
167
+
168
+ test_case_started_message = message.is_a?(Cucumber::Messages::TestCaseStarted) ? message : find_test_case_started_by(message)
169
+ repository.test_case_by_id[test_case_started_message.test_case_id]
170
+ end
171
+
172
+ # This method will be called with 1 of these 3 messages
173
+ # [TestCaseFinished || TestStepStarted || TestStepFinished]
174
+ def find_test_case_started_by(message)
175
+ ensure_only_message_types!(message, %i[test_case_finished test_step_started test_step_finished], '#find_test_case_started_by')
176
+
177
+ repository.test_case_started_by_id[message.test_case_started_id]
178
+ end
179
+
180
+ # This method will be called with only 1 message
181
+ # [TestCaseStarted]
182
+ def find_test_case_finished_by(test_case_started)
183
+ ensure_only_message_types!(test_case_started, %i[test_case_started], '#find_test_case_finished_by')
184
+
185
+ repository.test_case_finished_by_test_case_started_id[test_case_started.id]
186
+ end
187
+
188
+ def find_test_run_duration
189
+ if repository.test_run_started.nil? || repository.test_run_finished.nil?
190
+ nil
191
+ else
192
+ timestamp_to_time(repository.test_run_finished.timestamp) - timestamp_to_time(repository.test_run_started.timestamp)
193
+ end
194
+ end
195
+
196
+ # This method will be called with only 1 message
197
+ # [TestRunHookFinished]
198
+ def find_test_run_hook_started_by(test_run_hook_finished)
199
+ ensure_only_message_types!(test_run_hook_finished, %i[test_run_hook_finished], '#find_test_run_hook_started_by')
200
+
201
+ repository.test_run_hook_started_by_id[test_run_hook_finished.test_run_hook_started_id]
202
+ end
203
+
204
+ # This method will be called with only 1 message
205
+ # [TestRunHookStarted]
206
+ def find_test_run_hook_finished_by(test_run_hook_started)
207
+ ensure_only_message_types!(test_run_hook_started, %i[test_run_hook_started], '#find_test_run_hook_finished_by')
208
+
209
+ repository.test_run_hook_finished_by_test_run_hook_started_id[test_run_hook_started.id]
210
+ end
211
+
212
+ def find_test_run_started
213
+ repository.test_run_started
214
+ end
215
+
216
+ def find_test_run_finished
217
+ repository.test_run_finished
218
+ end
219
+
220
+ # This method will be called with 1 of these 2 messages
221
+ # [TestStepStarted || TestStepFinished]
222
+ def find_test_step_by(message)
223
+ ensure_only_message_types!(message, %i[test_case_started test_case_finished], '#find_test_step_by')
224
+
225
+ repository.test_step_by_id[message.test_step_id]
226
+ end
227
+
228
+ # This method will be called with 1 of these 2 messages
229
+ # [TestCaseStarted || TestCaseFinished]
230
+ def find_test_steps_started_by(message)
231
+ ensure_only_message_types!(message, %i[test_case_started test_case_finished], '#find_test_steps_started_by')
232
+
233
+ key = message.is_a?(Cucumber::Messages::TestCaseStarted) ? message.id : message.test_case_started_id
234
+ repository.test_steps_started_by_test_case_started_id.fetch(key, [])
235
+ end
236
+
237
+ # This method will be called with 1 of these 2 messages
238
+ # [TestCaseStarted || TestCaseFinished]
239
+ def find_test_steps_finished_by(message)
240
+ ensure_only_message_types!(message, %i[test_case_started test_case_finished], '#find_test_steps_finished_by')
241
+
242
+ if message.is_a?(Cucumber::Messages::TestCaseStarted)
243
+ repository.test_steps_finished_by_test_case_started_id.fetch(message.id, [])
244
+ else
245
+ test_case_started_message = find_test_case_started_by(message)
246
+ test_case_started_message.nil? ? [] : find_test_steps_finished_by(test_case_started_message)
247
+ end
248
+ end
249
+
250
+ private
251
+
252
+ def ensure_only_message_types!(supplied_message, permissible_message_types, method_name)
253
+ raise ArgumentError, "Supplied argument is not a Cucumber Message. Argument: #{supplied_message.class}" unless supplied_message.is_a?(Cucumber::Messages::Message)
254
+
255
+ permitted_klasses = permissible_message_types.map { |message| message_types[message] }
256
+ raise ArgumentError, "Supplied message type '#{supplied_message.class}' is not permitted to be used when calling #{method_name}" unless permitted_klasses.include?(supplied_message.class)
257
+ end
258
+
259
+ def message_types
260
+ {
261
+ pickle_step: Cucumber::Messages::PickleStep,
262
+ test_case: Cucumber::Messages::TestCase,
263
+ test_case_started: Cucumber::Messages::TestCaseStarted,
264
+ test_case_finished: Cucumber::Messages::TestCaseFinished,
265
+ test_run_hook_started: Cucumber::Messages::TestRunHookStarted,
266
+ test_run_hook_finished: Cucumber::Messages::TestRunHookFinished,
267
+ test_step: Cucumber::Messages::TestStep,
268
+ test_step_started: Cucumber::Messages::TestStepStarted,
269
+ test_step_finished: Cucumber::Messages::TestStepFinished
270
+ }
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/dsl_definition'
4
+
5
+ module Cucumber
6
+ module Rake
7
+ class ForkedCucumberRunner # :nodoc:
8
+ include ::Rake::DSL if defined?(::Rake::DSL)
9
+
10
+ def initialize(libs, cucumber_bin, cucumber_opts, bundler, feature_files)
11
+ @libs = libs
12
+ @cucumber_bin = cucumber_bin
13
+ @cucumber_opts = cucumber_opts
14
+ @bundler = bundler
15
+ @feature_files = feature_files
16
+ end
17
+
18
+ def load_path
19
+ [format('"%<path>s"', path: @libs.join(File::PATH_SEPARATOR))]
20
+ end
21
+
22
+ def quoted_binary(cucumber_bin)
23
+ [format('"%<path>s"', path: cucumber_bin)]
24
+ end
25
+
26
+ def use_bundler
27
+ @bundler.nil? ? File.exist?('./Gemfile') && bundler_gem_available? : @bundler
28
+ end
29
+
30
+ def bundler_gem_available?
31
+ Gem::Specification.find_by_name('bundler')
32
+ rescue Gem::LoadError
33
+ false
34
+ end
35
+
36
+ def cmd
37
+ if use_bundler
38
+ [
39
+ Cucumber::RUBY_BINARY, '-S', 'bundle', 'exec', 'cucumber',
40
+ @cucumber_opts, @feature_files
41
+ ].flatten
42
+ else
43
+ [
44
+ Cucumber::RUBY_BINARY, '-I', load_path,
45
+ quoted_binary(@cucumber_bin), @cucumber_opts, @feature_files
46
+ ].flatten
47
+ end
48
+ end
49
+
50
+ def run
51
+ sh cmd.join(' ') do |ok, res|
52
+ exit res.exitstatus unless ok
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/dsl_definition'
4
+
5
+ require_relative '../cli/main'
6
+
7
+ module Cucumber
8
+ module Rake
9
+ class InProcessCucumberRunner
10
+ include ::Rake::DSL if defined?(::Rake::DSL)
11
+
12
+ attr_reader :args
13
+
14
+ def initialize(libs, cucumber_opts, feature_files)
15
+ raise 'libs must be an Array when running in-process' unless libs.instance_of? Array
16
+
17
+ libs.reverse_each { |lib| $LOAD_PATH.unshift(lib) }
18
+ @args = (cucumber_opts + feature_files).flatten.compact
19
+ end
20
+
21
+ def run
22
+ failure = Cucumber::Cli::Main.execute(args)
23
+ raise 'Cucumber failed' if failure
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cucumber/platform'
4
- require 'cucumber/gherkin/formatter/ansi_escapes'
5
3
  require 'rake/dsl_definition'
6
4
 
5
+ require_relative '../gherkin/formatter/ansi_escapes'
6
+ require_relative '../platform'
7
+ require_relative 'forked_cucumber_runner'
8
+ require_relative 'in_process_cucumber_runner'
9
+
7
10
  module Cucumber
8
11
  module Rake
9
12
  # Defines a Rake task for running features.
@@ -26,120 +29,35 @@ module Cucumber
26
29
  include Cucumber::Gherkin::Formatter::AnsiEscapes
27
30
  include ::Rake::DSL if defined?(::Rake::DSL)
28
31
 
29
- class InProcessCucumberRunner # :nodoc:
30
- include ::Rake::DSL if defined?(::Rake::DSL)
31
-
32
- attr_reader :args
33
-
34
- def initialize(libs, cucumber_opts, feature_files)
35
- raise 'libs must be an Array when running in-process' unless libs.instance_of? Array
36
-
37
- libs.reverse_each { |lib| $LOAD_PATH.unshift(lib) }
38
- @args = (
39
- cucumber_opts +
40
- feature_files
41
- ).flatten.compact
42
- end
43
-
44
- def run
45
- require 'cucumber/cli/main'
46
- failure = Cucumber::Cli::Main.execute(args)
47
- raise 'Cucumber failed' if failure
48
- end
49
- end
50
-
51
- class ForkedCucumberRunner # :nodoc:
52
- include ::Rake::DSL if defined?(::Rake::DSL)
53
-
54
- def initialize(libs, cucumber_bin, cucumber_opts, bundler, feature_files)
55
- @libs = libs
56
- @cucumber_bin = cucumber_bin
57
- @cucumber_opts = cucumber_opts
58
- @bundler = bundler
59
- @feature_files = feature_files
60
- end
61
-
62
- def load_path
63
- [format('"%<path>s"', path: @libs.join(File::PATH_SEPARATOR))]
64
- end
65
-
66
- def quoted_binary(cucumber_bin)
67
- [format('"%<path>s"', path: cucumber_bin)]
68
- end
69
-
70
- def use_bundler
71
- @bundler.nil? ? File.exist?('./Gemfile') && bundler_gem_available? : @bundler
72
- end
73
-
74
- def bundler_gem_available?
75
- Gem::Specification.find_by_name('bundler')
76
- rescue Gem::LoadError
77
- false
78
- end
79
-
80
- def cmd
81
- if use_bundler
82
- [
83
- Cucumber::RUBY_BINARY, '-S', 'bundle', 'exec', 'cucumber',
84
- @cucumber_opts, @feature_files
85
- ].flatten
86
- else
87
- [
88
- Cucumber::RUBY_BINARY, '-I', load_path,
89
- quoted_binary(@cucumber_bin), @cucumber_opts, @feature_files
90
- ].flatten
91
- end
92
- end
93
-
94
- def run
95
- sh cmd.join(' ') do |ok, res|
96
- exit res.exitstatus unless ok
97
- end
98
- end
99
- end
100
-
101
- # Directories to add to the Ruby $LOAD_PATH
102
- attr_accessor :libs
103
-
104
32
  # Name of the cucumber binary to use for running features. Defaults to Cucumber::BINARY
105
33
  attr_accessor :binary
106
34
 
35
+ # Whether or not to run with bundler (bundle exec). Setting this to false may speed
36
+ # up the execution. The default value is true if Bundler is installed and you have
37
+ # a Gemfile, false otherwise.
38
+ #
39
+ # Note that this attribute has no effect if you don't run in forked mode.
40
+ attr_accessor :bundler
41
+
107
42
  # Extra options to pass to the cucumber binary. Can be overridden by the CUCUMBER_OPTS environment variable.
108
43
  # It's recommended to pass an Array, but if it's a String it will be #split by ' '.
109
44
  attr_reader :cucumber_opts
110
45
 
111
- def cucumber_opts=(opts) # :nodoc:
112
- unless opts.instance_of? String
113
- @cucumber_opts = opts
114
- return
115
- end
116
-
117
- @cucumber_opts = opts.split(' ')
118
- return if @cucumber_opts.length <= 1
119
-
120
- $stderr.puts 'WARNING: consider using an array rather than a space-delimited string with cucumber_opts to avoid undesired behavior.'
121
- end
122
-
123
46
  # Whether or not to fork a new ruby interpreter. Defaults to true. You may gain
124
47
  # some startup speed if you set it to false, but this may also cause issues with
125
48
  # your load path and gems.
126
49
  attr_accessor :fork
127
50
 
51
+ # Directories to add to the Ruby $LOAD_PATH
52
+ attr_accessor :libs
53
+
128
54
  # Define what profile to be used. When used with cucumber_opts it is simply appended
129
55
  # to it. Will be ignored when CUCUMBER_OPTS is used.
130
56
  attr_accessor :profile
131
57
 
132
- # Whether or not to run with bundler (bundle exec). Setting this to false may speed
133
- # up the execution. The default value is true if Bundler is installed and you have
134
- # a Gemfile, false otherwise.
135
- #
136
- # Note that this attribute has no effect if you don't run in forked mode.
137
- attr_accessor :bundler
138
-
139
58
  # Name of the running task
140
59
  attr_reader :task_name
141
60
 
142
- # Define Cucumber Rake task
143
61
  def initialize(task_name = 'cucumber', desc = 'Run Cucumber features')
144
62
  @task_name = task_name
145
63
  @desc = desc
@@ -151,24 +69,29 @@ module Cucumber
151
69
  define_task
152
70
  end
153
71
 
154
- def define_task # :nodoc:
155
- desc @desc
156
- task @task_name do
157
- runner.run
72
+ def cucumber_opts=(opts) # :nodoc:
73
+ unless opts.instance_of? String
74
+ @cucumber_opts = opts
75
+ return
158
76
  end
159
- end
160
77
 
161
- def runner(_task_args = nil) # :nodoc:
162
- cucumber_opts = [ENV['CUCUMBER_OPTS']&.split(/\s+/) || cucumber_opts_with_profile]
163
- return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if fork
78
+ @cucumber_opts = opts.split(' ')
79
+ return if @cucumber_opts.length <= 1
164
80
 
165
- InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
81
+ $stderr.puts 'WARNING: consider using an array rather than a space-delimited string with cucumber_opts to avoid undesired behavior.'
166
82
  end
167
83
 
168
84
  def cucumber_opts_with_profile # :nodoc:
169
85
  Array(cucumber_opts).concat(Array(@profile).flat_map { |p| ['--profile', p] })
170
86
  end
171
87
 
88
+ def define_task # :nodoc:
89
+ desc @desc
90
+ task @task_name do
91
+ runner.run
92
+ end
93
+ end
94
+
172
95
  def feature_files # :nodoc:
173
96
  make_command_line_safe(FileList[ENV['FEATURE'] || []])
174
97
  end
@@ -176,6 +99,13 @@ module Cucumber
176
99
  def make_command_line_safe(list)
177
100
  list.map { |string| string.gsub(' ', '\ ') }
178
101
  end
102
+
103
+ def runner(_task_args = nil) # :nodoc:
104
+ cucumber_opts = [ENV['CUCUMBER_OPTS']&.split(/\s+/) || cucumber_opts_with_profile]
105
+ return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if fork
106
+
107
+ InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
108
+ end
179
109
  end
180
110
  end
181
111
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{File.dirname(__FILE__)}/rake/*.rb"].map(&method(:require))
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ # In memory repository i.e. a thread based link to cucumber-query
5
+ class Repository
6
+ attr_accessor :meta, :test_run_started, :test_run_finished
7
+ attr_reader :attachments_by_test_case_started_id, :attachments_by_test_run_hook_started_id,
8
+ :hook_by_id,
9
+ :pickle_by_id, :pickle_step_by_id,
10
+ :step_by_id, :step_definition_by_id,
11
+ :test_case_by_id, :test_case_started_by_id, :test_case_finished_by_test_case_started_id,
12
+ :test_run_hook_started_by_id, :test_run_hook_finished_by_test_run_hook_started_id,
13
+ :test_step_by_id, :test_steps_started_by_test_case_started_id, :test_steps_finished_by_test_case_started_id
14
+
15
+ # TODO: Missing structs (2)
16
+ # final Map<String, List<Suggestion>> suggestionsByPickleStepId = new LinkedHashMap<>();
17
+ # final List<UndefinedParameterType> undefinedParameterTypes = new ArrayList<>();
18
+
19
+ # TODO: Missing handlers
20
+ # Source
21
+ #
22
+
23
+ def initialize
24
+ @attachments_by_test_case_started_id = Hash.new { |hash, key| hash[key] = [] }
25
+ @attachments_by_test_run_hook_started_id = Hash.new { |hash, key| hash[key] = [] }
26
+ @hook_by_id = {}
27
+ @pickle_by_id = {}
28
+ @pickle_step_by_id = {}
29
+ @step_by_id = {}
30
+ @step_definition_by_id = {}
31
+ @test_case_by_id = {}
32
+ @test_case_started_by_id = {}
33
+ @test_case_finished_by_test_case_started_id = {}
34
+ @test_run_hook_started_by_id = {}
35
+ @test_run_hook_finished_by_test_run_hook_started_id = {}
36
+ @test_step_by_id = {}
37
+ @test_steps_started_by_test_case_started_id = Hash.new { |hash, key| hash[key] = [] }
38
+ @test_steps_finished_by_test_case_started_id = Hash.new { |hash, key| hash[key] = [] }
39
+ end
40
+
41
+ def update(envelope)
42
+ return self.meta = envelope.meta if envelope.meta
43
+ return self.test_run_started = envelope.test_run_started if envelope.test_run_started
44
+ return self.test_run_finished = envelope.test_run_finished if envelope.test_run_finished
45
+ return update_attachment(envelope.attachment) if envelope.attachment
46
+ return update_gherkin_document(envelope.gherkin_document) if envelope.gherkin_document
47
+ return update_hook(envelope.hook) if envelope.hook
48
+ return update_pickle(envelope.pickle) if envelope.pickle
49
+ return update_step_definition(envelope.step_definition) if envelope.step_definition
50
+ return update_test_run_hook_started(envelope.test_run_hook_started) if envelope.test_run_hook_started
51
+ return update_test_run_hook_finished(envelope.test_run_hook_finished) if envelope.test_run_hook_finished
52
+ return update_test_case_started(envelope.test_case_started) if envelope.test_case_started
53
+ return update_test_case_finished(envelope.test_case_finished) if envelope.test_case_finished
54
+ return update_test_step_started(envelope.test_step_started) if envelope.test_step_started
55
+ return update_test_step_finished(envelope.test_step_finished) if envelope.test_step_finished
56
+ return update_test_case(envelope.test_case) if envelope.test_case
57
+
58
+ nil
59
+ end
60
+
61
+ private
62
+
63
+ def update_attachment(attachment)
64
+ attachments_by_test_case_started_id[attachment.test_case_started_id] << attachment if attachment.test_case_started_id
65
+ attachments_by_test_run_hook_started_id[attachment.test_run_hook_started_id] << attachment if attachment.test_run_hook_started_id
66
+ end
67
+
68
+ def update_feature(feature)
69
+ feature.children.each do |feature_child|
70
+ update_steps(feature_child.background.steps) if feature_child.background
71
+ update_scenario(feature_child.scenario) if feature_child.scenario
72
+ next unless feature_child.rule
73
+
74
+ feature_child.rule.children.each do |rule_child|
75
+ update_steps(rule_child.background.steps) if rule_child.background
76
+ update_scenario(rule_child.scenario) if rule_child.scenario
77
+ end
78
+ end
79
+ end
80
+
81
+ def update_gherkin_document(gherkin_document)
82
+ # TODO: Update lineage at a later date. Java Impl -> lineageById.putAll(Lineages.of(document));
83
+ update_feature(gherkin_document.feature) if gherkin_document.feature
84
+ end
85
+
86
+ def update_hook(hook)
87
+ hook_by_id[hook.id] = hook
88
+ end
89
+
90
+ def update_pickle(pickle)
91
+ pickle_by_id[pickle.id] = pickle
92
+ pickle.steps.each { |pickle_step| pickle_step_by_id[pickle_step.id] = pickle_step }
93
+ end
94
+
95
+ def update_scenario(scenario)
96
+ update_steps(scenario.steps)
97
+ end
98
+
99
+ def update_steps(steps)
100
+ steps.each { |step| step_by_id[step.id] = step }
101
+ end
102
+
103
+ def update_step_definition(step_definition)
104
+ step_definition_by_id[step_definition.id] = step_definition
105
+ end
106
+
107
+ def update_test_case(test_case)
108
+ test_case_by_id[test_case.id] = test_case
109
+ test_case.test_steps.each { |test_step| test_step_by_id[test_step.id] = test_step }
110
+ end
111
+
112
+ def update_test_case_started(test_case_started)
113
+ test_case_started_by_id[test_case_started.id] = test_case_started
114
+ end
115
+
116
+ def update_test_case_finished(test_case_finished)
117
+ test_case_finished_by_test_case_started_id[test_case_finished.test_case_started_id] = test_case_finished
118
+ end
119
+
120
+ def update_test_run_hook_started(test_run_hook_started)
121
+ test_run_hook_started_by_id[test_run_hook_started.id] = test_run_hook_started
122
+ end
123
+
124
+ def update_test_run_hook_finished(test_run_hook_finished)
125
+ test_run_hook_finished_by_test_run_hook_started_id[test_run_hook_finished.test_run_hook_started_id] = test_run_hook_finished
126
+ end
127
+
128
+ def update_test_step_started(test_step_started)
129
+ test_steps_started_by_test_case_started_id[test_step_started.test_case_started_id] << test_step_started
130
+ end
131
+
132
+ def update_test_step_finished(test_step_finished)
133
+ test_steps_finished_by_test_case_started_id[test_step_finished.test_case_started_id] << test_step_finished
134
+ end
135
+ end
136
+ end