aslakhellesoy-cucumber 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/History.txt +38 -3
  2. data/Manifest.txt +17 -1
  3. data/README.txt +2 -39
  4. data/bin/cucumber +1 -1
  5. data/examples/calculator_ruby_features/features/addition.rb +16 -0
  6. data/examples/i18n/ar/features/step_definitons/calculator_steps.rb +1 -1
  7. data/examples/i18n/da/features/step_definitons/kalkulator_steps.rb +1 -0
  8. data/examples/i18n/de/features/step_definitons/calculator_steps.rb +1 -0
  9. data/examples/i18n/en/features/step_definitons/calculator_steps.rb +1 -0
  10. data/examples/i18n/es/features/step_definitons/calculador_steps.rb +1 -0
  11. data/examples/i18n/et/features/step_definitions/kalkulaator_steps.rb +1 -0
  12. data/examples/i18n/fr/features/addition.feature +13 -11
  13. data/examples/i18n/fr/features/step_definitions/calculatrice_steps.rb +6 -2
  14. data/examples/i18n/id/features/step_definitons/calculator_steps.rb +1 -0
  15. data/examples/i18n/it/features/step_definitons/calcolatrice_steps.rb +1 -0
  16. data/examples/i18n/ja/features/step_definitons/calculator_steps.rb +2 -0
  17. data/examples/i18n/lt/features/step_definitons/calculator_steps.rb +1 -0
  18. data/examples/i18n/no/features/step_definitons/kalkulator_steps.rb +1 -0
  19. data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +1 -0
  20. data/examples/i18n/ro/features/step_definitons/calculator_steps.rb +1 -0
  21. data/examples/i18n/se/features/step_definitons/kalkulator_steps.rb +1 -0
  22. data/examples/i18n/zh-CN/features/step_definitons/calculator_steps.rb +1 -0
  23. data/examples/selenium/features/search.feature +1 -1
  24. data/examples/selenium/features/step_definitons/stories_steps.rb +2 -3
  25. data/examples/tickets/features/lib/eatting_machine.rb +18 -0
  26. data/examples/tickets/features/lib/pantry.rb +20 -0
  27. data/examples/tickets/features/scenario_outline.feature +64 -0
  28. data/examples/tickets/features/step_definitons/scenario_outline_steps.rb +34 -0
  29. data/examples/tickets/features/step_definitons/tickets_steps.rb +4 -0
  30. data/gem_tasks/fix_cr_lf.rake +1 -1
  31. data/gem_tasks/yard.rake +8 -0
  32. data/lib/autotest/cucumber_mixin.rb +3 -3
  33. data/lib/cucumber/broadcaster.rb +1 -1
  34. data/lib/cucumber/cli.rb +87 -42
  35. data/lib/cucumber/core_ext/exception.rb +20 -0
  36. data/lib/cucumber/core_ext/string.rb +1 -1
  37. data/lib/cucumber/executor.rb +35 -18
  38. data/lib/cucumber/formatters/ansicolor.rb +65 -74
  39. data/lib/cucumber/formatters/html_formatter.rb +33 -10
  40. data/lib/cucumber/formatters/pretty_formatter.rb +58 -16
  41. data/lib/cucumber/formatters/progress_formatter.rb +3 -0
  42. data/lib/cucumber/formatters/unicode.rb +27 -0
  43. data/lib/cucumber/languages.yml +6 -4
  44. data/lib/cucumber/platform.rb +1 -0
  45. data/lib/cucumber/rails/world.rb +6 -6
  46. data/lib/cucumber/step_mother.rb +3 -0
  47. data/lib/cucumber/tree/feature.rb +28 -2
  48. data/lib/cucumber/tree/scenario.rb +62 -1
  49. data/lib/cucumber/tree/step.rb +32 -1
  50. data/lib/cucumber/treetop_parser/feature.treetop.erb +54 -7
  51. data/lib/cucumber/treetop_parser/feature_ar.rb +377 -18
  52. data/lib/cucumber/treetop_parser/feature_cy.rb +377 -18
  53. data/lib/cucumber/treetop_parser/feature_da.rb +377 -18
  54. data/lib/cucumber/treetop_parser/feature_de.rb +377 -18
  55. data/lib/cucumber/treetop_parser/feature_en-lol.rb +377 -18
  56. data/lib/cucumber/treetop_parser/feature_en-tx.rb +377 -18
  57. data/lib/cucumber/treetop_parser/feature_en.rb +377 -18
  58. data/lib/cucumber/treetop_parser/feature_es.rb +377 -18
  59. data/lib/cucumber/treetop_parser/feature_et.rb +377 -18
  60. data/lib/cucumber/treetop_parser/feature_fr.rb +389 -30
  61. data/lib/cucumber/treetop_parser/feature_id.rb +377 -18
  62. data/lib/cucumber/treetop_parser/feature_it.rb +377 -18
  63. data/lib/cucumber/treetop_parser/feature_ja.rb +377 -18
  64. data/lib/cucumber/treetop_parser/feature_lt.rb +377 -18
  65. data/lib/cucumber/treetop_parser/feature_nl.rb +377 -18
  66. data/lib/cucumber/treetop_parser/feature_no.rb +377 -18
  67. data/lib/cucumber/treetop_parser/feature_pl.rb +377 -18
  68. data/lib/cucumber/treetop_parser/feature_pt.rb +377 -18
  69. data/lib/cucumber/treetop_parser/feature_ro.rb +377 -18
  70. data/lib/cucumber/treetop_parser/feature_ro2.rb +377 -18
  71. data/lib/cucumber/treetop_parser/feature_ru.rb +377 -18
  72. data/lib/cucumber/treetop_parser/feature_se.rb +377 -18
  73. data/lib/cucumber/treetop_parser/feature_zh-CN.rb +377 -18
  74. data/lib/cucumber/version.rb +1 -1
  75. data/lib/cucumber/world/pending.rb +22 -0
  76. data/lib/cucumber/world.rb +1 -0
  77. data/lib/cucumber.rb +2 -0
  78. data/rails_generators/cucumber/templates/env.rb +1 -0
  79. data/rails_generators/feature/feature_generator.rb +22 -2
  80. data/rails_generators/feature/templates/feature.erb +15 -12
  81. data/rails_generators/feature/templates/steps.erb +16 -14
  82. data/spec/cucumber/cli_spec.rb +87 -6
  83. data/spec/cucumber/executor_spec.rb +102 -30
  84. data/spec/cucumber/formatters/ansicolor_spec.rb +10 -10
  85. data/spec/cucumber/formatters/html_formatter_spec.rb +30 -0
  86. data/spec/cucumber/formatters/pretty_formatter_spec.rb +139 -4
  87. data/spec/cucumber/formatters/progress_formatter_spec.rb +16 -0
  88. data/spec/cucumber/tree/feature_spec.rb +84 -5
  89. data/spec/cucumber/tree/row_scenario_outline_spec.rb +73 -0
  90. data/spec/cucumber/tree/row_step_outline_spec.rb +38 -0
  91. data/spec/cucumber/tree/scenario_outline_spec.rb +50 -0
  92. data/spec/cucumber/tree/step_outline_spec.rb +17 -0
  93. data/spec/cucumber/tree/step_spec.rb +9 -0
  94. data/spec/cucumber/treetop_parser/empty_scenario_outline.feature +3 -0
  95. data/spec/cucumber/treetop_parser/feature_parser_spec.rb +22 -0
  96. data/spec/cucumber/treetop_parser/invalid_scenario_outlines.feature +7 -0
  97. data/spec/cucumber/treetop_parser/scenario_outline.feature +16 -0
  98. data/spec/cucumber/world/pending_spec.rb +46 -0
  99. data/spec/spec_helper.rb +2 -1
  100. metadata +19 -4
  101. data/TODO.txt +0 -26
