cucumber 2.0.0.beta.3 → 2.0.0.beta.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +20 -3
  3. data/cucumber.gemspec +2 -1
  4. data/examples/tcl/features/step_definitions/fib_steps.rb +1 -1
  5. data/features/docs/extending_cucumber/custom_formatter.feature +65 -7
  6. data/features/docs/formatters/debug_formatter.feature +24 -17
  7. data/features/docs/formatters/pretty_formatter.feature +42 -0
  8. data/features/docs/formatters/rerun_formatter.feature +3 -2
  9. data/lib/cucumber/cli/configuration.rb +3 -7
  10. data/lib/cucumber/cli/main.rb +1 -1
  11. data/lib/cucumber/{runtime → filters}/gated_receiver.rb +5 -1
  12. data/lib/cucumber/filters/quit.rb +24 -0
  13. data/lib/cucumber/filters/randomizer.rb +36 -0
  14. data/lib/cucumber/filters/tag_limits.rb +40 -0
  15. data/lib/cucumber/{runtime → filters}/tag_limits/test_case_index.rb +4 -2
  16. data/lib/cucumber/{runtime → filters}/tag_limits/verifier.rb +4 -2
  17. data/lib/cucumber/formatter/console.rb +2 -2
  18. data/lib/cucumber/formatter/debug.rb +1 -8
  19. data/lib/cucumber/formatter/fanout.rb +27 -0
  20. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +1 -3
  21. data/lib/cucumber/formatter/html.rb +12 -4
  22. data/lib/cucumber/formatter/ignore_missing_messages.rb +20 -0
  23. data/lib/cucumber/formatter/junit.rb +2 -2
  24. data/lib/cucumber/formatter/legacy_api/adapter.rb +1008 -0
  25. data/lib/cucumber/formatter/legacy_api/ast.rb +374 -0
  26. data/lib/cucumber/formatter/legacy_api/results.rb +51 -0
  27. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +30 -0
  28. data/lib/cucumber/formatter/pretty.rb +4 -0
  29. data/lib/cucumber/formatter/rerun.rb +14 -88
  30. data/lib/cucumber/language_support/language_methods.rb +0 -54
  31. data/lib/cucumber/multiline_argument/data_table.rb +3 -4
  32. data/lib/cucumber/platform.rb +1 -1
  33. data/lib/cucumber/runtime.rb +41 -107
  34. data/spec/cucumber/{runtime → filters}/gated_receiver_spec.rb +3 -3
  35. data/spec/cucumber/{runtime → filters}/tag_limits/test_case_index_spec.rb +3 -3
  36. data/spec/cucumber/{runtime → filters}/tag_limits/verifier_spec.rb +4 -4
  37. data/spec/cucumber/{runtime/tag_limits/filter_spec.rb → filters/tag_limits_spec.rb} +6 -6
  38. data/spec/cucumber/formatter/debug_spec.rb +39 -530
  39. data/spec/cucumber/formatter/html_spec.rb +56 -0
  40. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +1902 -0
  41. data/spec/cucumber/formatter/pretty_spec.rb +128 -0
  42. data/spec/cucumber/formatter/rerun_spec.rb +106 -0
  43. data/spec/cucumber/formatter/spec_helper.rb +6 -2
  44. data/spec/cucumber/rb_support/rb_language_spec.rb +2 -2
  45. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +1 -1
  46. data/spec/cucumber/runtime_spec.rb +1 -5
  47. data/spec/spec_helper.rb +2 -0
  48. metadata +44 -29
  49. data/features/docs/extending_cucumber/formatter_callbacks.feature +0 -370
  50. data/features/docs/output_from_hooks.feature +0 -128
  51. data/lib/cucumber/reports/legacy_formatter.rb +0 -1349
  52. data/lib/cucumber/runtime/results.rb +0 -64
  53. data/lib/cucumber/runtime/tag_limits.rb +0 -15
  54. data/lib/cucumber/runtime/tag_limits/filter.rb +0 -31
  55. data/spec/cucumber/reports/legacy_formatter_spec.rb +0 -1860
  56. data/spec/cucumber/runtime/results_spec.rb +0 -88
@@ -72,6 +72,7 @@ module Cucumber
72
72
  end
73
73
 
74
74
  def after_feature_element(feature_element)
