cucumber 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -0
  3. data/Gemfile +3 -1
  4. data/History.md +18 -2
  5. data/README.md +5 -1
  6. data/cucumber.gemspec +3 -4
  7. data/cucumber.yml +2 -2
  8. data/features/docs/api/run_cli_main_with_existing_runtime.feature +4 -7
  9. data/features/docs/defining_steps/skip_scenario.feature +6 -2
  10. data/features/docs/formatters/api_methods.feature +36 -0
  11. data/features/docs/profiles.feature +2 -2
  12. data/features/lib/step_definitions/profile_steps.rb +1 -1
  13. data/lib/cucumber.rb +11 -4
  14. data/lib/cucumber/cli/configuration.rb +2 -2
  15. data/lib/cucumber/cli/options.rb +2 -2
  16. data/lib/cucumber/configuration.rb +25 -3
  17. data/lib/cucumber/deprecate.rb +29 -0
  18. data/lib/cucumber/filters/activate_steps.rb +39 -5
  19. data/lib/cucumber/formatter/console.rb +4 -4
  20. data/lib/cucumber/formatter/io.rb +1 -1
  21. data/lib/cucumber/formatter/legacy_api/adapter.rb +30 -30
  22. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +4 -9
  23. data/lib/cucumber/platform.rb +2 -7
  24. data/lib/cucumber/rb_support/rb_language.rb +72 -26
  25. data/lib/cucumber/rb_support/rb_step_definition.rb +2 -2
  26. data/lib/cucumber/rb_support/rb_world.rb +6 -1
  27. data/lib/cucumber/rb_support/snippet.rb +21 -0
  28. data/lib/cucumber/running_test_case.rb +5 -1
  29. data/lib/cucumber/runtime.rb +11 -15
  30. data/lib/cucumber/runtime/support_code.rb +20 -128
  31. data/lib/cucumber/step_argument.rb +25 -0
  32. data/lib/cucumber/step_match.rb +6 -12
  33. data/lib/cucumber/step_match_search.rb +67 -0
  34. data/lib/cucumber/version +1 -0
  35. data/spec/cucumber/configuration_spec.rb +3 -2
  36. data/spec/cucumber/filters/activate_steps_spec.rb +95 -3
  37. data/spec/cucumber/formatter/html_spec.rb +1 -1
  38. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +55 -28
  39. data/spec/cucumber/formatter/pretty_spec.rb +2 -2
  40. data/spec/cucumber/formatter/spec_helper.rb +22 -12
  41. data/spec/cucumber/rb_support/rb_language_spec.rb +9 -45
  42. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +2 -2
  43. data/spec/cucumber/rb_support/rb_world_spec.rb +47 -0
  44. data/spec/cucumber/runtime/for_programming_languages_spec.rb +1 -1
  45. data/spec/cucumber/runtime/support_code_spec.rb +4 -111
  46. data/spec/cucumber/step_argument_spec.rb +18 -0
  47. data/spec/cucumber/step_match_search_spec.rb +122 -0
  48. data/spec/cucumber/step_match_spec.rb +8 -2
  49. data/spec/cucumber/world/pending_spec.rb +2 -1
  50. data/spec/cucumber_spec.rb +39 -0
  51. metadata +45 -50
  52. data/features/docs/wire_protocol/erb_configuration.feature +0 -56
  53. data/features/docs/wire_protocol/handle_unexpected_response.feature +0 -30
  54. data/features/docs/wire_protocol/invoke_message.feature +0 -216
  55. data/features/docs/wire_protocol/readme.md +0 -26
  56. data/features/docs/wire_protocol/snippets_message.feature +0 -51
  57. data/features/docs/wire_protocol/step_matches_message.feature +0 -81
  58. data/features/docs/wire_protocol/table_diffing.feature +0 -126
  59. data/features/docs/wire_protocol/tags.feature +0 -87
  60. data/features/docs/wire_protocol/timeouts.feature +0 -64
  61. data/lib/cucumber/events/bus.rb +0 -86
  62. data/lib/cucumber/gherkin/formatter/argument.rb +0 -17
  63. data/lib/cucumber/gherkin/formatter/hashable.rb +0 -27
  64. data/lib/cucumber/language_support.rb +0 -30
  65. data/lib/cucumber/language_support/language_methods.rb +0 -72
  66. data/lib/cucumber/rb_support/regexp_argument_matcher.rb +0 -21
  67. data/lib/cucumber/wire_support/configuration.rb +0 -38
  68. data/lib/cucumber/wire_support/connection.rb +0 -61
  69. data/lib/cucumber/wire_support/request_handler.rb +0 -32
  70. data/lib/cucumber/wire_support/wire_exception.rb +0 -32
  71. data/lib/cucumber/wire_support/wire_language.rb +0 -68
  72. data/lib/cucumber/wire_support/wire_packet.rb +0 -34
  73. data/lib/cucumber/wire_support/wire_protocol.rb +0 -43
  74. data/lib/cucumber/wire_support/wire_protocol/requests.rb +0 -133
  75. data/lib/cucumber/wire_support/wire_step_definition.rb +0 -21
  76. data/spec/cucumber/events/bus_spec.rb +0 -94
  77. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +0 -22
  78. data/spec/cucumber/wire_support/configuration_spec.rb +0 -64
  79. data/spec/cucumber/wire_support/connection_spec.rb +0 -64
  80. data/spec/cucumber/wire_support/wire_exception_spec.rb +0 -50
  81. data/spec/cucumber/wire_support/wire_language_spec.rb +0 -46
  82. data/spec/cucumber/wire_support/wire_packet_spec.rb +0 -44