data/lib/cucumber/cli.rb CHANGED
@@ -2,13 +2,15 @@ require 'optparse'
2
2
  require 'cucumber'
3
3
 
4
4
  module Cucumber
5
+ class YmlLoadError < StandardError; end
6
+
5
7
  class CLI
6
8
  class << self
7
9
  attr_writer :step_mother, :executor, :features
8
10
 
9
- def execute
11
+ def execute(args)
10
12
  @execute_called = true
11
- parse(ARGV).execute!(@step_mother, @executor, @features)
13
+ parse(args).execute!(@step_mother, @executor, @features)
12
14
  end
13
15
 
14
16
  def execute_called?
@@ -44,11 +46,16 @@ module Cucumber
44
46
  end
45
47
 
46
48
  def parse_options!(args)
49
+ @args = args
47
50
  return parse_args_from_profile('default') if args.empty?
48
51
  args.extend(OptionParser::Arguable)
49
52
 
50
53
  args.options do |opts|
51
- opts.banner = "Usage: cucumber [options] FILES|DIRS"
54
+ opts.banner = ["Usage: cucumber [options] [[FILE[:LINE[:LINE]*]] | [FILES|DIRS]]", "",
55
+ "Examples:",
56
+ "cucumber examples/i18n/en/features",
57
+ "cucumber --language it examples/i18n/it/features/somma.feature:6:98:113", "", ""
58
+ ].join("\n")
52
59
  opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR", "Require files before executing the features.",