75
+ print_messages
75
76
  @io.puts
76
77
  @io.flush
77
78
  end
@@ -83,6 +84,7 @@ module Cucumber
83
84
  end
84
85
 
85
86
  def after_background(background)
87
+ print_messages
86
88
  @in_background = nil
87
89
  @io.puts
88
90
  @io.flush
@@ -125,6 +127,7 @@ module Cucumber
125
127
  def before_step(step)
126
128
  @current_step = step
127
129
  @indent = 6
130
+ print_messages
128
131
  end
129
132
 
130
133
  def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
@@ -161,6 +164,7 @@ module Cucumber
161
164
 
162
165
  def exception(exception, status)
163
166
  return if @hide_this_step
167
+ print_messages
164
168
  print_exception(exception, status, @indent)
165
169
  @io.flush
166
170
  end
@@ -2,106 +2,32 @@ require 'cucumber/formatter/io'
2
2
 
3
3
  module Cucumber
4
4
  module Formatter
5
- # The formatter used for <tt>--format rerun</tt>
6
- #
7
- # This formatter keeps track of all failing features and print out their location.
8
- # Example:
9
- #
10
- # features/foo.feature:34 features/bar.feature:11:76:81
11
- #
12
- # This formatter is used by AutoTest - it will use the output to decide what
13
- # to run the next time, simply passing the output string on the command line.
14
- #
15
5
  class Rerun
16
- include Io
6
+ include Formatter::Io
17
7
 
18
8
  def initialize(runtime, path_or_io, options)
19
9
  @io = ensure_io(path_or_io, "rerun")
20
- @options = options
21
- @file_names = []
22
- @file_colon_lines = Hash.new{|h,k| h[k] = []}
10
+ @failures = {}
23
11
  end
24
12
 
25
- def before_feature(feature_element)
26
- @lines = []
27
- @file = feature_element.file
28
- # See https://github.com/cucumber/cucumber/issues/629
29
- if @file.include?(' ')
30
- warn("Filenames with spaces like '#{@file}' cause unexpected behaviour from the rerun formatter.")
31
- end
13
+ def after_test_case(test_case, result)
14
+ return if result.passed?
15
+ @failures[test_case.location.file] ||= []
16
+ @failures[test_case.location.file] << test_case.location.line
32
17
  end
33
18
 
34
- def after_feature(*)
35
- unless @lines.empty?
36
- after_first_time do
37
- @io.print ' '
38
- end
39
- @io.print "#{@file}:#{@lines.join(':')}"
40
- @io.flush
41
- end
19
+ def done
20
+ return if @failures.empty?
21
+ @io.print file_failures.join(' ')
42
22
  end
43
23
 
44
- def after_features(features)
45
- @io.close
24
+ [:before_test_case, :before_test_step, :after_test_step].each do |method|
25
+ define_method(method) { |*| }
46
26
  end
47
27
 
48
- def before_feature_element(feature_element)
49
- @rerun = false
50
- end
51
-
52
- def after_feature_element(feature_element)
53
- if (@rerun || feature_element.failed? || feature_element.status == :skipped) && !(Cucumber::Reports::Legacy::Ast::ScenarioOutline === feature_element)
54
- @lines << feature_element.line
55
- end
56
- end
57
-
58
- def after_table_row(table_row)
59
- return unless @in_examples and Cucumber::Reports::LegacyExampleTableRow === table_row
60
- unless @header_row
61
- if table_row.failed? || table_row.status == :skipped
62
- @rerun = true
63
- @lines << table_row.line
64
- end
65
- end
66
-
67
- @header_row = false if @header_row
68
- end
69
-
70
- def before_examples(*args)
71
- @header_row = true
72
- @in_examples = true
73
- @current_example_line = nil
74
- end
75
-
76
- def after_examples(*args)
77
- @in_examples = false
78
- if @current_example_line and @rerun
79
- @lines << @current_example_line
80
- end
81
- end
82
-
83
- def before_table_row(table_row)
84
- return unless @in_examples
85
- end
86
-
87
- def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
88
- @rerun = true if [:failed, :pending, :undefined].index(status)
89
- end
90
-
91
- def scenario_name(keyword, name, file_colon_line, source_indent)
92
- return unless @in_examples
93
- if @current_example_line and @rerun
94
- @lines << @current_example_line
95
- end
96
- @rerun = false
97
- @current_example_line = file_colon_line.split(':')[1]
98
- end
99
-
100
- private
101
-
102
- def after_first_time
103
- yield if @not_first_time
104
- @not_first_time = true
28
+ private
29
+ def file_failures
30
+ @failures.map { |file, lines| [file, lines].join(':') }
105
31
  end
