cucumber 0.8.7 → 0.9.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 (64) hide show
  1. data/.gitignore +24 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +16 -3
  4. data/Rakefile +4 -50
  5. data/cucumber.gemspec +36 -600
  6. data/features/cucumber_cli.feature +1 -1
  7. data/features/json_formatter.feature +1 -1
  8. data/features/junit_formatter.feature +10 -6
  9. data/features/post_configuration_hook.feature +15 -2
  10. data/features/step_definitions/cucumber_steps.rb +5 -1
  11. data/features/step_definitions/wire_steps.rb +1 -0
  12. data/features/support/env.rb +2 -5
  13. data/features/wire_protocol.feature +1 -1
  14. data/lib/cucumber.rb +8 -0
  15. data/lib/cucumber/ast/outline_table.rb +4 -4
  16. data/lib/cucumber/ast/step_invocation.rb +14 -13
  17. data/lib/cucumber/ast/table.rb +2 -1
  18. data/lib/cucumber/ast/tree_walker.rb +3 -3
  19. data/lib/cucumber/cli/configuration.rb +32 -7
  20. data/lib/cucumber/cli/main.rb +26 -30
  21. data/lib/cucumber/cli/options.rb +1 -3
  22. data/lib/cucumber/cli/profile_loader.rb +2 -0
  23. data/lib/cucumber/configuration.rb +37 -0
  24. data/lib/cucumber/errors.rb +40 -0
  25. data/lib/cucumber/feature_file.rb +5 -12
  26. data/lib/cucumber/formatter/junit.rb +2 -2
  27. data/lib/cucumber/formatter/tag_cloud.rb +1 -1
  28. data/lib/cucumber/js_support/js_dsl.js +4 -4
  29. data/lib/cucumber/js_support/js_language.rb +9 -5
  30. data/lib/cucumber/language_support.rb +2 -2
  31. data/lib/cucumber/parser/gherkin_builder.rb +19 -19
  32. data/lib/cucumber/platform.rb +3 -4
  33. data/lib/cucumber/rake/task.rb +1 -7
  34. data/lib/cucumber/rb_support/rb_dsl.rb +1 -0
  35. data/lib/cucumber/rb_support/rb_language.rb +1 -0
  36. data/lib/cucumber/rspec/doubles.rb +3 -3
  37. data/lib/cucumber/runtime.rb +192 -0
  38. data/lib/cucumber/runtime/features_loader.rb +62 -0
  39. data/lib/cucumber/runtime/results.rb +46 -0
  40. data/lib/cucumber/runtime/support_code.rb +174 -0
  41. data/lib/cucumber/runtime/user_interface.rb +80 -0
  42. data/lib/cucumber/step_mother.rb +6 -427
  43. data/lib/cucumber/wire_support/configuration.rb +2 -0
  44. data/lib/cucumber/wire_support/wire_language.rb +1 -8
  45. data/spec/cucumber/ast/background_spec.rb +3 -3
  46. data/spec/cucumber/ast/feature_spec.rb +2 -2
  47. data/spec/cucumber/ast/scenario_outline_spec.rb +1 -1
  48. data/spec/cucumber/ast/scenario_spec.rb +1 -2
  49. data/spec/cucumber/ast/tree_walker_spec.rb +1 -1
  50. data/spec/cucumber/cli/configuration_spec.rb +31 -5
  51. data/spec/cucumber/cli/drb_client_spec.rb +1 -1
  52. data/spec/cucumber/cli/main_spec.rb +8 -37
  53. data/spec/cucumber/cli/options_spec.rb +20 -0
  54. data/spec/cucumber/formatter/spec_helper.rb +5 -7
  55. data/spec/cucumber/rb_support/rb_language_spec.rb +2 -2
  56. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +1 -1
  57. data/spec/cucumber/runtime_spec.rb +294 -0
  58. data/spec/cucumber/step_match_spec.rb +10 -8
  59. data/spec/cucumber/world/pending_spec.rb +1 -1
  60. data/spec/spec_helper.rb +2 -21
  61. metadata +215 -84
  62. data/Caliper.yml +0 -4
  63. data/VERSION.yml +0 -5
  64. data/spec/cucumber/step_mother_spec.rb +0 -302