@@ -19,7 +19,7 @@ module Cucumber
19
19
  def ensure_file(path, name)
20
20
  raise "You *must* specify --out FILE for the #{name} formatter" unless String === path
21
21
  raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
22
- ensure_io(path, name)
22
+ ensure_io(path)
23
23
  end
24
24
 
25
25
  def ensure_dir(path, name)
@@ -9,9 +9,14 @@ module Cucumber
9
9
  module Formatter
10
10
  module LegacyApi
11
11
 
12
- Adapter = Struct.new(:formatter, :results, :support_code, :config) do
12
+ Adapter = Struct.new(:formatter, :results, :config) do
13
13
  extend Forwardable
14
14
 
15
+ def initialize(*)
16
+ super
17
+ @matches = collect_matches
18
+ end
19
+
15
20
  def_delegators :formatter,
16
21
  :ask
17
22
 
@@ -51,7 +56,7 @@ module Cucumber
51
56
  private
52
57
 
53
58
  def printer
54
- @printer ||= FeaturesPrinter.new(formatter, results, support_code, config).before
59
+ @printer ||= FeaturesPrinter.new(formatter, results, config, @matches).before
55
60
  end
56
61
 
57
62
  def record_test_case_result(test_case, result)
@@ -59,8 +64,16 @@ module Cucumber
59
64
  results.scenario_visited(scenario)
60
65
  end
61
66
 
67
+ def collect_matches
68
+ result = {}
69
+ config.on_event(:step_match) do |event|
70
+ result[event.test_step.source.last] = event.step_match
71
+ end
72
+ result
73
+ end
74
+
62
75
  require 'cucumber/core/test/timer'
63
- FeaturesPrinter = Struct.new(:formatter, :results, :support_code, :config) do
76
+ FeaturesPrinter = Struct.new(:formatter, :results, :config, :matches) do
64
77
  extend Forwardable
65
78
 
66
79
  def before
@@ -89,7 +102,7 @@ module Cucumber
89
102
  def feature(node, *)
90
103
  if node != @current_feature
91
104
  @child.after if @child
92
- @child = FeaturePrinter.new(formatter, results, support_code, config, node).before
105
+ @child = FeaturePrinter.new(formatter, results, matches, config, node).before
93
106
  @current_feature = node