106
32
  end
107
33
  end
@@ -4,21 +4,6 @@ require 'cucumber/step_definition_light'
4
4
  module Cucumber
5
5
  module LanguageSupport
6
6
  module LanguageMethods
7
- def around(scenario)
8
- execute_around(scenario) do
9
- yield
10
- end
11
- end
12
-
13
- def before(scenario)
14
- begin_scenario(scenario)
15
- execute_before(scenario)
16
- end
17
-
18
- def after(scenario)
19
- execute_after(scenario)
20
- end_scenario
21
- end
22
7
 
23
8
  def after_configuration(configuration)
24
9
  hooks[:after_configuration].each do |hook|
@@ -26,12 +11,6 @@ module Cucumber
26
11
  end
27
12
  end
28
13
 
29
- def execute_after_step(scenario)
30
- hooks_for(:after_step, scenario).each do |hook|
31
- invoke(hook, 'AfterStep', scenario)
32
- end
33
- end
34
-
35
14
  def execute_transforms(args)
36
15
  args.map do |arg|
37
16
  matching_transform = transforms.detect {|transform| transform.match(arg) }
@@ -87,39 +66,6 @@ module Cucumber
87
66
  @transforms ||= []
88
67
  end
89
68
 
90
- def execute_around(scenario, &block)
91
- hooks_for(:around, scenario).reverse.inject(block) do |blk, hook|
92
- proc do
93
- invoke(hook, 'Around', scenario, true) do
94
- blk.call(scenario)
95
- end
96
- end
97
- end.call
98
- end
99
-
100
- def execute_before(scenario)
101
- hooks_for(:before, scenario).each do |hook|
102
- invoke(hook, 'Before', scenario, true)
103
- end
104
- end
105
-
106
- def execute_after(scenario)
107
- hooks_for(:after, scenario).reverse_each do |hook|
108
- invoke(hook, 'After', scenario, true)
109
- end
110
- end
111
-
112
- def invoke(hook, location, scenario, exception_fails_scenario, &block)
113
- begin
114
- hook.invoke(location, scenario, &block)
115
- rescue Exception => exception
116
- if exception_fails_scenario
117
- scenario.fail!(exception)
118
- else
119
- raise
120
- end
121
- end
122
- end
123
69
  end
124
70
  end
125
71
  end
@@ -427,8 +427,9 @@ module Cucumber
427
427
  end
428
428
 
429
429
  def to_s(options = {}) #:nodoc:
430
+ # TODO: factor out this code so we don't depend on such a big lump of old cruft
430
431
  require 'cucumber/formatter/pretty'
431
- require 'cucumber/reports/legacy_formatter'
432
+ require 'cucumber/formatter/legacy_api/adapter'
432
433
  options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
433
434
  io = StringIO.new
434
435
 
@@ -436,9 +437,7 @@ module Cucumber
436
437
  Cucumber::Term::ANSIColor.coloring = options[:color]
437
438
  formatter = Formatter::Pretty.new(nil, io, options)
438
439
  formatter.instance_variable_set('@indent', options[:indent])
439
-
440
- Reports::Legacy::Ast::MultilineArg.for(self).accept(Reports::FormatterWrapper.new([formatter]))
441
-
440
+ Formatter::LegacyApi::Ast::MultilineArg.for(self).accept(Formatter::Fanout.new([formatter]))
442
441
  Cucumber::Term::ANSIColor.coloring = c
443
442
  io.rewind
444
443
  s = "\n" + io.read + (" " * (options[:indent] - 2))
@@ -4,7 +4,7 @@ require 'rbconfig'
4
4
 
5
5
  module Cucumber
6
6
  unless defined?(Cucumber::VERSION)
7
- VERSION = '2.0.0.beta.3'
7
+ VERSION = '2.0.0.beta.4'
8
8
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
9
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
10
  JRUBY = defined?(JRUBY_VERSION)
