cucumber 2.0.0.beta.2 → 2.0.0.beta.3

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +25 -7
  3. data/cucumber.gemspec +1 -1
  4. data/features/docs/defining_steps/nested_steps.feature +0 -1
  5. data/features/docs/defining_steps/printing_messages.feature +1 -0
  6. data/features/docs/defining_steps/table_diffing.feature +9 -4
  7. data/features/docs/exception_in_after_step_hook.feature +1 -0
  8. data/features/docs/formatters/json_formatter.feature +51 -4
  9. data/features/docs/formatters/junit_formatter.feature +1 -0
  10. data/features/docs/gherkin/outlines.feature +4 -0
  11. data/features/docs/output_from_hooks.feature +128 -0
  12. data/features/docs/wire_protocol_table_diffing.feature +6 -2
  13. data/features/docs/writing_support_code/after_hooks.feature +56 -0
  14. data/lib/cucumber/cli/configuration.rb +0 -4
  15. data/lib/cucumber/cli/main.rb +0 -1
  16. data/lib/cucumber/cli/options.rb +0 -3
  17. data/lib/cucumber/formatter/console.rb +3 -1
  18. data/lib/cucumber/formatter/debug.rb +4 -0
  19. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +26 -3
  20. data/lib/cucumber/formatter/html.rb +6 -2
  21. data/lib/cucumber/formatter/usage.rb +1 -58
  22. data/lib/cucumber/mappings.rb +25 -7
  23. data/lib/cucumber/multiline_argument.rb +40 -82
  24. data/lib/cucumber/multiline_argument/data_table.rb +719 -0
  25. data/lib/cucumber/multiline_argument/doc_string.rb +10 -0
  26. data/lib/cucumber/platform.rb +1 -1
  27. data/lib/cucumber/rb_support/rb_world.rb +2 -4
  28. data/lib/cucumber/reports/legacy_formatter.rb +69 -22
  29. data/lib/cucumber/runtime.rb +0 -39
  30. data/lib/cucumber/runtime/for_programming_languages.rb +12 -10
  31. data/lib/cucumber/runtime/support_code.rb +11 -4
  32. data/lib/cucumber/wire_support/wire_protocol/requests.rb +2 -2
  33. data/spec/cucumber/formatter/pretty_spec.rb +5 -5
  34. data/spec/cucumber/mappings_spec.rb +137 -8
  35. data/spec/cucumber/multiline_argument/data_table_spec.rb +508 -0
  36. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +3 -3
  37. data/spec/cucumber/rb_support/snippet_spec.rb +1 -1
  38. data/spec/cucumber/runtime/for_programming_languages_spec.rb +16 -12
  39. metadata +13 -6
  40. data/lib/cucumber/runtime/features_loader.rb +0 -62
@@ -62,10 +62,6 @@ module Cucumber
62
62
  @options[:expand]
63
63
  end
64
64
 
65
- def dotcucumber
66
- @options[:dotcucumber]
67
- end
68
-
69
65
  def snippet_type
70
66
  @options[:snippet_type] || :regexp
71
67
  end
@@ -36,7 +36,6 @@ module Cucumber
36
36
  end
37
37
 
38
38
  runtime.run!
39
- runtime.write_stepdefs_json
40
39
  failure = runtime.results.failure? || Cucumber.wants_to_quit
41
40
  @kernel.exit(failure ? 1 : 0)
42
41
  rescue FileNotFoundException => e
@@ -223,9 +223,6 @@ module Cucumber
223
223
  opts.on("-x", "--expand", "Expand Scenario Outline Tables in output.") do
224
224
  @options[:expand] = true
225
225
  end
226
- opts.on("--dotcucumber DIR", "Write metadata to DIR") do |dir|
227
- @options[:dotcucumber] = dir
228
- end
229
226
  opts.on("--order TYPE[:SEED]", "Run examples in the specified order. Available types:",
230
227
  *<<-TEXT.split("\n")) do |order|
231
228
  [defined] Run scenarios in the order they were defined (default).
@@ -139,7 +139,9 @@ module Cucumber
139
139
 
140
140
  unknown_programming_language = runtime.unknown_programming_language?
141
141
  snippets = undefined.map do |step|
142
- step_name = Undefined === step.exception ? step.exception.step_name : step.name
142
+ # step_name = Undefined === step.exception ? step.exception.step_name : step.name
143
+ # TODO: This probably won't work for nested steps :( See above for old code.
144
+ step_name = step.name
143
145
  @runtime.snippet_text(step.actual_keyword, step_name, step.multiline_arg)
144
146
  end.compact.uniq
145
147
 
@@ -24,6 +24,10 @@ module Cucumber
24
24
  @indent += 2 if name.to_s =~ /^before/
25
25
  end
26
26
 