@@ -0,0 +1,62 @@
1
+ require 'cucumber/errors'
2
+
3
+ module Cucumber
4
+ class Runtime
5
+
6
+ class FeaturesLoader
7
+ include Formatter::Duration
8
+
9
+ def initialize(feature_files, filters, tag_expression)
10
+ @feature_files, @filters, @tag_expression = feature_files, filters, tag_expression
11
+ end
12
+
13
+ def features
14
+ load unless @features
15
+ @features
16
+ end
17
+
18
+ private
19
+
20
+ def load
21
+ features = Ast::Features.new
22
+
23
+ tag_counts = {}
24
+ start = Time.new
25
+ log.debug("Features:\n")
26
+ @feature_files.each do |f|
27
+ feature_file = FeatureFile.new(f)
28
+ feature = feature_file.parse(@filters, tag_counts)
29
+ if feature
30
+ features.add_feature(feature)
31
+ log.debug(" * #{f}\n")
32
+ end
33
+ end
34
+ duration = Time.now - start
35
+ log.debug("Parsing feature files took #{format_duration(duration)}\n\n")
36
+
37
+ check_tag_limits(tag_counts)
38
+
39
+ @features = features
40
+ end
41
+
42
+ def check_tag_limits(tag_counts)
43
+ error_messages = []
44
+ @tag_expression.limits.each do |tag_name, tag_limit|
45
+ tag_locations = (tag_counts[tag_name] || [])
46
+ tag_count = tag_locations.length
47
+ if tag_count > tag_limit
48
+ error = "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
49
+ tag_locations.join("\n ")
50
+ error_messages << error
51
+ end
52
+ end
53
+ raise TagExcess.new(error_messages) if error_messages.any?
54
+ end
55
+
56
+ def log
57
+ Cucumber.logger
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,46 @@
1
+ module Cucumber
2
+ class Runtime
3
+
4
+ class Results
5
+ def initialize(configuration)
6
+ @configuration = configuration
7
+ end
8
+
9
+ def step_visited(step) #:nodoc:
10
+ steps << step unless steps.index(step)
11
+ end
12
+
13
+ def scenario_visited(scenario) #:nodoc:
14
+ scenarios << scenario unless scenarios.index(scenario)
15
+ end
16
+
17
+ def steps(status = nil) #:nodoc:
18
+ @steps ||= []
19
+ if(status)
20
+ @steps.select{|step| step.status == status}
21
+ else
22
+ @steps
23
+ end
24
+ end
25
+
26
+ def scenarios(status = nil) #:nodoc:
27
+ @scenarios ||= []
28
+ if(status)
29
+ @scenarios.select{|scenario| scenario.status == status}
30
+ else
31
+ @scenarios
32
+ end
33
+ end
34
+
35
+ def failure?
36
+ if @configuration.wip?
37
+ scenarios(:passed).any?
38
+ else
39
+ scenarios(:failed).any? ||
40
+ (@configuration.strict? && (steps(:undefined).any? || steps(:pending).any?))
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,174 @@
1
+ require 'cucumber/constantize'
2
+
3
+ module Cucumber
4
+ class Runtime
5
+
6
+ class SupportCode
7
+ class StepInvoker
8
+ include Gherkin::Rubify
9
+
10
+ def initialize(support_code)
11
+ @support_code = support_code
12
+ end
13
+
14
+ def uri(uri)
15
+ end
16
+
17
+ def step(step)
18
+ cucumber_multiline_arg = case(rubify(step.multiline_arg))
19
+ when Gherkin::Formatter::Model::PyString
20
+ step.multiline_arg.value
21
+ when Array
22
+ Ast::Table.new(step.multiline_arg.map{|row| row.cells})
23
+ else
24
+ nil
25
+ end
26
+ @support_code.invoke(step.name, cucumber_multiline_arg)
27
+ end
28
+
29
+ def eof
30
+ end
31
+ end
32
+
33
+ include Constantize
34
+
35
+ def initialize(step_mother, in_guess_mode)
36
+ @step_mother = step_mother
37
+ @guess_step_matches = in_guess_mode
38
+ @unsupported_programming_languages = []
39
+ @programming_languages = []
40
+ @language_map = {}
41
+ end
42
+
43
+ def invoke_steps(steps_text, i18n, file_colon_line)
44
+ file, line = file_colon_line.split(':')
45
+ parser = Gherkin::Parser::Parser.new(StepInvoker.new(self), true, 'steps')
46
+ parser.parse(steps_text, file, line.to_i)
47
+ end
48
+
49
+ def load_programming_language!(ext)
50
+ return @language_map[ext] if @language_map[ext]
51
+ programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language")
52
+ programming_language = programming_language_class.new(@step_mother)
53
+ @programming_languages << programming_language
54
+ @language_map[ext] = programming_language
55
+ programming_language
56
+ end
57
+
58
+ def load_files!(files)
59
+ log.debug("Code:\n")
60
+ files.each do |file|
61
+ load_file(file)
62
+ end
63
+ log.debug("\n")
64
+ end
65
+
66
+ def unmatched_step_definitions
67
+ @programming_languages.map do |programming_language|
68
+ programming_language.unmatched_step_definitions
69
+ end.flatten
70
+ end
71
+
72
+ def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
73
+ load_programming_language!('rb') if unknown_programming_language?
74
+ @programming_languages.map do |programming_language|
75
+ programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
76
+ end.join("\n")
77
+ end
78
+
79
+ def unknown_programming_language?
80
+ @programming_languages.empty?
81
+ end
82
+
83
+ def fire_hook(name, *args)
84
+ @programming_languages.each do |programming_language|
85
+ programming_language.send(name, *args)
86
+ end
87
+ end
88
+
89
+ def around(scenario, block)
90
+ @programming_languages.reverse.inject(block) do |blk, programming_language|
91
+ proc do
92
+ programming_language.around(scenario) do
93
+ blk.call(scenario)
94
+ end
95
+ end
96
+ end.call
97
+ end
98
+
99
+ def step_match(step_name, name_to_report=nil) #:nodoc:
100
+ matches = matches(step_name, name_to_report)
101
+ raise Undefined.new(step_name) if matches.empty?
102
+ matches = best_matches(step_name, matches) if matches.size > 1 && guess_step_matches?
103
+ raise Ambiguous.new(step_name, matches, guess_step_matches?) if matches.size > 1
104
+ matches[0]
105
+ end
106
+
107
+ def invoke(step_name, multiline_argument=nil)
108
+ begin
109
+ step_match(step_name).invoke(multiline_argument)
110
+ rescue Exception => e
111
+ e.nested! if Undefined === e
112
+ raise e
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def guess_step_matches?
119
+ @guess_step_matches
120
+ end
121
+
122
+ def matches(step_name, name_to_report)
123
+ @programming_languages.map do |programming_language|
124
+ programming_language.step_matches(step_name, name_to_report).to_a
125
+ end.flatten
126
+ end
127
+
128
+ def best_matches(step_name, step_matches) #:nodoc:
129
+ no_groups = step_matches.select {|step_match| step_match.args.length == 0}
130
+ max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
131
+ top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
132
+
133
+ if no_groups.any?
134
+ longest_regexp_length = no_groups.map {|step_match| step_match.text_length }.max
135
+ no_groups.select {|step_match| step_match.text_length == longest_regexp_length }
136
+ elsif top_groups.any?
137
+ shortest_capture_length = top_groups.map {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } }.min
138
+ top_groups.select {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } == shortest_capture_length }
139
+ else
140
+ top_groups
141
+ end
142
+ end
143
+
144
+ def load_file(file)
145
+ if programming_language = programming_language_for(file)
146
+ log.debug(" * #{file}\n")
147
+ programming_language.load_code_file(file)
148
+ else
149
+ log.debug(" * #{file} [NOT SUPPORTED]\n")
150
+ end
151
+ end
152
+
153
+ def log
154
+ Cucumber.logger
155
+ end
156
+
157
+ def programming_language_for(step_def_file) #:nodoc:
158
+ if ext = File.extname(step_def_file)[1..-1]
159
+ return nil if @unsupported_programming_languages.index(ext)
160
+ begin
161
+ load_programming_language!(ext)
162
+ rescue LoadError => e
163
+ log.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n")
164
+ @unsupported_programming_languages << ext
165
+ nil
166
+ end
167
+ else
168
+ nil
169
+ end
170
+ end
171
+
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,80 @@
1
+ require 'timeout'
2
+
3
+ module Cucumber
4
+ class Runtime
5
+
6
+ module UserInterface
7
+ attr_writer :visitor
8
+
9
+ # Output +announcement+ alongside the formatted output.
10
+ # This is an alternative to using Kernel#puts - it will display
11
+ # nicer, and in all outputs (in case you use several formatters)
12
+ #
13
+ def announce(msg)
14
+ msg.respond_to?(:join) ? @visitor.announce(msg.join("\n")) : @visitor.announce(msg.to_s)
15
+ end
16
+
17
+ # Suspends execution and prompts +question+ to the console (STDOUT).
18
+ # An operator (manual tester) can then enter a line of text and hit
19
+ # <ENTER>. The entered text is returned, and both +question+ and
20
+ # the result is added to the output using #announce.
21
+ #
22
+ # If you want a beep to happen (to grab the manual tester's attention),
23
+ # just prepend ASCII character 7 to the question:
24
+ #
25
+ # ask("#{7.chr}How many cukes are in the external system?")
26
+ #
27
+ # If that doesn't issue a beep, you can shell out to something else
28
+ # that makes a sound before invoking #ask.
29
+ #
30
+ def ask(question, timeout_seconds)
31
+ STDOUT.puts(question)
32
+ STDOUT.flush
33
+ announce(question)
34
+
35
+ if(Cucumber::JRUBY)
36
+ answer = jruby_gets(timeout_seconds)
37
+ else
38
+ answer = mri_gets(timeout_seconds)
39
+ end
40
+
41
+ if(answer)
42
+ announce(answer)
43
+ answer
44
+ else
45
+ raise("Waited for input for #{timeout_seconds} seconds, then timed out.")
46
+ end
47
+ end
48
+
49
+ # Embed +file+ of MIME type +mime_type+ into the output. This may or may
50
+ # not be ignored, depending on what kind of formatter(s) are active.
51
+ #
52
+ def embed(file, mime_type)
53
+ @visitor.embed(file, mime_type)
54
+ end
55
+
56
+ private
57
+
58
+ def mri_gets(timeout_seconds)
59
+ begin
60
+ Timeout.timeout(timeout_seconds) do
61
+ STDIN.gets
62
+ end
63
+ rescue Timeout::Error => e
64
+ nil
65
+ end
66
+ end
67
+
68
+ def jruby_gets(timeout_seconds)
69
+ answer = nil
70
+ t = java.lang.Thread.new do
71
+ answer = STDIN.gets
72
+ end
73
+ t.start
74
+ t.join(timeout_seconds * 1000)
75
+ answer
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -1,431 +1,10 @@
1
- require 'cucumber/constantize'
2
- require 'cucumber/core_ext/instance_exec'
3
- require 'cucumber/language_support/language_methods'
4
- require 'cucumber/formatter/duration'
5
- require 'cucumber/cli/options'
6
- require 'timeout'
1
+ require 'cucumber/runtime'
7
2
 
