cucumber 0.1.12 → 0.1.13

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 (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.rb +2 -0
  34. data/lib/cucumber/broadcaster.rb +1 -1
  35. data/lib/cucumber/cli.rb +87 -42
  36. data/lib/cucumber/core_ext/exception.rb +20 -0
  37. data/lib/cucumber/core_ext/string.rb +1 -1
  38. data/lib/cucumber/executor.rb +35 -18
  39. data/lib/cucumber/formatters/ansicolor.rb +65 -74
  40. data/lib/cucumber/formatters/html_formatter.rb +33 -10
  41. data/lib/cucumber/formatters/pretty_formatter.rb +58 -16
  42. data/lib/cucumber/formatters/progress_formatter.rb +3 -0
  43. data/lib/cucumber/formatters/unicode.rb +27 -0
  44. data/lib/cucumber/languages.yml +6 -4
  45. data/lib/cucumber/platform.rb +1 -0
  46. data/lib/cucumber/rails/world.rb +6 -6
  47. data/lib/cucumber/step_mother.rb +3 -0
  48. data/lib/cucumber/tree/feature.rb +28 -2
  49. data/lib/cucumber/tree/scenario.rb +62 -1
  50. data/lib/cucumber/tree/step.rb +32 -1
  51. data/lib/cucumber/treetop_parser/feature.treetop.erb +54 -7
  52. data/lib/cucumber/treetop_parser/feature_ar.rb +377 -18
  53. data/lib/cucumber/treetop_parser/feature_cy.rb +377 -18
  54. data/lib/cucumber/treetop_parser/feature_da.rb +377 -18
  55. data/lib/cucumber/treetop_parser/feature_de.rb +377 -18
  56. data/lib/cucumber/treetop_parser/feature_en-lol.rb +377 -18
  57. data/lib/cucumber/treetop_parser/feature_en-tx.rb +377 -18
  58. data/lib/cucumber/treetop_parser/feature_en.rb +377 -18
  59. data/lib/cucumber/treetop_parser/feature_es.rb +377 -18
  60. data/lib/cucumber/treetop_parser/feature_et.rb +377 -18
  61. data/lib/cucumber/treetop_parser/feature_fr.rb +389 -30
  62. data/lib/cucumber/treetop_parser/feature_id.rb +377 -18
  63. data/lib/cucumber/treetop_parser/feature_it.rb +377 -18
  64. data/lib/cucumber/treetop_parser/feature_ja.rb +377 -18
  65. data/lib/cucumber/treetop_parser/feature_lt.rb +377 -18
  66. data/lib/cucumber/treetop_parser/feature_nl.rb +377 -18
  67. data/lib/cucumber/treetop_parser/feature_no.rb +377 -18
  68. data/lib/cucumber/treetop_parser/feature_pl.rb +377 -18
  69. data/lib/cucumber/treetop_parser/feature_pt.rb +377 -18
  70. data/lib/cucumber/treetop_parser/feature_ro.rb +377 -18
  71. data/lib/cucumber/treetop_parser/feature_ro2.rb +377 -18
  72. data/lib/cucumber/treetop_parser/feature_ru.rb +377 -18
  73. data/lib/cucumber/treetop_parser/feature_se.rb +377 -18
  74. data/lib/cucumber/treetop_parser/feature_zh-CN.rb +377 -18
  75. data/lib/cucumber/version.rb +1 -1
  76. data/lib/cucumber/world.rb +1 -0
  77. data/lib/cucumber/world/pending.rb +22 -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
@@ -0,0 +1,34 @@
1
+ Given /^there are (\d+) (\w+)$/ do |count, fruit|
2
+ @eattingMachine = EattingMachine.new(fruit, count)
3
+ end
4
+
5
+ Given "the belly space is < 12 and > 6" do
6
+ end
7
+
8
+ Given "I have the following fruits in my pantry" do |pantry_table|
9
+ @pantry = Pantry.new
10
+ pantry_table.hashes.each do |item|
11
+ @pantry.add(item['name'].downcase, item['quantity'])
12
+ end
13
+ end
14
+
15
+ When /^I eat (\d+) (\w+)$/ do |count, fruit|
16
+ @eattingMachine.eat(count)
17
+ @eattingMachine.belly_count = count.to_i
18
+ end
19
+
20
+ When /^I eat (\d+) (\w+) from the pantry$/ do |count, fruit|
21
+ @pantry.remove(fruit, count.to_i)
22
+ end
23
+
24
+ Then /^I should have (\d+) (\w+)$/ do |count, fruit|
25
+ @eattingMachine.fruit_total.should == count.to_i
26
+ end
27
+
28
+ Then /^I should have (\d+) (\w+) in my belly$/ do |count, fruit|
29
+ @eattingMachine.belly_count.should == count.to_i
30
+ end
31
+
32
+ Then /^I should have (\d+) (\w+) in the pantry$/ do |count, fruit|
33
+ @pantry.count(fruit).should == count.to_i
34
+ end
@@ -1,5 +1,9 @@
1
1
  require 'spec'
2
2
 
3
+ World do
4
+ Object.new
5
+ end
6
+
3
7
  Given "be_empty" do
4
8
  [1,2].should_not be_empty
5
9
  end
@@ -2,7 +2,7 @@ desc 'Make all files use UNIX (\n) line endings'
2
2
  task :fix_cr_lf do
3
3
  files = FileList['**/*']
4
4
  files.each do |f|
5
- next if File.directory?(f)
5
+ next if File.directory?(f) || f =~ /dos/
6
6
  s = IO.read(f)
7
7
  s.gsub!(/\r?\n/, "\n")
8
8
  File.open(f, "w") { |io| io.write(s) }
@@ -0,0 +1,8 @@
1
+ begin
2
+ require 'yard'
3
+
4
+ YARD::Rake::YardocTask.new do |t|
5
+ t.files = ['lib/**/*.rb']
6
+ end
7
+ rescue LoadError => ignore
8
+ end
@@ -1,5 +1,6 @@
1
1
  require 'autotest'
2
2
  require 'tempfile'
3
+ require File.dirname(__FILE__) + '/../cucumber/platform'
3
4
 
4
5
  module Autotest::CucumberMixin
5
6
  def self.included(receiver)
@@ -121,11 +122,10 @@ module Autotest::CucumberMixin
121
122
  else
122
123
  scenario_args = scenarios_to_run.map { |s| "-s '#{s}'" }.join(' ')
123
124
  end
124
-
125
- return "#{cucumber} #{args} #{scenario_args}"
125
+ return "#{$CUCUMBER_RUBY} #{cucumber} #{args} #{scenario_args}"
126
126
  end
127
127
 
128
128
  def cucumber
129
- File.exist?("script/cucumber") ? "script/cucumber" : "cucumber"
129
+ File.file?("script/cucumber") ? "script/cucumber" : "cucumber"
130
130
  end
131
131
  end
@@ -15,6 +15,8 @@ require 'cucumber/formatters'
15
15
  require 'cucumber/treetop_parser/feature_parser'
16
16
  require 'cucumber/cli'
17
17
  require 'cucumber/broadcaster'
18
+ require 'cucumber/world'
19
+ require 'cucumber/core_ext/exception'
18
20
 
19
21
  module Cucumber
20
22
  LANGUAGE_FILE = File.expand_path(File.dirname(__FILE__) + '/cucumber/languages.yml')
@@ -11,7 +11,7 @@ module Cucumber
11
11
 
12
12
  def method_missing(method_name, *args)
13
13
  @receivers.each do |receiver|
14
- r = (receiver == STDOUT) ? Kernel: receiver # Needed to make colors work on Windows
14
+ r = (receiver == STDOUT) ? Kernel : receiver # Needed to make colors work on Windows
15
15
  r.__send__(method_name, *args) if receiver.respond_to?(method_name)
16
16
  end
17
17
  end
@@ -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