@@ -9,6 +9,10 @@ require 'cucumber/load_path'
9
9
  require 'cucumber/language_support/language_methods'
10
10
  require 'cucumber/formatter/duration'
11
11
  require 'cucumber/file_specs'
12
+ require 'cucumber/filters/quit'
13
+ require 'cucumber/filters/randomizer'
14
+ require 'cucumber/filters/tag_limits'
15
+ require 'cucumber/formatter/fanout'
12
16
 
13
17
  module Cucumber
14
18
  module FixRuby21Bug9285
@@ -35,9 +39,7 @@ module Cucumber
35
39
 
36
40
  require 'cucumber/core'
37
41
  require 'cucumber/runtime/user_interface'
38
- require 'cucumber/runtime/results'
39
42
  require 'cucumber/runtime/support_code'
40
- require 'cucumber/runtime/tag_limits'
41
43
  class Runtime
42
44
  attr_reader :results, :support_code, :configuration
43
45
 
@@ -46,17 +48,15 @@ module Cucumber
46
48
  include Runtime::UserInterface
47
49
 
48
50
  def initialize(configuration = Configuration.default)
49
- @current_scenario = nil
50
51
  @configuration = Configuration.parse(configuration)
51
52
  @support_code = SupportCode.new(self, @configuration)
52
- @results = Results.new(@configuration)
53
+ @results = Formatter::LegacyApi::Results.new
53
54
  end
54
55
 
55
- # Allows you to take an existing runtime and change it's configuration
56
+ # Allows you to take an existing runtime and change its configuration
56
57
  def configure(new_configuration)
57
58
  @configuration = Configuration.parse(new_configuration)
58
59
  @support_code.configure(@configuration)
59
- @results.configure(@configuration)
60
60
  end
61
61
 
62
62
  def load_programming_language(language)
@@ -79,10 +79,6 @@ module Cucumber
79
79
  @configuration.dry_run?
80
80
  end
81
81
 
82
- def step_visited(step) #:nodoc:
83
- @results.step_visited(step)
84
- end
85
-
86
82
  def scenarios(status = nil)
87
83
  @results.scenarios(status)
88
84
  end
@@ -103,55 +99,10 @@ module Cucumber
103
99
  @support_code.snippet_text(::Gherkin::I18n.code_keyword_for(step_keyword), step_name, multiline_arg)
104
100
  end
105
101
 
106
- def with_hooks(scenario, skip_hooks=false)
107
- around(scenario, skip_hooks) do
108
- before_and_after(scenario, skip_hooks) do
109
- yield scenario
110
- end
111
- end
112
- end
113
-
114
- def around(scenario, skip_hooks=false, &block) #:nodoc:
115
- if skip_hooks
116
- yield
117
- return
118
- end
119
-
120
- @support_code.around(scenario, block)
121
- end
122
-
123
- def before_and_after(scenario, skip_hooks=false) #:nodoc:
124
- before(scenario) unless skip_hooks
125
- yield scenario
126
- after(scenario) unless skip_hooks
127
- record_result scenario
128
- end
129
-
130
- def record_result(scenario)
131
- @results.scenario_visited(scenario)
132
- end
133
-
134
102
  def begin_scenario(scenario)
135
103
  @support_code.fire_hook(:begin_scenario, scenario)
136
104
  end
137
105
 
138
- def before(scenario) #:nodoc:
139
- return if dry_run? || @current_scenario
140
- @current_scenario = scenario
141
- @support_code.fire_hook(:before, scenario)
142
- end
143
-
144
- def after(scenario) #:nodoc:
145
- @current_scenario = nil
146
- return if dry_run?
147
- @support_code.fire_hook(:after, scenario)
148
- end
149
-
150
- def after_step #:nodoc:
151
- return if dry_run?
152
- @support_code.fire_hook(:execute_after_step, @current_scenario)
153
- end
154
-
155
106
  def unknown_programming_language?
156
107
  @support_code.unknown_programming_language?
157
108
  end
@@ -159,8 +110,7 @@ module Cucumber
159
110
  # Returns Ast::DocString for +string_without_triple_quotes+.
160
111
  #
161
112
  def doc_string(string_without_triple_quotes, content_type='', line_offset=0)