94
107
  end
95
108
  end
@@ -125,6 +138,7 @@ module Cucumber
125
138
  def timer
126
139
  @timer ||= Cucumber::Core::Test::Timer.new
127
140
  end
141
+
128
142
  end
129
143
 
130
144
  module TestCaseSource
@@ -173,9 +187,9 @@ module Cucumber
173
187
 
174
188
  require 'ostruct'
175
189
  class StepSource < OpenStruct
176
- def build_step_invocation(indent, support_code, config, messages, embeddings)
190
+ def build_step_invocation(indent, matches, config, messages, embeddings)
177
191
  step_result.step_invocation(
178
- step_match(support_code),
192
+ matches.fetch(step) { NoStepMatch.new(step, step.name) },
179
193
  step,
180
194
  indent,
181
195
  background,
@@ -184,14 +198,6 @@ module Cucumber
184
198
  embeddings
185
199
  )
186
200
  end
187
-
188
- private
189
-
190
- def step_match(support_code)
191
- support_code.step_match(step.name)
192
- rescue Cucumber::Undefined
193
- NoStepMatch.new(step, step.name)
194
- end
195
201
  end
196
202
 
197
203
  end
@@ -203,7 +209,7 @@ module Cucumber
203
209
  end
204
210
  end
205
211
 
206
- FeaturePrinter = Struct.new(:formatter, :results, :support_code, :config, :node) do
212
+ FeaturePrinter = Struct.new(:formatter, :results, :matches, :config, :node) do
207
213
 
208
214
  def before
209
215
  formatter.before_feature(node)
@@ -251,7 +257,7 @@ module Cucumber
251
257
  @delayed_messages = []
252
258
  @delayed_embeddings = []
253
259
 
254
- @child.after_test_case if @child
260
+ @child.after_test_case(test_case, test_case_result) if @child
255
261
  @previous_test_case_background = @current_test_case_background
256
262
  @previous_test_case_scenario_outline = current_test_step_source && current_test_step_source.scenario_outline
257
263
  end
@@ -357,14 +363,14 @@ module Cucumber
357
363
 
358
364
  if @failed_hidden_background_step
359
365
  indent = Indent.new(@child.node)
360
- step_invocation = @failed_hidden_background_step.build_step_invocation(indent, support_code, config, messages = [], embeddings = [])
366
+ step_invocation = @failed_hidden_background_step.build_step_invocation(indent, matches, config, messages = [], embeddings = [])
361
367
  @child.step_invocation(step_invocation, @failed_hidden_background_step)
362
368
  @failed_hidden_background_step = nil
363
369
  end
364
370
 
365
371
  unless @last_step == current_test_step_source.step
366
372
  indent ||= Indent.new(@child.node)
367
- step_invocation = current_test_step_source.build_step_invocation(indent, support_code, config, @delayed_messages, @delayed_embeddings)
373
+ step_invocation = current_test_step_source.build_step_invocation(indent, matches, config, @delayed_messages, @delayed_embeddings)
368
374
  results.step_visited step_invocation
369
375
  @child.step_invocation(step_invocation, current_test_step_source)
370
376
  @last_step = current_test_step_source.step
@@ -523,23 +529,21 @@ module Cucumber
523
529
  def step_invocation(step_invocation, source)
524
530
  @child ||= StepsPrinter.new(formatter).before
525
531
  @child.step_invocation step_invocation
526
- @last_step_result = source.step_result
527
532
  end
528
533
 
529
534
  def after_step_hook(result)
530
535
  result.accept formatter
531
536
  end
532
537
 
533
- def after_test_case(*args)
538
+ def after_test_case(test_case, result)
539
+ @test_case_result = result
534
540
  after
535
541
  end
536
542
 
537
543
  def after
538
544
  return if @done
539
545
  @child.after if @child
540
- # TODO - the last step result might not accurately reflect the
541
- # overall scenario result.
542
- scenario = last_step_result.scenario(node.name, node.location)
546
+ scenario = LegacyResultBuilder.new(@test_case_result).scenario(node.name, node.location)
543
547
  after_hook_results.accept(formatter)