27
+ def puts(*args)
28
+ print("puts")
29
+ end
30
+
27
31
  private
28
32
 
29
33
  def print(text)
@@ -10,6 +10,8 @@ module Cucumber
10
10
  @gf = gherkin_formatter
11
11
  @print_empty_match = print_empty_match
12
12
  @options = options
13
+ @delayed_messages = []
14
+ @delayed_embeddings = []
13
15
  end
14
16
 
15
17
  def before_feature(feature)
@@ -19,10 +21,12 @@ module Cucumber
19
21
 
20
22
  def before_background(background)
21
23
  @outline = false
24
+ @before_steps = true
22
25
  @gf.background(background.gherkin_statement)
23
26
  end
24
27
 
25
28
  def before_feature_element(feature_element)
29
+ @before_steps = true
26
30
  case(feature_element)
27
31
  when Core::Ast::Scenario
28
32
  @outline = false
@@ -65,6 +69,8 @@ module Cucumber
65
69
  def before_step(step)
66
70
  unless @outline and @options[:expand]
67
71
  @gf.step(step.gherkin_statement)
72
+ pass_delayed_output
73
+ @before_steps = false
68
74
  else
69
75
  if @in_instantiated_scenario
70
76
  @current_step_hash = to_hash(step.gherkin_statement)
@@ -111,9 +117,11 @@ module Cucumber
111
117
  @current_step_hash['comments'],
112
118
  @current_step_hash['keyword'],
113
119
  step_match.format_args(),
114
- @current_step_hash['line'],
120
+ file_colon_line.split(':')[1].to_i,
115
121
  @current_step_hash['rows'],
116
122
  @current_step_hash['doc_string']))
123
+ pass_delayed_output
124
+ @before_steps = false
117
125
  @gf.match(@current_match)
118
126
  @gf.result(@current_result)
119
127
  end
@@ -159,11 +167,19 @@ module Cucumber
159
167
  if defined?(JRUBY_VERSION)
160
168
  data = data.to_java_bytes
161
169
  end
162
- @gf.embedding(mime_type, data)
170
+ unless @before_steps
171
+ @gf.embedding(mime_type, data)
172
+ else
173
+ @delayed_embeddings.push [mime_type, data]
174
+ end
163
175
  end
164
176
 
165
177
  def puts(message)
166
- @gf.write(message)
178
+ unless @before_steps
179
+ @gf.write(message)
180
+ else
181
+ @delayed_messages.push message
182
+ end
167
183
  end
168
184
 
169
185
  private
@@ -175,6 +191,13 @@ module Cucumber
175
191
  gherkin_statement.to_hash
176
192
  end
177
193
  end
194
+
195
+ def pass_delayed_output
196
+ @delayed_messages.each { |message| @gf.write(message) }
197
+ @delayed_embeddings.each { |embed_data| @gf.embedding(embed_data[0], embed_data[1]) }
198
+ @delayed_messages = []
199
+ @delayed_embeddings = []
200
+ end
178
201
  end
179
202
  end
180
203
  end
@@ -12,7 +12,7 @@ module Cucumber
12
12
  Cucumber::Core::Ast::Scenario => 'scenario',
13
13
  Cucumber::Core::Ast::ScenarioOutline => 'scenario outline'
14
14
  }
15
- AST_DATA_TABLE = Cucumber::Core::Ast::DataTable
15
+ AST_DATA_TABLE = Cucumber::Reports::Legacy::Ast::MultilineArg::DataTable
16
16
 
17
17
  include ERB::Util # for the #h method
18
18
  include Duration
@@ -618,8 +618,12 @@ module Cucumber
618
618
 
619
619
  def lines_around(file, line)
620
620
  if File.file?(file)
621
+ begin
621
622
  lines = File.open(file).read.split("\n")
622
- min = [0, line-3].max
623
+ rescue ArgumentError
624
+ return "# Couldn't get snippet for #{file}"
625
+ end
626
+ min = [0, line-3].max
623
627
  max = [line+1, lines.length-1].min
624
628
  selected_lines = []
625
629
  selected_lines.join("\n")
@@ -21,36 +21,6 @@ module Cucumber
21
21
  print_profile_information
22
22
  end
23
23
 
24
- def before_background(background)
25
- @outline = false
26
- end
27
-
28
- def before_feature_element(feature_element)
29
- case(feature_element)
30
- when Core::Ast::Scenario
31
- @outline = false
32
- when Core::Ast::ScenarioOutline
33
- @outline = true
34
- if @options[:expand]
35
- @in_instantiated_scenario = false
36
- end
37
- else
38
- raise "Bad type: #{feature_element.class}"
39
- end
40
- end
41
-
42
- def scenario_name(keyword, name, file_colon_line, source_indent)
43
- if @outline and @in_instantiated_scenario
44
- if @new_example_table
45
- @example_row = 1
46
- @new_example_table = false
47
- else
48
- @example_row += 1
49
- end
50
- @example_line = @current_example_rows[@example_row].to_hash['line']
51
- end
52
- end
53
-
54
24
  def before_step(step)