162
- file, line = *caller[0].split(':')[0..1]
163
- location = Core::Ast::Location.new(file, line)
113
+ location = Core::Ast::Location.of_caller
164
114
  Core::Ast::DocString.new(string_without_triple_quotes, content_type, location)
165
115
  end
166
116
 
@@ -228,68 +178,52 @@ module Cucumber
228
178
  @mappings = Mappings.for(self)
229
179
  end
230
180
 
231
- require 'cucumber/reports/legacy_formatter'
181
+ require 'cucumber/formatter/legacy_api/adapter'
182
+ require 'cucumber/formatter/legacy_api/runtime_facade'
183
+ require 'cucumber/formatter/legacy_api/results'
184
+ require 'cucumber/formatter/ignore_missing_messages'
185
+ require 'cucumber/core/report/summary'
232
186
  def report
233
- @report ||= Cucumber::Reports::LegacyFormatter.new(self, @configuration.formatters(self))
187
+ @report ||= Formatter::Fanout.new([summary_report] + formatters)
188
+ end
189
+
190
+ def summary_report
191
+ @summary_report ||= Core::Report::Summary.new
192
+ end
193
+
194
+ def formatters
195
+ @formatters ||= @configuration.formatter_factories { |factory, path_or_io, options|
196
+ results = Formatter::LegacyApi::Results.new
197
+ runtime_facade = Formatter::LegacyApi::RuntimeFacade.new(results, @support_code, @configuration)
198
+ formatter = factory.new(runtime_facade, path_or_io, options)
199
+ Formatter::LegacyApi::Adapter.new(
200
+ Formatter::IgnoreMissingMessages.new(formatter),
201
+ results, @support_code, @configuration)
202
+ }
234
203
  end
235
204
 
205
+ def failure?
206
+ if @configuration.wip?
207
+ summary_report.test_cases.total_passed > 0
208
+ else
209
+ summary_report.test_cases.total_failed > 0 || summary_report.test_steps.total_failed > 0 ||
210
+ (@configuration.strict? && (summary_report.test_steps.total_undefined > 0 || summary_report.test_steps.total_skipped > 0))
211
+ end
212
+ end
213
+ public :failure?
214
+
236
215
  require 'cucumber/core/test/filters'
237
216
  def filters
238
217
  tag_expressions = @configuration.tag_expressions
239
218
  name_regexps = @configuration.name_regexps
240
219
  tag_limits = @configuration.tag_limits
241
220
  [].tap do |filters|
242
- filters << [Cucumber::Runtime::Randomizer, [@configuration.seed]] if @configuration.randomize?
243
- filters << [Cucumber::Runtime::TagLimits::Filter, [tag_limits]] if tag_limits.any?
221
+ filters << [Filters::Randomizer, [@configuration.seed]] if @configuration.randomize?
222
+ filters << [Filters::TagLimits, [tag_limits]] if tag_limits.any?
244
223
  filters << [Cucumber::Core::Test::TagFilter, [tag_expressions]]
245
224
  filters << [Cucumber::Core::Test::NameFilter, [name_regexps]]
246
225
  filters << [Cucumber::Core::Test::LocationsFilter, [filespecs.locations]]
247
- filters << [Quit, []]
248
- end
249
- end
250
-
251
- class Randomizer
252
- def initialize(seed, receiver)
253
- @receiver = receiver
254
- @test_cases = []
255
- @seed = seed
256
- end
257
-
258
- def test_case(test_case)
259
- @test_cases << test_case
260
- end
261
-
262
- def done
263
- shuffled_test_cases.each do |test_case|
264
- test_case.describe_to(@receiver)
265
- end
266
- @receiver.done
267
- end
268
-
269
- private
270
-
271
- def shuffled_test_cases
272
- @test_cases.shuffle(random: Random.new(seed))
273
- end
274
-
275
- attr_reader :seed
276
- private :seed
277
- end
278
-
279
- class Quit
280
- def initialize(receiver)
281
- @receiver = receiver
282
- end
283
-
284
- def test_case(test_case)
285
- unless Cucumber.wants_to_quit
286
- test_case.describe_to @receiver
287
- end
288
- end
289
-
290
- def done
291
- @receiver.done
292
- self
226
+ filters << [Filters::Quit, []]
293
227
  end
294
228
  end
295
229