544
548
  formatter.after_feature_element(scenario)
545
549
  @done = true
@@ -548,10 +552,6 @@ module Cucumber
548
552
 
549
553
  private
550
554
 
551
- def last_step_result
552
- @last_step_result || LegacyResultBuilder.new(Core::Test::Result::Unknown.new)
553
- end
554
-
555
555
  def indent
556
556
  @indent ||= Indent.new(node)
557
557
  end
@@ -610,8 +610,8 @@ module Cucumber
610
610
  @child.examples_table_row(node, before_hook_results)
611
611
  end
612
612
 
613
- def after_test_case
614
- @child.after_test_case
613
+ def after_test_case(test_case, result)
614
+ @child.after_test_case(test_case, result)
615
615
  end
616
616
 
617
617
  def after
@@ -712,7 +712,7 @@ module Cucumber
712
712
  end
713
713
 
714
714
  def after_test_case(*args)
715
- @child.after_test_case
715
+ @child.after_test_case(*args)
716
716
  end
717
717
 
718
718
  def after
@@ -11,11 +11,10 @@ module Cucumber
11
11
  end
12
12
 
13
13
  def snippet_text(step_keyword, step_name, multiline_arg) #:nodoc:
14
- support_code.snippet_text(Cucumber::Gherkin::I18n.code_keyword_for(step_keyword).strip, step_name, multiline_arg)
15
- end
16
-
17
- def unknown_programming_language?
18
- support_code.unknown_programming_language?
14
+ keyword = Cucumber::Gherkin::I18n.code_keyword_for(step_keyword).strip
15
+ configuration.snippet_generators.map { |generator|
16
+ generator.call(keyword, step_name, multiline_arg, configuration.snippet_type)
17
+ }.join("\n")
19
18
  end
20
19
 
21
20
  def scenarios(status = nil)
@@ -25,10 +24,6 @@ module Cucumber
25
24
  def steps(status = nil)
26
25
  results.steps(status)
27
26
  end
28
-
29
- def step_match(step_name, name_to_report=nil)
30
- support_code.step_match(step_name, name_to_report)
31
- end
32
27
  end
33
28
 
34
29
  end
@@ -1,22 +1,17 @@
1
1
  # Detect the platform we're running on so we can tweak behaviour
2
2
  # in various places.
3
3
  require 'rbconfig'
4
+ require 'cucumber/core/platform'
4
5
 
5
6
  module Cucumber
6
7
  unless defined?(Cucumber::VERSION)
7
- VERSION = '2.1.0'
8
+ VERSION = File.read(File.expand_path("../version", __FILE__))
8
9
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
10
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
- JRUBY = defined?(JRUBY_VERSION)
11
- WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
12
- OS_X = RbConfig::CONFIG['host_os'] =~ /darwin/
13
- WINDOWS_MRI = WINDOWS && !JRUBY
14
11
  RAILS = defined?(Rails)
15
12
  RUBY_BINARY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
16
13
  RUBY_2_2 = RUBY_VERSION =~ /^2\.2/
17
14
  RUBY_2_1 = RUBY_VERSION =~ /^2\.1/
18
- RUBY_2_0 = RUBY_VERSION =~ /^2\.0/
19
- RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
20
15
 
21
16
  class << self
22
17
  attr_accessor :use_full_backtrace
@@ -7,6 +7,8 @@ require 'cucumber/rb_support/rb_transform'
7
7
  require 'cucumber/rb_support/snippet'
8
8
  require 'cucumber/gherkin/i18n'
9
9
  require 'multi_test'
10
+ require 'cucumber/step_match'
11
+ require 'cucumber/step_definition_light'
10
12
 
11
13
  module Cucumber
12
14
  module RbSupport
@@ -32,7 +34,6 @@ module Cucumber
32
34
 
33
35
  # The Ruby implementation of the programming language API.