55
25
  @step = step
56
26
  @start_time = Time.now
@@ -65,30 +35,17 @@ module Cucumber
65
35
  unless step_definition.nil? # nil if it's from a scenario outline
66
36
  stepdef_key = StepDefKey.new(step_definition.regexp_source, step_definition.file_colon_line)
67
37
 
68
- file_colon_line = @step.file_colon_line
69
- if @outline and @in_instantiated_scenario
70
- file_colon_line = replace_line_number(@step.file_colon_line, @example_line)
71
- end
72
-
73
38
  @stepdef_to_match[stepdef_key] << {
74
39
  :keyword => keyword,
75
40
  :step_match => step_match,
76
41
  :status => status,
77
- :file_colon_line => file_colon_line,
42
+ :file_colon_line => @step.file_colon_line,
78
43
  :duration => @duration
79
44
  }
80
45
  end
81
46
  super
82
47
  end
83
48
 
84
- def before_examples(examples)
85
- if @options[:expand]
86
- @in_instantiated_scenario = true
87
- @new_example_table = true
88
- @current_example_rows = to_hash(examples.gherkin_statement)['rows']
89
- end
90
- end
91
-
92
49
  def print_summary(features)
93
50
  add_unused_stepdefs
94
51
  aggregate_info
@@ -176,20 +133,6 @@ module Cucumber
176
133
  @stepdef_to_match[stepdef_key] = []
177
134
  end
178
135
  end
179
-
180
- private
181
-
182
- def replace_line_number(file_colon_line, line)
183
- file_colon_line.split(':')[0] + ':' + line.to_s
184
- end
185
-
186
- def to_hash(gherkin_statement)
187
- if defined?(JRUBY_VERSION)
188
- gherkin_statement.toMap()
189
- else
190
- gherkin_statement.to_hash
191
- end
192
- end
193
136
  end
194
137
  end
195
138
  end
@@ -68,13 +68,13 @@ module Cucumber
68
68
 
69
69
  def map_test_case_hooks(mapper)
70
70
  ruby.hooks_for(:before, scenario).each do |hook|
71
- mapper.before do
72
- hook.invoke('Before', scenario)
71
+ mapper.before do |result|
72
+ hook.invoke('Before', scenario.with_result(result))
73
73
  end
74
74
  end
75
75
  ruby.hooks_for(:after, scenario).each do |hook|
76
- mapper.after do
77
- hook.invoke('After', scenario)
76
+ mapper.after do |result|
77
+ hook.invoke('After', scenario.with_result(result))
78
78
  end
79
79
  end
80
80
  ruby.hooks_for(:around, scenario).each do |hook|
@@ -86,9 +86,10 @@ module Cucumber
86
86
 
87
87
  # adapts our test_case to look like the Cucumber Runtime's old Scenario
88
88
  class TestCase
89
- def initialize(test_case, feature)
89
+ def initialize(test_case, feature, result = Core::Test::Result::Unknown.new)
90
90
  @test_case = test_case
91
91
  @feature = feature
92
+ @result = result
92
93
  end
93
94
 
94
95
  def accept_hook?(hook)
@@ -96,7 +97,7 @@ module Cucumber
96
97
  end
97
98
 
98
99
  def failed?
99
- warn("Calling failed? on a scenario is not currently supported in Cucumber 2.0. Please see https://github.com/cucumber/cucumber/issues/726")
100
+ @result.failed?
100
101
  end
101
102
 
102
103
  def language
@@ -111,6 +112,11 @@ module Cucumber
111
112
  @test_case.name
112
113
  end
113
114
 
115
+ def title
116
+ warn("deprecated: call #name instead")
117
+ name
118
+ end
119
+
114
120
  def source_tags
115
121
  #warn('deprecated: call #tags instead')
116
122
  tags
@@ -123,12 +129,24 @@ module Cucumber
123
129
  def tags
124
130
  @test_case.tags
125
131
  end
132
+
133
+ def outline?
134
+ false
135
+ end
136
+
137
+ def with_result(result)
138
+ self.class.new(@test_case, @feature, result)
139
+ end
126
140
  end
127
141
 
128
142
  class Scenario < TestCase
129
143
  end
130
144
 
131
145
  class ScenarioOutlineExample < TestCase
146
+ def outline?
147
+ true
148
+ end
149
+
132
150
  def scenario_outline
133
151
  self
134
152
  end
@@ -204,7 +222,7 @@ module Cucumber
204
222
 
