cucumber 2.1.0 → 2.2.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 (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)