34
36
  class RbLanguage
35
- include LanguageSupport::LanguageMethods
36
37
  attr_reader :current_world,
37
38
  :step_definitions
38
39
 
@@ -44,26 +45,21 @@ module Cucumber
44
45
  RbDsl.alias_adverb(adverb.strip)
45
46
  end
46
47
 
47
- def initialize(runtime)
48
+ def initialize(runtime, configuration)
48
49
  @runtime = runtime
49
50
  @step_definitions = []
50
51
  RbDsl.rb_language = self
51
52
  @world_proc = @world_modules = nil
53
+ configuration.register_snippet_generator(Snippet::Generator.new)
52
54
  end
53
55
 
54
- def step_matches(name_to_match, name_to_format)
55
- @step_definitions.map do |step_definition|
56
- if(arguments = step_definition.arguments_from(name_to_match))
57
- StepMatch.new(step_definition, name_to_match, name_to_format, arguments)
58
- else
59
- nil
56
+ def step_matches(name_to_match)
57
+ @step_definitions.reduce([]) { |result, step_definition|
58
+ if (arguments = step_definition.arguments_from(name_to_match))
59
+ result << StepMatch.new(step_definition, name_to_match, arguments)
60
60
  end
61
- end.compact
62
- end
63
-
64
- def snippet_text(code_keyword, step_name, multiline_arg, snippet_type = :regexp)
65
- snippet_class = typed_snippet_class(snippet_type)
66
- snippet_class.new(code_keyword, step_name, multiline_arg).to_s
61
+ result
62
+ }
67
63
  end
68
64
 
69
65
  def begin_rb_scenario(scenario)
@@ -96,6 +92,7 @@ module Cucumber
96
92
  end
97
93
 
98
94
  def load_code_file(code_file)
95
+ return unless File.extname(code_file) == ".rb"
99
96
  load File.expand_path(code_file) # This will cause self.add_step_definition, self.add_hook, and self.add_transform to be called from RbDsl
100
97
  end
101
98
 
@@ -107,8 +104,67 @@ module Cucumber
107
104
  @current_world = nil
108
105
  end
109
106
 
107
+ def after_configuration(configuration)
108
+ hooks[:after_configuration].each do |hook|
109
+ hook.invoke('AfterConfiguration', configuration)
110
+ end
111
+ end
112
+
113
+ def execute_transforms(args)
114
+ args.map do |arg|
115
+ matching_transform = transforms.detect {|transform| transform.match(arg) }
116
+ matching_transform ? matching_transform.invoke(arg) : arg
117
+ end
118
+ end
119
+
120
+ def add_hook(phase, hook)
121
+ hooks[phase.to_sym] << hook
122
+ hook
123
+ end
124
+
125
+ def clear_hooks
126
+ @hooks = nil
127
+ end
128
+
129
+ def add_transform(transform)
130
+ transforms.unshift transform
131
+ transform
132
+ end
133
+
134
+ def hooks_for(phase, scenario) #:nodoc:
135
+ hooks[phase.to_sym].select{|hook| scenario.accept_hook?(hook)}
136
+ end
137
+
138
+ def unmatched_step_definitions
139
+ available_step_definition_hash.keys - invoked_step_definition_hash.keys
140
+ end
141
+
142
+ def available_step_definition(regexp_source, file_colon_line)
143
+ available_step_definition_hash[StepDefinitionLight.new(regexp_source, file_colon_line)] = nil
144
+ end
145
+
146
+ def invoked_step_definition(regexp_source, file_colon_line)
147
+ invoked_step_definition_hash[StepDefinitionLight.new(regexp_source, file_colon_line)] = nil
148
+ end
149
+
110
150
  private
111
151
 
152
+ def available_step_definition_hash
153
+ @available_step_definition_hash ||= {}
154
+ end
155
+
156
+ def invoked_step_definition_hash
157
+ @invoked_step_definition_hash ||= {}
158
+ end
159
+
160
+ def hooks
161
+ @hooks ||= Hash.new{|h,k| h[k] = []}
162
+ end
163
+
164
+ def transforms
165
+ @transforms ||= []
166
+ end
167
+
112
168
  def create_world
