cucumber 11.1.0 → 12.0.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/cucumber/cli/configuration.rb +0 -6
  4. data/lib/cucumber/cli/options.rb +0 -16
  5. data/lib/cucumber/configuration.rb +5 -5
  6. data/lib/cucumber/errors.rb +43 -23
  7. data/lib/cucumber/events/attach_called.rb +28 -0
  8. data/lib/cucumber/events/envelope.rb +2 -2
  9. data/lib/cucumber/events/gherkin_source_parsed.rb +1 -3
  10. data/lib/cucumber/events/gherkin_source_read.rb +1 -1
  11. data/lib/cucumber/events/hook_test_step_created.rb +1 -3
  12. data/lib/cucumber/events/step_activated.rb +1 -1
  13. data/lib/cucumber/events/step_definition_registered.rb +1 -3
  14. data/lib/cucumber/events/test_case_created.rb +1 -1
  15. data/lib/cucumber/events/test_case_finished.rb +1 -1
  16. data/lib/cucumber/events/test_case_ready.rb +1 -1
  17. data/lib/cucumber/events/test_case_started.rb +1 -3
  18. data/lib/cucumber/events/test_run_finished.rb +1 -1
  19. data/lib/cucumber/events/test_run_hook_finished.rb +1 -1
  20. data/lib/cucumber/events/test_run_hook_started.rb +1 -3
  21. data/lib/cucumber/events/test_run_started.rb +1 -3
  22. data/lib/cucumber/events/test_step_created.rb +1 -3
  23. data/lib/cucumber/events/test_step_finished.rb +1 -3
  24. data/lib/cucumber/events/test_step_started.rb +1 -3
  25. data/lib/cucumber/events/undefined_parameter_type.rb +1 -1
  26. data/lib/cucumber/events.rb +7 -2
  27. data/lib/cucumber/filters/retry.rb +7 -22
  28. data/lib/cucumber/formatter/console.rb +5 -5
  29. data/lib/cucumber/formatter/console_counts.rb +5 -3
  30. data/lib/cucumber/formatter/console_issues.rb +3 -3
  31. data/lib/cucumber/formatter/duration_extractor.rb +0 -2
  32. data/lib/cucumber/formatter/fail_fast.rb +2 -2
  33. data/lib/cucumber/formatter/html.rb +10 -6
  34. data/lib/cucumber/formatter/json.rb +10 -8
  35. data/lib/cucumber/formatter/junit.rb +3 -3
  36. data/lib/cucumber/formatter/message.rb +1 -5
  37. data/lib/cucumber/formatter/message_builder.rb +39 -147
  38. data/lib/cucumber/formatter/message_handlers.rb +13 -0
  39. data/lib/cucumber/formatter/pretty.rb +7 -6
  40. data/lib/cucumber/formatter/progress.rb +1 -0
  41. data/lib/cucumber/formatter/rerun.rb +15 -42
  42. data/lib/cucumber/formatter/summary.rb +15 -1
  43. data/lib/cucumber/glue/hook.rb +0 -1
  44. data/lib/cucumber/glue/multiple_world.rb +26 -0
  45. data/lib/cucumber/glue/nil_world.rb +12 -0
  46. data/lib/cucumber/glue/proto_world.rb +4 -5
  47. data/lib/cucumber/glue/registry_and_more.rb +15 -32
  48. data/lib/cucumber/multiline_argument/data_table.rb +5 -7
  49. data/lib/cucumber/retry_policy.rb +27 -0
  50. data/lib/cucumber/rspec/doubles.rb +1 -7
  51. data/lib/cucumber/runtime/meta_message_builder.rb +1 -1
  52. data/lib/cucumber/runtime/user_interface.rb +18 -3
  53. data/lib/cucumber/runtime.rb +34 -53
  54. metadata +53 -26
  55. data/lib/cucumber/events/base.rb +0 -25
  56. data/lib/cucumber/formatter/fanout.rb +0 -28
  57. data/lib/cucumber/query.rb +0 -273
  58. data/lib/cucumber/repository.rb +0 -136
@@ -1,273 +0,0 @@
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
@@ -1,136 +0,0 @@
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