53
60
  "If this option is not specified, all *.rb files that",
54
61
  "are siblings or below the features will be autorequired",
@@ -62,7 +69,7 @@ module Cucumber
62
69
  @options[:scenario_names] ||= []
63
70
  @options[:scenario_names] << v
64
71
  end
65
- opts.on("-a LANG", "--language LANG", "Specify language for features (Default: #{@options[:lang]})",
72
+ opts.on("-l LANG", "--language LANG", "Specify language for features (Default: #{@options[:lang]})",
66
73
  "Available languages: #{Cucumber.languages.join(", ")}",
67
74
  "Look at #{Cucumber::LANGUAGE_FILE} for keywords") do |v|
68
75
  @options[:lang] = v
@@ -113,6 +120,9 @@ module Cucumber
113
120
  @options[:snippets] = false
114
121
  @options[:source] = false
115
122
  end
123
+ opts.on("-b", "--backtrace", "Show full backtrace for all errors") do
124
+ Exception.cucumber_full_backtrace = true
125
+ end
116
126
  opts.on("-v", "--verbose", "Show the files and features loaded") do
117
127
  @options[:verbose] = true
118
128
  end
@@ -135,33 +145,12 @@ module Cucumber
135
145
  @paths += args
136
146
  end
137
147
 
138
- def parse_args_from_profile(profile)
139
- unless File.exist?('cucumber.yml')
140
- return exit_with_error("cucumber.yml was not found. Please define your '#{profile}' and other profiles in cucumber.yml.\n"+
141
- "Type 'cucumber --help' for usage.\n")
142
- end
143
-
144
- require 'yaml'
145
- cucumber_yml = YAML::load(IO.read('cucumber.yml'))
146
- args_from_yml = cucumber_yml[profile]
147
- if args_from_yml.nil?
148
- exit_with_error <<-END_OF_ERROR
149
- Could not find profile: '#{profile}'
150
-
151
- Defined profiles in cucumber.yml:
152
- * #{cucumber_yml.keys.join("\n * ")}
153
- END_OF_ERROR
154
- elsif !args_from_yml.is_a?(String)
155
- exit_with_error "Profiles must be defined as a String. The '#{profile}' profile was #{args_from_yml.inspect} (#{args_from_yml.class}).\n"
156
- else
157
- parse_options!(args_from_yml.split(' '))
158
- end
159
- end
160
148
 
161
149
  def execute!(step_mother, executor, features)
162
150
  Term::ANSIColor.coloring = @options[:color] unless @options[:color].nil?
163
151
  Cucumber.load_language(@options[:lang])
164
152
  require_files
153
+ enable_diffing
165
154
  executor.formatters = build_formatter_broadcaster(step_mother)
166
155
  load_plain_text_features(features)
167
156
  executor.lines_for_features = @options[:lines_for_features]
@@ -183,20 +172,60 @@ Defined profiles in cucumber.yml:
183
172
  arg
184
173
  end
185
174
  end
186
-
175
+
176
+ def cucumber_yml
177
+ return @cucumber_yml if @cucumber_yml
178
+ unless File.exist?('cucumber.yml')
179
+ raise(YmlLoadError,"cucumber.yml was not found. Please refer to cucumber's documentaion on defining profiles in cucumber.yml. You must define a 'default' profile to use the cucumber command without any arguments.\nType 'cucumber --help' for usage.\n")
180
+ end
181
+
182
+ require 'yaml'
183
+ begin
184
+ @cucumber_yml = YAML::load(IO.read('cucumber.yml'))
185
+ rescue Exception => e
186
+ raise(YmlLoadError,"cucumber.yml was found, but could not be parsed. Please refer to cucumber's documentaion on correct profile usage.\n")
187
+ end
188
+
189
+ if @cucumber_yml.nil? || !@cucumber_yml.is_a?(Hash)
190
+ raise(YmlLoadError,"cucumber.yml was found, but was blank or malformed. Please refer to cucumber's documentaion on correct profile usage.\n")
191
+ end
192
+
193
+ return @cucumber_yml
194
+ end
195
+
196
+ def parse_args_from_profile(profile)
197
+ unless cucumber_yml.has_key?(profile)
198
+ return(exit_with_error <<-END_OF_ERROR)
199
+ Could not find profile: '#{profile}'
200
+
201
+ Defined profiles in cucumber.yml:
202
+ * #{cucumber_yml.keys.join("\n * ")}
203
+ END_OF_ERROR
204
+ end
205
+
206
+ args_from_yml = cucumber_yml[profile] || ''
207
+
208
+ if !args_from_yml.is_a?(String)
209
+ exit_with_error "Profiles must be defined as a String. The '#{profile}' profile was #{args_from_yml.inspect} (#{args_from_yml.class}).\n"
210
+ elsif args_from_yml =~ /^\s*$/
211
+ exit_with_error "The 'foo' profile in cucumber.yml was blank. Please define the command line arguments for the 'foo' profile in cucumber.yml.\n"
212
+ else
213
+ parse_options!(args_from_yml.split(' '))
214
+ end
215
+
216
+ rescue YmlLoadError => e
217
+ exit_with_error(e.message)
218
+ end
219
+
220
+
187
221
  # Requires files - typically step files and ruby feature files.
188
222
  def require_files
189
- ARGV.clear # Shut up RSpec
223
+ @args.clear # Shut up RSpec
190
224
  require "cucumber/treetop_parser/feature_#{@options[:lang]}"
191
225
  require "cucumber/treetop_parser/feature_parser"
192
226
 
193
227
  verbose_log("Ruby files required:")
194
- requires = @options[:require] || feature_dirs
195
- libs = requires.map do |path|
196
- path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
197
- File.directory?(path) ? Dir["#{path}/**/*.rb"] : path
198
- end.flatten.uniq
199
- libs.each do |lib|
228
+ files_to_require.each do |lib|
200
229
  begin
201
230
  require lib
202
231
  verbose_log(" * #{lib}")
@@ -207,6 +236,15 @@ Defined profiles in cucumber.yml:
207
236
  end
208
237
  verbose_log("\n")
209
238
  end
239
+
240
+ def files_to_require
241
+ requires = @options[:require] || feature_dirs
242
+ files = requires.map do |path|
243
+ path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
244
+ File.directory?(path) ? Dir["#{path}/**/*.rb"] : path
245
+ end.flatten.uniq
246
+ files.sort { |a,b| (b =~ %r{/support/} || -1) <=> (a =~ %r{/support/} || -1) }
247
+ end
210
248
 
211
249
  def feature_files
212
250
  potential_feature_files = @paths.map do |path|
@@ -280,14 +318,14 @@ Defined profiles in cucumber.yml:
280
318
  private
281
319
 
282
320
  def constantize(camel_cased_word)
283
- names = camel_cased_word.split('::')
284
- names.shift if names.empty? || names.first.empty?
285
-
286
- constant = Object
287
- names.each do |name|
288
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
289
- end
290
- constant
321
+ names = camel_cased_word.split('::')
322
+ names.shift if names.empty? || names.first.empty?
323
+
324
+ constant = Object
325
+ names.each do |name|
326
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
327
+ end
328
+ constant
291
329
  end
292
330
 
293
331
  def verbose_log(string)
@@ -303,6 +341,13 @@ Defined profiles in cucumber.yml:
303
341
  Kernel.exit 1
304
342
  end
305
343
 
344
+ def enable_diffing
345
+ if defined?(::Spec)
346
+ require 'spec/expectations/differs/default'
347
+ options = ::Spec::Runner::Options.new(nil, nil)
348
+ ::Spec::Expectations.differ = ::Spec::Expectations::Differs::Default.new(options)
349
+ end
350
+ end
306
351
  end
307
352
  end
308
353
 
@@ -0,0 +1,20 @@
1
+ class Exception
2
+ CUCUMBER_FILTER_PATTERNS = [
3
+ /vendor\/rails/,
4
+ /vendor\/plugins\/cucumber/,
5
+ /spec\/expectations/,
6
+ /spec\/matchers/
7
+ ]
8
+
9
+ def self.cucumber_full_backtrace=(v)
10
+ @@cucumber_full_backtrace = v
11
+ end
12
+ self.cucumber_full_backtrace = false
13
+
14
+ def cucumber_backtrace
15
+ return (backtrace || []) if @@cucumber_full_backtrace
16
+ (backtrace || []).map {|b| b.split("\n") }.flatten.reject do |line|
17
+ CUCUMBER_FILTER_PATTERNS.detect{|p| line =~ p}
18
+ end.map { |line| line.strip }
19
+ end
20
+ end
@@ -20,7 +20,7 @@ class String
20
20
  s
21
21
  end
22
22
 
23
- if $CUCUMBER_JRUBY && $CUCUMBER_RAILS
23
+ if ($CUCUMBER_JRUBY && $CUCUMBER_RAILS)
24
24
  # Workaround for http://tinyurl.com/55uu3u
25
25
  alias jlength length
26
26
  else
@@ -65,6 +65,10 @@ module Cucumber
65
65
  visit_scenario(scenario)
66
66
  end
67
67
 
68
+ def visit_scenario_outline(scenario)
69
+ visit_regular_scenario(scenario)
70
+ end
71
+
68
72
  def visit_scenario(scenario)
69
73
  if accept_scenario?(scenario)
70
74
  @executed_scenarios[scenario.name] = true
@@ -77,8 +81,6 @@ module Cucumber
77
81
  @pending = nil
78
82
 
79
83
  @world = create_world
80
- @world.extend(Spec::Matchers) if defined?(Spec::Matchers)
81
- define_step_call_methods(@world)
82
84
 
83
85
  formatters.scenario_executing(scenario)
84
86
  @before_scenario_procs.each{|p| p.call_in(@world, *[])}
@@ -104,6 +106,11 @@ module Cucumber
104
106
  visit_step(step)
105
107
  end
106
108
 
109
+ def visit_step_outline(step)
110
+ regexp, args, proc = step.regexp_args_proc(@step_mother)
111
+ formatters.step_traced(step, regexp, args)
112
+ end
113
+
107
114
  def visit_step(step)
108
115
  unless @pending || @error
109
116
  begin
@@ -112,6 +119,9 @@ module Cucumber
112
119
  step.execute_in(@world, regexp, args, proc)
113
120
  @after_step_procs.each{|p| p.call_in(@world, *[])}
114
121
  formatters.step_passed(step, regexp, args)
122
+ rescue ForcedPending => e
123
+ step.error = e
124
+ record_pending_step(step, regexp, args)
115
125
  rescue Pending
116
126
  record_pending_step(step, regexp, args)
117
127
  rescue => e
@@ -124,6 +134,9 @@ module Cucumber
124
134
  regexp, args, proc = step.regexp_args_proc(@step_mother)
125
135
  step.execute_in(@world, regexp, args, proc)
126
136
  formatters.step_skipped(step, regexp, args)
137
+ rescue ForcedPending => e
138
+ step.error = e
139
+ record_pending_step(step, regexp, args)
127
140
  rescue Pending
128
141
  record_pending_step(step, regexp, args)
129
142
  rescue Exception
@@ -137,22 +150,6 @@ module Cucumber
137
150
  formatters.step_pending(step, regexp, args)
138
151
  end
139
152
 
140
- def define_step_call_methods(world)
141
- world.instance_variable_set('@__executor', self)
142
- world.instance_eval do
143
- class << self
144
- def run_step(name)
145
- _, args, proc = @__executor.instance_variable_get(:@step_mother).regexp_args_proc(name)
146
- proc.call_in(self, *args)
147
- end
148
-
149
- %w{given when then and but}.each do |keyword|
150
- alias_method Cucumber.language[keyword], :run_step
151
- end
152
- end
153
- end
154
- end
155
-
156
153
  def executing_unprepared_row_scenario?(scenario)
157
154
  accept_scenario?(scenario) && !@executed_scenarios[scenario.name]
158
155
  end
@@ -182,7 +179,27 @@ module Cucumber
182
179
  @world_procs.each do |world_proc|
183
180
  world = world_proc.call(world)
184
181
  end
182
+
183
+ world.extend(World::Pending)
184
+ world.extend(::Spec::Matchers) if defined?(::Spec::Matchers)
185
+ define_step_call_methods(world)
185
186
  world
186
187
  end
188
+
189
+ def define_step_call_methods(world)
190
+ world.instance_variable_set('@__executor', self)
191
+ world.instance_eval do
192
+ class << self
193
+ def run_step(name)
194
+ _, args, proc = @__executor.instance_variable_get(:@step_mother).regexp_args_proc(name)
195
+ proc.call_in(self, *args)
196
+ end
197
+
198
+ %w{given when then and but}.each do |keyword|
199
+ alias_method Cucumber.language[keyword], :run_step
200
+ end
201
+ end
202
+ end
203
+ end
187
204
  end
188
205
  end
@@ -1,5 +1,5 @@
1
1
  # Hack to work around Win32/Console, which bundles a licence-violating, outdated
2
- # copy of term/ansicolor that doesn't implement Term::ANSIColor#coloring=.
2
+ # copy of term/ansicolor that doesn't implement Term::ANSIColor#coloring=.
3
3
  # We want the official one!
4
4
  gem 'term-ansicolor'
5
5
  $LOAD_PATH.each{|path| $LOAD_PATH.unshift($LOAD_PATH.delete(path)) if path =~ /term-ansicolor/}
@@ -7,7 +7,7 @@ require 'term/ansicolor'
7
7
 
8
8
  if $CUCUMBER_WINDOWS_MRI
9
9
  begin
10
- require 'Win32/Console/ANSI'
10
+ require 'Win32/Console/ANSI'
11
11
  rescue LoadError
12
12
  STDERR.puts "You must gem install win32console to get coloured output on MRI/Windows"
13
13
  Term::ANSIColor.coloring = false
@@ -18,92 +18,83 @@ Term::ANSIColor.coloring = false if !STDOUT.tty? || ($CUCUMBER_WINDOWS && !$CUCU
18
18
 
19
19
  module Cucumber
20
20
  module Formatters
21
- # Adds ANSI color support to formatters.
22
- # You can define your own colours by defining CUCUMBER_COLORS in your shell. Example:
21
+ # Defines aliases for coloured output. You can tweak the colours by defining
22
+ # a <tt>$CUCUMBER_COLORS</tt> variable in your shell, very much like you can
23
+ # tweak the familiar POSIX command <tt>ls</tt> with
24
+ # <a href="http://mipsisrisc.com/rambling/2008/06/27/lscolorsls_colors-now-with-linux-support/">$LSCOLORS/$LS_COLORS</a>
23
25
  #
24
- # CUCUMBER_COLORS=pending=black,on_yellow:failed=dark,magenta:failed_param=bold,red
25
- #
26
- # The string must be a series of key=value pairs where:
26
+ # The colours that you can change are:
27
27
  #
28
- # * Each key=value pair is separated by a colon (:)
29
- # * Each key must be one of:
30
- # ** passed
31
- # ** passed_param
32
- # ** failed
33
- # ** failed_param
34
- # ** skipped
35
- # ** skipped_param
36
- # ** pending
37
- # * Each value must be a comma-separated string composed of:
38
- # ** bold
39
- # ** dark
40
- # ** italic
41
- # ** underline
42
- # ** underscore
43
- # ** blink
44
- # ** rapid_blink
45
- # ** negative
46
- # ** concealed
47
- # ** strikethrough
48
- # ** black
49
- # ** red
50
- # ** green
51
- # ** yellow
52
- # ** blue
53
- # ** magenta
54
- # ** cyan
55
- # ** white
56
- # ** grey
57
- # ** on_black
58
- # ** on_red
59
- # ** on_green
60
- # ** on_yellow
61
- # ** on_blue
62
- # ** on_magenta
63
- # ** on_cyan
64
- # ** on_white
28
+ # * <tt>pending</tt> - defaults to <tt>yellow</tt>
29
+ # * <tt>pending_param</tt> - defaults to <tt>yellow,bold</tt>
30
+ # * <tt>failed</tt> - defaults to <tt>red</tt>
31
+ # * <tt>failed_param</tt> - defaults to <tt>red,bold</tt>
32
+ # * <tt>passed</tt> - defaults to <tt>green</tt>
33
+ # * <tt>passed_param</tt> - defaults to <tt>green,bold</tt>
34
+ # * <tt>skipped</tt> - defaults to <tt>cyan</tt>
35
+ # * <tt>skipped_param</tt> - defaults to <tt>cyan,bold</tt>
36
+ # * <tt>comment</tt> - defaults to <tt>grey</tt>
37
+ # * <tt>tag</tt> - defaults to <tt>blue</tt>
65
38
  #
39
+ # For instance, if your shell has a black background and a green font (like the
40
+ # "Homebrew" settings for OS X' Terminal.app), you may want to override passed
41
+ # steps to be white instead of green. Examples:
42
+ #
43
+ # export CUCUMBER_COLORS="passed=white"
44
+ # export CUCUMBER_COLORS="passed=white,bold:passed_param=white,bold,underline"
45
+ #
46
+ # (If you're on Windows, use SET instead of export).
47
+ # To see what colours and effects are available, just run this in your shell:
48
+ #
49
+ # ruby -e "require 'rubygems'; require 'term/ansicolor'; puts Term::ANSIColor.attributes"
50
+ #
51
+ # Although not listed, you can also use <tt>grey</tt>
66
52
  module ANSIColor
67
- include ::Term::ANSIColor
68
-
69
- # Params are underlined bold, except in windows, which doesn't support underline. Use non-bold colour.
70
- param = PLATFORM =~ /mswin|mingw/ ? '' : 'underline,bold,'
53
+ include Term::ANSIColor
71
54
 
72
- # Default aliases
73
- ALIASES = {
74
- :passed => 'bold,green',
75
- :passed_param => "#{param}green",
76
- :failed => 'bold,red',
77
- :failed_param => "#{param}red",
78
- :skipped => 'bold,cyan',
79
- :skipped_param => "#{param}cyan",
80
- :pending => 'bold,yellow', # No pending_param
81
- :comment => 'grey'
82
- }
83
- if ENV['CUCUMBER_COLORS']
84
- ENV['CUCUMBER_COLORS'].split(':').each do |pair|
85
- a = pair.split('=')
86
- ALIASES[a[0].to_sym] = a[1]
87
- end
88
- end
89
-
90
55
  # Not supported in Term::ANSIColor
91
56
  def grey(m)
92
57
  if ::Term::ANSIColor.coloring?
93
- "\e[90m#{m}\e[0m"
58
+ "\e[90m#{m}\e[0m"
94
59
  else
95
60
  m
96
61
  end
97
62
  end
98
-
99
- ALIASES.each do |m, color_string|
100
- colors = color_string.split(",").reverse
101
- define_method(m) do |*s|
102
- clear + colors.inject(s[0]) do |memo, color|
103
- s[0].nil? ? __send__(color) + memo.to_s : __send__(color, memo.to_s)
63
+
64
+ ALIASES = Hash.new do |h,k|
65
+ if k.to_s =~ /(.*)_param/
66
+ h[$1] + ',bold'
67
+ end
68
+ end.merge({
69
+ 'pending' => 'yellow',
70
+ 'failed' => 'red',
71
+ 'passed' => 'green',
72
+ 'skipped' => 'cyan',
73
+ 'comment' => 'grey',
74
+ 'tag' => 'blue'
75
+ })
76
+
77
+ if ENV['CUCUMBER_COLORS'] # Example: export CUCUMBER_COLORS="passed=red:failed=yellow"
78
+ ENV['CUCUMBER_COLORS'].split(':').each do |pair|
79
+ a = pair.split('=')
80
+ ALIASES[a[0]] = a[1]
81
+ end
82
+ end
83
+
84
+ ALIASES.each do |method, color|
85
+ unless method =~ /.*_param/
86
+ code = <<-EOF
87
+ def #{method}(string=nil, &proc)
88
+ #{ALIASES[method].split(",").join("(") + "(string, &proc" + ")" * ALIASES[method].split(",").length}
104
89
  end
90
+ # This resets the colour to the non-param colour
91
+ def #{method}_param(string=nil, &proc)
92
+ #{ALIASES[method+'_param'].split(",").join("(") + "(string, &proc" + ")" * ALIASES[method+'_param'].split(",").length} + #{ALIASES[method].split(",").join(' + ')}
93
+ end
94
+ EOF
95
+ eval(code)
105
96
  end
106
97
  end
107
98
  end
108
99
  end
109
- end
100
+ end
@@ -1,3 +1,5 @@
1
+ require 'cgi'
2
+
1
3
  module Cucumber
2
4
  module Formatters
3
5
  class HtmlFormatter
@@ -53,15 +55,11 @@ HTML
53
55
  end
54
56
 
55
57
  def visit_regular_scenario(scenario)
56
- @scenario_table_header = scenario.table_header
57
- @io.puts %{ <dl class="new">}
58
- @io.puts %{ <dt>#{Cucumber.language['scenario']}: #{scenario.name}</dt>}
59
- @io.puts %{ <dd>}
60
- @io.puts %{ <ul>}
61
- scenario.accept(self)
62
- @io.puts %{ </ul>}
63
- @io.puts %{ </dd>}
64
- @io.puts %{ </dl>}
58
+ visit_scenario(scenario, Cucumber.language['scenario'])
59
+ end
60
+
61
+ def visit_scenario_outline(scenario)
62
+ visit_scenario(scenario, Cucumber.language['scenario_outline'])
65
63
  end
66
64
 
67
65
  def visit_row_scenario(scenario)
@@ -86,6 +84,7 @@ HTML
86
84
 
87
85
  def visit_row_step(step)
88
86
  _, args, _ = step.regexp_args_proc(@step_mother)
87
+ args = step.visible_args if step.outline?
89
88
  args.each do |arg|
90
89
  @io.puts %{ <td id="#{step.id}"><span>#{arg}</span></td>}
91
90
  end
@@ -95,6 +94,11 @@ HTML
95
94
  regexp, _, _ = step.regexp_args_proc(@step_mother)
96
95
  @io.puts %{ <li class="new" id="#{step.id}">#{step.keyword} #{step.format(regexp, '<span>%s</span>')}</li>}
97
96
  end
97
+
98
+ def visit_step_outline(step)
99
+ regexp, _, _ = step.regexp_args_proc(@step_mother)
100
+ @io.puts %{ <li class="new" id="#{step.id}">#{step.keyword} #{CGI.escapeHTML(step.format(nil))}</li>}
101
+ end
98
102
 
99
103
  def step_passed(step, regexp, args)
100
104
  print_javascript_tag("stepPassed(#{step.id})")
@@ -112,7 +116,11 @@ HTML
112
116
  def step_skipped(step, regexp, args)
113
117
  # noop
114
118
  end
115
-
119
+
120
+ def step_traced(step, regexp, args)
121
+ # noop
122
+ end
123
+
116
124
  def print_javascript_tag(js)
117
125
  @io.puts %{ <script type="text/javascript">#{js}</script>}
118
126
  end
@@ -123,6 +131,21 @@ HTML
123
131
  </html>
124
132
  HTML
125
133
  end
134
+
135
+ private
136
+
137
+ def visit_scenario(scenario, scenario_or_scenario_outline_keyword)
138
+ @scenario_table_header = scenario.table_header
139
+ @io.puts %{ <dl class="new">}
140
+ @io.puts %{ <dt>#{scenario_or_scenario_outline_keyword}: #{scenario.name}</dt>}
141
+ @io.puts %{ <dd>}
142
+ @io.puts %{ <ul>}
143
+ scenario.accept(self)
144
+ @io.puts %{ </ul>}
145
+ @io.puts %{ </dd>}
146
+ @io.puts %{ </dl>}
147
+ end
148
+
126
149
  end
127
150
  end
128
151
  end