113
169
  if(@world_proc)
114
170
  @current_world = @world_proc.call
@@ -145,19 +201,9 @@ module Cucumber
145
201
  end
146
202
  end
147
203
 
148
- SNIPPET_TYPES = {
149
- :regexp => Snippet::Regexp,
150
- :classic => Snippet::Classic,
151
- :percent => Snippet::Percent
152
- }
153
-
154
- def typed_snippet_class(type)
155
- SNIPPET_TYPES.fetch(type || :regexp)
156
- end
157
-
158
204
  def self.cli_snippet_type_options
159
- SNIPPET_TYPES.keys.sort_by(&:to_s).map do |type|
160
- SNIPPET_TYPES[type].cli_option_string(type)
205
+ Snippet::SNIPPET_TYPES.keys.sort_by(&:to_s).map do |type|
206
+ Snippet::SNIPPET_TYPES[type].cli_option_string(type)
161
207
  end
162
208
  end
163
209
  end
@@ -1,6 +1,6 @@
1
1
  require 'cucumber/step_match'
2
+ require 'cucumber/step_argument'
2
3
  require 'cucumber/core_ext/string'
3
- require 'cucumber/rb_support/regexp_argument_matcher'
4
4
 
5
5
  module Cucumber
6
6
  module RbSupport
@@ -91,7 +91,7 @@ module Cucumber
91
91
  end
92
92
 
93
93
  def arguments_from(step_name)
94
- args = RegexpArgumentMatcher.arguments_from(@regexp, step_name)
94
+ args = StepArgument.arguments_from(@regexp, step_name)
95
95
  @rb_language.invoked_step_definition(regexp_source, location) if args
96
96
  args
97
97
  end
@@ -13,7 +13,7 @@ module Cucumber
13
13
 
14
14
  # Call a Transform with a string from another Transform definition
15
15
  def Transform(arg)
16
- rb = @__cucumber_runtime.load_programming_language('rb')
16
+ rb = @__cucumber_runtime.support_code.ruby
17
17
  rb.execute_transforms([arg]).first
18
18
  end
19
19
 
@@ -94,6 +94,11 @@ module Cucumber
94
94
  #
95
95
  # If you'd prefer to see the message immediately, call {Kernel.puts} instead.
96
96
  def puts(*messages)
97
+ # Even though they won't be output until later, converting the messages to
98
+ # strings right away will protect them from modifications to their original
99
+ # objects in the mean time
100
+ messages.collect! { |message| "#{message}" }
101
+
97
102
  @__cucumber_runtime.puts(*messages)
98
103
  end
99
104
 
@@ -4,6 +4,21 @@ module Cucumber
4
4
 
5
5
  ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
6
6
 
7
+ class Generator
8
+ def self.register_on(configuration)
9
+ configuration.snippet_generators << new
10
+ end
11
+
12
+ def call(code_keyword, step_name, multiline_arg, snippet_type = :regexp)
13
+ snippet_class = typed_snippet_class(snippet_type)
14
+ snippet_class.new(code_keyword, step_name, multiline_arg).to_s
15
+ end
16
+
17
+ def typed_snippet_class(type)
18
+ SNIPPET_TYPES.fetch(type || :regexp)
19
+ end
20
+ end
21
+
7
22
  class BaseSnippet
8
23
 
9
24
  def initialize(code_keyword, pattern, multiline_argument)
@@ -91,6 +106,12 @@ module Cucumber
91
106
  end
92
107
  end
93
108
 
109
+ SNIPPET_TYPES = {
110
+ :regexp => Regexp,
111
+ :classic => Classic,
112
+ :percent => Percent
113
+ }
114
+
94
115
  module MultilineArgumentSnippet
95
116
 
96
117
  def self.new(multiline_argument)