205
223
  private
206
224
  def map_step(node, step_match)
207
- multiline_arg = MultilineArgument.from(node.multiline_arg)
225
+ multiline_arg = MultilineArgument.from_core(node.multiline_arg)
208
226
  mapper.map { step_match.invoke(multiline_arg) }
209
227
  end
210
228
 
@@ -1,104 +1,62 @@
1
1
  require 'delegate'
2
+ require 'cucumber/multiline_argument/data_table'
3
+ require 'cucumber/multiline_argument/doc_string'
4
+ require 'gherkin/rubify'
5
+
2
6
  module Cucumber
3
7
  module MultilineArgument
4
- def self.from(core_multiline_arg)
5
- Builder.new(core_multiline_arg).result
6
- end
7
-
8
- class Builder
9
- def initialize(multiline_arg)
10
- multiline_arg.describe_to self
11
- end
8
+ extend Gherkin::Rubify
12
9
 
13
- def doc_string(string, *args)
14
- @result = DocString.new(string)
10
+ class << self
11
+ def from_core(node)
12
+ builder.wrap(node)
15
13
  end
16
14
 
17
- def data_table(table, *args)
18
- @result = DataTable.new(table)
15
+ def from(argument, location=nil)
16
+ location ||= Core::Ast::Location.of_caller
17
+ argument = rubify(argument)
18
+ case argument
19
+ when String
20
+ doc_string(argument, 'text/plain', location)
21
+ when Array
22
+ location = location.on_line(argument.first.line..argument.last.line)
23
+ data_table(argument.map{ |row| row.cells }, location)
24
+ when DataTable, DocString, None
25
+ argument
26
+ when nil
27
+ None.new
28
+ else
29
+ raise ArgumentError, "Don't know how to convert #{argument.class} #{argument.inspect} into a MultilineArgument"
30
+ end
19
31
  end
20
32
 
21
- def result
22
- @result || None.new
33
+ def doc_string(argument, content_type, location)
34
+ builder.doc_string(Core::Ast::DocString.new(argument, content_type, location))
23
35
  end
24
- end
25
36
 
26
- class DocString < SimpleDelegator
27
- def append_to(array)
28
- array << self
37
+ def data_table(data, location)
38
+ builder.data_table(Core::Ast::DataTable.new(data, location))
29
39
  end
30
- end
31
40
 
32
- class DataTable < SimpleDelegator
33
- def append_to(array)
34
- array << self
35
- end
41
+ private
36
42
 
37
- def to_json(options)
38
- raw.to_json(options)
43
+ def builder
44
+ @builder ||= Builder.new
39
45
  end
40
46
 
41
- def diff!(other_table)
42
- other_table = ensure_table(other_table)
43
- other_table_cell_matrix = other_table.cell_matrix
44
-
45
- ensure_green!
46
-
47
- require_diff_lcs
48
- cell_matrix.extend(Diff::LCS)
49
-
50
- changes = cell_matrix.diff(other_table_cell_matrix).flatten
51
-
52
- return if changes.empty?
53
-
54
- inserted = 0
55
- missing = 0
56
-
57
- changes.each do |change|
58
- if(change.action == '-')
59
- missing_row_pos = change.position + inserted
60
- cell_matrix[missing_row_pos].each{|cell| cell.status = :undefined}
61
-
62
- missing += 1
63
- else # '+'
64
- inserted_row = change.element
65
- inserted_row.each{|cell| cell.status = :comment}
66
-
67
- insert_row_pos = change.position + missing
68
- cell_matrix.insert(insert_row_pos, inserted_row)
69
-
70
- inserted += 1
71
- end
47
+ class Builder
48
+ def wrap(node)
49
+ @result = None.new
50
+ node.describe_to(self)
51
+ @result
72
52
  end
73
53
 
74
- raise Different.new(self)
75
- end
76
-
77
- def ensure_green! #:nodoc:
78
- each_cell{|cell| cell.status = :passed}
79
- end
80
-
81
- private
82
-
83
- def ensure_table(table_or_array) #:nodoc:
84
- return table_or_array if DataTable === table_or_array
85
- DataTable.new(table_or_array)
86
- end
87
-
88
- def require_diff_lcs #:nodoc:
89
- begin
90
- require 'diff/lcs'
91
- rescue LoadError => e
92
- e.message << "\n Please gem install diff-lcs\n"
93
- raise e
54
+ def doc_string(node, *args)
55
+ @result = DocString.new(node)
94
56
  end
95
- end
96
57
 
97
- class Different < StandardError
98
- attr_reader :table
99
- def initialize(table)
100
- @table = table
101
- super("Tables were not identical")
58
+ def data_table(node, *args)
59
+ @result = DataTable.new(node)
102
60
  end
103
61
  end
104
62
  end