8
3
  module Cucumber
9
- # Raised when there is no matching StepDefinition for a step.
10
- class Undefined < StandardError
11
- attr_reader :step_name
12
-
13
- def initialize(step_name)
14
- super %{Undefined step: "#{step_name}"}
15
- @step_name = step_name
16
- end
17
-
18
- def nested!
19
- @nested = true
20
- end
21
-
22
- def nested?
23
- @nested
24
- end
25
- end
26
-
27
- # Raised when a StepDefinition's block invokes World#pending
28
- class Pending < StandardError
29
- end
30
-
31
- # Raised when a step matches 2 or more StepDefinitions
32
- class Ambiguous < StandardError
33
- def initialize(step_name, step_definitions, used_guess)
34
- message = "Ambiguous match of \"#{step_name}\":\n\n"
35
- message << step_definitions.map{|sd| sd.backtrace_line}.join("\n")
36
- message << "\n\n"
37
- message << "You can run again with --guess to make Cucumber be more smart about it\n" unless used_guess
38
- super(message)
39
- end
40
- end
41
-
42
- class TagExcess < StandardError
43
- def initialize(messages)
44
- super(messages.join("\n"))
45
- end
46
- end
47
-
48
- # This is the meaty part of Cucumber that ties everything together.
49
- class StepMother
50
- include Constantize
51
- include Formatter::Duration
52
- attr_writer :options, :visitor, :log
53
-
54
- def initialize
55
- @unsupported_programming_languages = []
56
- @programming_languages = []
57
- @language_map = {}
58
- @current_scenario = nil
59
- end
60
-
61
- def load_plain_text_features(feature_files)
62
- features = Ast::Features.new
63
-
64
- tag_counts = {}
65
- start = Time.new
66
- log.debug("Features:\n")
67
- feature_files.each do |f|
68
- feature_file = FeatureFile.new(f)
69
- feature = feature_file.parse(options, tag_counts)
70
- if feature
71
- features.add_feature(feature)
72
- log.debug(" * #{f}\n")
73
- end
74
- end
75
- duration = Time.now - start
76
- log.debug("Parsing feature files took #{format_duration(duration)}\n\n")
77
-
78
- check_tag_limits(tag_counts)
79
-
80
- features
81
- end
82
-
83
- def check_tag_limits(tag_counts)
84
- error_messages = []
85
- options[:tag_expression].limits.each do |tag_name, tag_limit|
86
- tag_locations = (tag_counts[tag_name] || [])
87
- tag_count = tag_locations.length
88
- if tag_count > tag_limit
89
- error = "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
90
- tag_locations.join("\n ")
91
- error_messages << error
92
- end
93
- end
94
- raise TagExcess.new(error_messages) if error_messages.any?
95
- end
96
-
97
- def load_code_files(step_def_files)
98
- log.debug("Code:\n")
99
- step_def_files.each do |step_def_file|
100
- load_code_file(step_def_file)
101
- end
102
- log.debug("\n")
103
- end
104
-
105
- def load_code_file(step_def_file)
106
- if programming_language = programming_language_for(step_def_file)
107
- log.debug(" * #{step_def_file}\n")
108
- programming_language.load_code_file(step_def_file)
109
- else
110
- log.debug(" * #{step_def_file} [NOT SUPPORTED]\n")
111
- end
112
- end
113
-
114
- # Loads and registers programming language implementation.
115
- # Instances are cached, so calling with the same argument
116
- # twice will return the same instance.
117
- #
118
- def load_programming_language(ext)
119
- return @language_map[ext] if @language_map[ext]
120
- programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language")
121
- programming_language = programming_language_class.new(self)
122
- @programming_languages << programming_language
123
- @language_map[ext] = programming_language
124
- programming_language
125
- end
126
-
127
- # Returns the options passed on the command line.
128
- def options
129
- @options ||= Cli::Options.new
130
- end
131
-
132
- def step_visited(step) #:nodoc:
133
- steps << step unless steps.index(step)
134
- end
135
-
136
- def steps(status = nil) #:nodoc:
137
- @steps ||= []
138
- if(status)
139
- @steps.select{|step| step.status == status}
140
- else
141
- @steps
142
- end
143
- end
144
-
145
- # Output +announcement+ alongside the formatted output.
146
- # This is an alternative to using Kernel#puts - it will display
147
- # nicer, and in all outputs (in case you use several formatters)
148
- #
149
- def announce(msg)
150
- msg.respond_to?(:join) ? @visitor.announce(msg.join("\n")) : @visitor.announce(msg.to_s)
151
- end
152
-
153
- # Suspends execution and prompts +question+ to the console (STDOUT).
154
- # An operator (manual tester) can then enter a line of text and hit
155
- # <ENTER>. The entered text is returned, and both +question+ and
156
- # the result is added to the output using #announce.
157
- #
158
- # If you want a beep to happen (to grab the manual tester's attention),
159
- # just prepend ASCII character 7 to the question:
160
- #
161
- # ask("#{7.chr}How many cukes are in the external system?")
162
- #
163
- # If that doesn't issue a beep, you can shell out to something else
164
- # that makes a sound before invoking #ask.
165
- #
166
- def ask(question, timeout_seconds)
167
- STDOUT.puts(question)
168
- STDOUT.flush
169
- announce(question)
170
-
171
- if(Cucumber::JRUBY)
172
- answer = jruby_gets(timeout_seconds)
173
- else
174
- answer = mri_gets(timeout_seconds)
175
- end
176
-
177
- if(answer)
178
- announce(answer)
179
- answer
180
- else
181
- raise("Waited for input for #{timeout_seconds} seconds, then timed out.")
182
- end
183
- end
184
-
185
- # Embed +file+ of MIME type +mime_type+ into the output. This may or may
186
- # not be ignored, depending on what kind of formatter(s) are active.
187
- #
188
- def embed(file, mime_type)
189
- @visitor.embed(file, mime_type)
190
- end
191
-
192
- def scenarios(status = nil) #:nodoc:
193
- @scenarios ||= []
194
- if(status)
195
- @scenarios.select{|scenario| scenario.status == status}
196
- else
197
- @scenarios
198
- end
199
- end
200
-
201
- def invoke(step_name, multiline_argument=nil)
202
- begin
203
- step_match(step_name).invoke(multiline_argument)
204
- rescue Exception => e
205
- e.nested! if Undefined === e
206
- raise e
207
- end
208
- end
209
-
210
- # Invokes a series of steps +steps_text+. Example:
211
- #
212
- # invoke(%Q{
213
- # Given I have 8 cukes in my belly
214
- # Then I should not be thirsty
215
- # })
216
- def invoke_steps(steps_text, i18n, file_colon_line)
217
- file, line = file_colon_line.split(':')
218
- parser = Gherkin::Parser::Parser.new(StepInvoker.new(self), true, 'steps')
219
- parser.parse(steps_text, file, line.to_i)
220
- end
221
-
222
- class StepInvoker
223
- def initialize(step_mother)
224
- @step_mother = step_mother
225
- end
226
-
227
- def step(statement, multiline_arg, result)
228
- cucumber_multiline_arg = case(multiline_arg)
229
- when Gherkin::Formatter::Model::PyString
230
- multiline_arg.value
231
- when Array
232
- Ast::Table.new(multiline_arg.map{|row| row.cells})
233
- else
234
- nil
235
- end
236
- @step_mother.invoke(*[statement.name, cucumber_multiline_arg].compact)
237
- end
238
-
239
- def eof
240
- end
241
- end
242
-
243
- # Returns a Cucumber::Ast::Table for +text_or_table+, which can either
244
- # be a String:
245
- #
246
- # table(%{
247
- # | account | description | amount |
248
- # | INT-100 | Taxi | 114 |
249
- # | CUC-101 | Peeler | 22 |
250
- # })
251
- #
252
- # or a 2D Array:
253
- #
254
- # table([
255
- # %w{ account description amount },
256
- # %w{ INT-100 Taxi 114 },
257
- # %w{ CUC-101 Peeler 22 }
258
- # ])
259
- #
260
- def table(text_or_table, file=nil, line_offset=0)
261
- if Array === text_or_table
262
- Ast::Table.new(text_or_table)
263
- else
264
- Ast::Table.parse(text_or_table, file, line_offset)
265
- end
266
- end
267
-
268
- # Returns a regular String for +string_with_triple_quotes+. Example:
269
- #
270
- # """
271
- # hello
272
- # world
273
- # """
274
- #
275
- # Is retured as: " hello\nworld"
276
- #
277
- def py_string(string_with_triple_quotes, file=nil, line_offset=0)
278
- Ast::PyString.parse(string_with_triple_quotes)
279
- end
280
-
281
- def step_match(step_name, name_to_report=nil) #:nodoc:
282
- matches = @programming_languages.map do |programming_language|
283
- programming_language.step_matches(step_name, name_to_report).to_a
284
- end.flatten
285
- raise Undefined.new(step_name) if matches.empty?
286
- matches = best_matches(step_name, matches) if matches.size > 1 && options[:guess]
287
- raise Ambiguous.new(step_name, matches, options[:guess]) if matches.size > 1
288
- matches[0]
289
- end
290
-
291
- def best_matches(step_name, step_matches) #:nodoc:
292
- no_groups = step_matches.select {|step_match| step_match.args.length == 0}
293
- max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
294
- top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
295
-
296
- if no_groups.any?
297
- longest_regexp_length = no_groups.map {|step_match| step_match.text_length }.max
298
- no_groups.select {|step_match| step_match.text_length == longest_regexp_length }
299
- elsif top_groups.any?
300
- shortest_capture_length = top_groups.map {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } }.min
301
- top_groups.select {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } == shortest_capture_length }
302
- else
303
- top_groups
304
- end
305
- end
306
-
307
- def unmatched_step_definitions
308
- @programming_languages.map do |programming_language|
309
- programming_language.unmatched_step_definitions
310
- end.flatten
311
- end
312
-
313
- def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
314
- load_programming_language('rb') if unknown_programming_language?
315
- @programming_languages.map do |programming_language|
316
- programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
317
- end.join("\n")
318
- end
319
-
320
- def unknown_programming_language?
321
- @programming_languages.empty?
322
- end
323
-
324
- def with_hooks(scenario, skip_hooks=false)
325
- around(scenario, skip_hooks) do
326
- before_and_after(scenario, skip_hooks) do
327
- yield scenario
328
- end
329
- end
330
- end
331
-
332
- def around(scenario, skip_hooks=false, &block) #:nodoc:
333
- unless skip_hooks
334
- @programming_languages.reverse.inject(block) do |blk, programming_language|
335
- proc do
336
- programming_language.around(scenario) do
337
- blk.call(scenario)
338
- end
339
- end
340
- end.call
341
- else
342
- yield
343
- end
344
- end
345
-
346
- def before_and_after(scenario, skip_hooks=false) #:nodoc:
347
- before(scenario) unless skip_hooks
348
- yield scenario
349
- after(scenario) unless skip_hooks
350
- scenario_visited(scenario)
351
- end
352
-
353
- def before(scenario) #:nodoc:
354
- return if options[:dry_run] || @current_scenario
355
- @current_scenario = scenario
356
- @programming_languages.each do |programming_language|
357
- programming_language.before(scenario)
358
- end
359
- end
360
-
361
- def after(scenario) #:nodoc:
362
- @current_scenario = nil
363
- return if options[:dry_run]
364
- @programming_languages.each do |programming_language|
365
- programming_language.after(scenario)
366
- end
367
- end
368
-
369
- def after_step #:nodoc:
370
- return if options[:dry_run]
371
- @programming_languages.each do |programming_language|
372
- programming_language.execute_after_step(@current_scenario)
373
- end
374
- end
375
-
376
- def after_configuration(configuration) #:nodoc
377
- @programming_languages.each do |programming_language|
378
- programming_language.after_configuration(configuration)
379
- end
380
- end
381
-
382
- private
383
-
384
- def programming_language_for(step_def_file) #:nodoc:
385
- if ext = File.extname(step_def_file)[1..-1]
386
- return nil if @unsupported_programming_languages.index(ext)
387
- begin
388
- load_programming_language(ext)
389
- rescue LoadError => e
390
- log.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n")
391
- @unsupported_programming_languages << ext
392
- nil
393
- end
394
- else
395
- nil
396
- end
397
- end
398
-
399
- def max_step_definition_length #:nodoc:
400
- @max_step_definition_length ||= step_definitions.map{|step_definition| step_definition.text_length}.max
401
- end
402
-
403
- def scenario_visited(scenario) #:nodoc:
404
- scenarios << scenario unless scenarios.index(scenario)
405
- end
406
-
407
- def log
408
- @log ||= Logger.new(STDOUT)
409
- end
410
-
411
- def mri_gets(timeout_seconds)
412
- begin
413
- Timeout.timeout(timeout_seconds) do
414
- STDIN.gets
415
- end
416
- rescue Timeout::Error => e
417
- nil
418
- end
419
- end
420
-
421
- def jruby_gets(timeout_seconds)
422
- answer = nil
423
- t = java.lang.Thread.new do
424
- answer = STDIN.gets
425
- end
426
- t.start
427
- t.join(timeout_seconds * 1000)
428
- answer
4
+ class StepMother < Runtime
5
+ def initialize(*args)
6
+ warn("StepMother has been deprecated and will be gently put to sleep before the next release. Please use Runtime instead. #{caller[0]}")
7
+ super
429
8
  end
430
9
  end
431
- end
10
+ end