jeffrafter-cucumber 0.1.10

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 (242) hide show
  1. data/History.txt +262 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +241 -0
  4. data/README.txt +41 -0
  5. data/Rakefile +8 -0
  6. data/TODO.txt +26 -0
  7. data/bin/cucumber +6 -0
  8. data/config/hoe.rb +69 -0
  9. data/config/requirements.rb +15 -0
  10. data/cucumber.yml +1 -0
  11. data/examples/calculator_ruby_features/Rakefile +6 -0
  12. data/examples/calculator_ruby_features/features/addition.rb +23 -0
  13. data/examples/calculator_ruby_features/features/step_definitons/calculator_steps.rb +43 -0
  14. data/examples/cs/README.textile +22 -0
  15. data/examples/cs/Rakefile +12 -0
  16. data/examples/cs/features/hello.feature +11 -0
  17. data/examples/cs/features/step_definitons/hello_steps.rb +25 -0
  18. data/examples/cs/features/step_definitons/tree_steps.rb +14 -0
  19. data/examples/cs/features/tree.feature +9 -0
  20. data/examples/cs/src/Hello.cs +18 -0
  21. data/examples/dos_line_endings/Rakefile +6 -0
  22. data/examples/dos_line_endings/features/dos_line_endings.feature +9 -0
  23. data/examples/i18n/README.textile +18 -0
  24. data/examples/i18n/Rakefile +16 -0
  25. data/examples/i18n/ar/Rakefile +6 -0
  26. data/examples/i18n/ar/features/addition.feature +17 -0
  27. data/examples/i18n/ar/features/step_definitons/calculator_steps.rb +32 -0
  28. data/examples/i18n/ar/lib/calculator.rb +10 -0
  29. data/examples/i18n/da/Rakefile +6 -0
  30. data/examples/i18n/da/features/step_definitons/kalkulator_steps.rb +22 -0
  31. data/examples/i18n/da/features/summering.feature +17 -0
  32. data/examples/i18n/da/lib/kalkulator.rb +11 -0
  33. data/examples/i18n/de/Rakefile +6 -0
  34. data/examples/i18n/de/features/addition.feature +17 -0
  35. data/examples/i18n/de/features/division.feature +10 -0
  36. data/examples/i18n/de/features/step_definitons/calculator_steps.rb +30 -0
  37. data/examples/i18n/de/lib/calculator.rb +14 -0
  38. data/examples/i18n/en/Rakefile +6 -0
  39. data/examples/i18n/en/features/addition.feature +17 -0
  40. data/examples/i18n/en/features/division.feature +10 -0
  41. data/examples/i18n/en/features/step_definitons/calculator_steps.rb +31 -0
  42. data/examples/i18n/en/lib/calculator.rb +14 -0
  43. data/examples/i18n/es/Rakefile +6 -0
  44. data/examples/i18n/es/features/adicion.feature +17 -0
  45. data/examples/i18n/es/features/step_definitons/calculador_steps.rb +22 -0
  46. data/examples/i18n/es/lib/calculador.rb +11 -0
  47. data/examples/i18n/et/Rakefile +6 -0
  48. data/examples/i18n/et/features/liitmine.feature +17 -0
  49. data/examples/i18n/et/features/step_definitions/kalkulaator_steps.rb +26 -0
  50. data/examples/i18n/et/lib/kalkulaator.rb +10 -0
  51. data/examples/i18n/fr/Rakefile +6 -0
  52. data/examples/i18n/fr/features/addition.feature +13 -0
  53. data/examples/i18n/fr/features/step_definitions/calculatrice_steps.rb +22 -0
  54. data/examples/i18n/fr/lib/calculatrice.rb +10 -0
  55. data/examples/i18n/id/Rakefile +6 -0
  56. data/examples/i18n/id/features/addition.feature +17 -0
  57. data/examples/i18n/id/features/division.feature +10 -0
  58. data/examples/i18n/id/features/step_definitons/calculator_steps.rb +31 -0
  59. data/examples/i18n/id/lib/calculator.rb +14 -0
  60. data/examples/i18n/it/Rakefile +6 -0
  61. data/examples/i18n/it/features/somma.feature +10 -0
  62. data/examples/i18n/it/features/step_definitons/calcolatrice_steps.rb +22 -0
  63. data/examples/i18n/it/lib/calcolatrice.rb +11 -0
  64. data/examples/i18n/ja/README.txt +5 -0
  65. data/examples/i18n/ja/Rakefile +6 -0
  66. data/examples/i18n/ja/features/addition.feature +17 -0
  67. data/examples/i18n/ja/features/division.feature +10 -0
  68. data/examples/i18n/ja/features/step_definitons/calculator_steps.rb +30 -0
  69. data/examples/i18n/ja/lib/calculator.rb +14 -0
  70. data/examples/i18n/lt/Rakefile +6 -0
  71. data/examples/i18n/lt/features/addition.feature +17 -0
  72. data/examples/i18n/lt/features/division.feature +10 -0
  73. data/examples/i18n/lt/features/step_definitons/calculator_steps.rb +31 -0
  74. data/examples/i18n/lt/lib/calculator.rb +14 -0
  75. data/examples/i18n/no/Rakefile +6 -0
  76. data/examples/i18n/no/features/step_definitons/kalkulator_steps.rb +22 -0
  77. data/examples/i18n/no/features/summering.feature +17 -0
  78. data/examples/i18n/no/lib/kalkulator.rb +11 -0
  79. data/examples/i18n/pt/Rakefile +6 -0
  80. data/examples/i18n/pt/features/adicao.feature +10 -0
  81. data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +22 -0
  82. data/examples/i18n/pt/lib/calculadora.rb +10 -0
  83. data/examples/i18n/ro/Rakefile +6 -0
  84. data/examples/i18n/ro/features/step_definitons/calculator_steps.rb +22 -0
  85. data/examples/i18n/ro/features/suma.feature +10 -0
  86. data/examples/i18n/ro/lib/calculator.rb +11 -0
  87. data/examples/i18n/se/Rakefile +6 -0
  88. data/examples/i18n/se/features/step_definitons/kalkulator_steps.rb +22 -0
  89. data/examples/i18n/se/features/summering.feature +17 -0
  90. data/examples/i18n/se/lib/kalkulator.rb +11 -0
  91. data/examples/i18n/zh-CN/Rakefile +6 -0
  92. data/examples/i18n/zh-CN/features/addition.feature +17 -0
  93. data/examples/i18n/zh-CN/features/step_definitons/calculator_steps.rb +24 -0
  94. data/examples/i18n/zh-CN/lib/calculator.rb +10 -0
  95. data/examples/java/README.textile +22 -0
  96. data/examples/java/Rakefile +12 -0
  97. data/examples/java/features/hello.feature +11 -0
  98. data/examples/java/features/step_definitons/hello_steps.rb +25 -0
  99. data/examples/java/features/step_definitons/tree_steps.rb +14 -0
  100. data/examples/java/features/tree.feature +9 -0
  101. data/examples/java/src/cucumber/demo/Hello.java +16 -0
  102. data/examples/selenium/Rakefile +6 -0
  103. data/examples/selenium/features/search.feature +9 -0
  104. data/examples/selenium/features/step_definitons/stories_steps.rb +41 -0
  105. data/examples/test_unit/Rakefile +6 -0
  106. data/examples/test_unit/features/step_definitions/test_unit_steps.rb +26 -0
  107. data/examples/test_unit/features/test_unit.feature +9 -0
  108. data/examples/tickets/Rakefile +11 -0
  109. data/examples/tickets/cucumber.yml +2 -0
  110. data/examples/tickets/features/step_definitons/tickets_steps.rb +32 -0
  111. data/examples/tickets/features/tickets.feature +24 -0
  112. data/examples/watir/Rakefile +6 -0
  113. data/examples/watir/features/search.feature +9 -0
  114. data/examples/watir/features/step_definitons/stories_steps.rb +51 -0
  115. data/features/see_features.feature +8 -0
  116. data/features/steps/features_steps.rb +9 -0
  117. data/gem_tasks/deployment.rake +34 -0
  118. data/gem_tasks/environment.rake +7 -0
  119. data/gem_tasks/features.rake +6 -0
  120. data/gem_tasks/fix_cr_lf.rake +10 -0
  121. data/gem_tasks/flog.rake +4 -0
  122. data/gem_tasks/gemspec.rake +6 -0
  123. data/gem_tasks/rspec.rake +35 -0
  124. data/gem_tasks/treetop.rake +41 -0
  125. data/lib/autotest/cucumber.rb +6 -0
  126. data/lib/autotest/cucumber_mixin.rb +109 -0
  127. data/lib/autotest/cucumber_rails.rb +6 -0
  128. data/lib/autotest/cucumber_rails_rspec.rb +6 -0
  129. data/lib/autotest/cucumber_rspec.rb +6 -0
  130. data/lib/autotest/discover.rb +9 -0
  131. data/lib/cucumber/broadcaster.rb +20 -0
  132. data/lib/cucumber/cli.rb +303 -0
  133. data/lib/cucumber/core_ext/proc.rb +69 -0
  134. data/lib/cucumber/core_ext/string.rb +28 -0
  135. data/lib/cucumber/executor.rb +190 -0
  136. data/lib/cucumber/formatters/ansicolor.rb +109 -0
  137. data/lib/cucumber/formatters/autotest_formatter.rb +23 -0
  138. data/lib/cucumber/formatters/cucumber.css +132 -0
  139. data/lib/cucumber/formatters/cucumber.js +11 -0
  140. data/lib/cucumber/formatters/html_formatter.rb +128 -0
  141. data/lib/cucumber/formatters/jquery.js +32 -0
  142. data/lib/cucumber/formatters/pretty_formatter.rb +234 -0
  143. data/lib/cucumber/formatters/profile_formatter.rb +92 -0
  144. data/lib/cucumber/formatters/progress_formatter.rb +58 -0
  145. data/lib/cucumber/formatters.rb +1 -0
  146. data/lib/cucumber/languages.yml +258 -0
  147. data/lib/cucumber/model/table.rb +28 -0
  148. data/lib/cucumber/model.rb +1 -0
  149. data/lib/cucumber/platform.rb +12 -0
  150. data/lib/cucumber/rails/rspec.rb +12 -0
  151. data/lib/cucumber/rails/world.rb +73 -0
  152. data/lib/cucumber/rake/task.rb +101 -0
  153. data/lib/cucumber/step_methods.rb +49 -0
  154. data/lib/cucumber/step_mother.rb +95 -0
  155. data/lib/cucumber/tree/feature.rb +63 -0
  156. data/lib/cucumber/tree/features.rb +21 -0
  157. data/lib/cucumber/tree/given_scenario.rb +13 -0
  158. data/lib/cucumber/tree/scenario.rb +174 -0
  159. data/lib/cucumber/tree/step.rb +134 -0
  160. data/lib/cucumber/tree/table.rb +26 -0
  161. data/lib/cucumber/tree/top_down_visitor.rb +23 -0
  162. data/lib/cucumber/tree.rb +18 -0
  163. data/lib/cucumber/treetop_parser/feature.treetop.erb +206 -0
  164. data/lib/cucumber/treetop_parser/feature_ar.rb +1591 -0
  165. data/lib/cucumber/treetop_parser/feature_cy.rb +1591 -0
  166. data/lib/cucumber/treetop_parser/feature_da.rb +1591 -0
  167. data/lib/cucumber/treetop_parser/feature_de.rb +1591 -0
  168. data/lib/cucumber/treetop_parser/feature_en-tx.rb +1591 -0
  169. data/lib/cucumber/treetop_parser/feature_en.rb +1591 -0
  170. data/lib/cucumber/treetop_parser/feature_es.rb +1591 -0
  171. data/lib/cucumber/treetop_parser/feature_et.rb +1591 -0
  172. data/lib/cucumber/treetop_parser/feature_fr.rb +1591 -0
  173. data/lib/cucumber/treetop_parser/feature_id.rb +1591 -0
  174. data/lib/cucumber/treetop_parser/feature_it.rb +1591 -0
  175. data/lib/cucumber/treetop_parser/feature_ja.rb +1591 -0
  176. data/lib/cucumber/treetop_parser/feature_lt.rb +1591 -0
  177. data/lib/cucumber/treetop_parser/feature_nl.rb +1591 -0
  178. data/lib/cucumber/treetop_parser/feature_no.rb +1591 -0
  179. data/lib/cucumber/treetop_parser/feature_parser.rb +34 -0
  180. data/lib/cucumber/treetop_parser/feature_pl.rb +1591 -0
  181. data/lib/cucumber/treetop_parser/feature_pt.rb +1591 -0
  182. data/lib/cucumber/treetop_parser/feature_ro.rb +1591 -0
  183. data/lib/cucumber/treetop_parser/feature_ro2.rb +1591 -0
  184. data/lib/cucumber/treetop_parser/feature_ru.rb +1591 -0
  185. data/lib/cucumber/treetop_parser/feature_se.rb +1591 -0
  186. data/lib/cucumber/treetop_parser/feature_zh-CN.rb +1591 -0
  187. data/lib/cucumber/version.rb +9 -0
  188. data/lib/cucumber.rb +38 -0
  189. data/rails_generators/cucumber/USAGE +11 -0
  190. data/rails_generators/cucumber/cucumber_generator.rb +30 -0
  191. data/rails_generators/cucumber/templates/cucumber +7 -0
  192. data/rails_generators/cucumber/templates/cucumber.rake +7 -0
  193. data/rails_generators/cucumber/templates/env.rb +8 -0
  194. data/rails_generators/cucumber/templates/webrat_steps.rb +95 -0
  195. data/rails_generators/feature/USAGE +12 -0
  196. data/rails_generators/feature/feature_generator.rb +16 -0
  197. data/rails_generators/feature/templates/feature.erb +28 -0
  198. data/rails_generators/feature/templates/steps.erb +22 -0
  199. data/script/console +10 -0
  200. data/script/console.cmd +1 -0
  201. data/script/destroy +14 -0
  202. data/script/destroy.cmd +1 -0
  203. data/script/generate +14 -0
  204. data/script/generate.cmd +1 -0
  205. data/script/txt2html +74 -0
  206. data/script/txt2html.cmd +1 -0
  207. data/setup.rb +1585 -0
  208. data/spec/cucumber/broadcaster_spec.rb +27 -0
  209. data/spec/cucumber/cli_spec.rb +412 -0
  210. data/spec/cucumber/core_ext/proc_spec.rb +45 -0
  211. data/spec/cucumber/core_ext/string_spec.rb +34 -0
  212. data/spec/cucumber/executor_spec.rb +298 -0
  213. data/spec/cucumber/formatters/ansicolor_spec.rb +35 -0
  214. data/spec/cucumber/formatters/autotest_formatter_spec.rb +26 -0
  215. data/spec/cucumber/formatters/features.html +269 -0
  216. data/spec/cucumber/formatters/html_formatter_spec.rb +74 -0
  217. data/spec/cucumber/formatters/pretty_formatter_spec.rb +204 -0
  218. data/spec/cucumber/formatters/profile_formatter_spec.rb +192 -0
  219. data/spec/cucumber/formatters/progress_formatter_spec.rb +65 -0
  220. data/spec/cucumber/model/table_spec.rb +20 -0
  221. data/spec/cucumber/rails/stubs/mini_rails.rb +17 -0
  222. data/spec/cucumber/rails/stubs/test_help.rb +1 -0
  223. data/spec/cucumber/rails/world_spec.rb +11 -0
  224. data/spec/cucumber/sell_cucumbers.feature +19 -0
  225. data/spec/cucumber/step_mother_spec.rb +74 -0
  226. data/spec/cucumber/tree/feature_spec.rb +43 -0
  227. data/spec/cucumber/tree/row_scenario_spec.rb +55 -0
  228. data/spec/cucumber/tree/scenario_spec.rb +76 -0
  229. data/spec/cucumber/tree/step_spec.rb +50 -0
  230. data/spec/cucumber/treetop_parser/empty_feature.feature +4 -0
  231. data/spec/cucumber/treetop_parser/empty_scenario.feature +9 -0
  232. data/spec/cucumber/treetop_parser/feature_parser_spec.rb +98 -0
  233. data/spec/cucumber/treetop_parser/fit_scenario.feature +8 -0
  234. data/spec/cucumber/treetop_parser/given_scenario.feature +9 -0
  235. data/spec/cucumber/treetop_parser/multiline_steps.feature +17 -0
  236. data/spec/cucumber/treetop_parser/multiple_tables.feature +29 -0
  237. data/spec/cucumber/treetop_parser/spaces.feature +10 -0
  238. data/spec/cucumber/treetop_parser/test_dos.feature +25 -0
  239. data/spec/cucumber/treetop_parser/with_comments.feature +10 -0
  240. data/spec/spec.opts +2 -0
  241. data/spec/spec_helper.rb +20 -0
  242. metadata +335 -0
@@ -0,0 +1,109 @@
1
+ require 'autotest'
2
+ require 'tempfile'
3
+
4
+ module Autotest::CucumberMixin
5
+ def self.included(receiver)
6
+ receiver::ALL_HOOKS << [:run_features, :ran_features]
7
+ end
8
+
9
+ attr_accessor :scenarios_to_run
10
+
11
+ def initialize
12
+ super
13
+ reset_features
14
+ end
15
+
16
+ def run
17
+ hook :initialize
18
+ reset
19
+ reset_features
20
+ add_sigint_handler
21
+
22
+ self.last_mtime = Time.now if $f
23
+
24
+ loop do # ^c handler
25
+ begin
26
+ get_to_green
27
+ if self.tainted then
28
+ rerun_all_tests
29
+ rerun_all_features if all_good
30
+ else
31
+ hook :all_good
32
+ end
33
+ wait_for_changes
34
+ # Once tests and features are green, reset features every
35
+ # time a file is changed to see if anything breaks.
36
+ reset_features
37
+ rescue Interrupt
38
+ break if self.wants_to_quit
39
+ reset
40
+ reset_features
41
+ end
42
+ end
43
+ hook :quit
44
+ end
45
+
46
+ def all_features_good
47
+ scenarios_to_run == []
48
+ end
49
+
50
+ def get_to_green
51
+ begin
52
+ super
53
+ run_features
54
+ wait_for_changes unless all_features_good
55
+ end until all_features_good
56
+ end
57
+
58
+ def rerun_all_features
59
+ reset_features
60
+ run_features
61
+ end
62
+
63
+ def reset_features
64
+ self.scenarios_to_run = :all
65
+ end
66
+
67
+ def run_features
68
+ hook :run_features
69
+ Tempfile.open('autotest-cucumber') do |dirty_scenarios_file|
70
+ cmd = self.make_cucumber_cmd self.scenarios_to_run, dirty_scenarios_file.path
71
+ return if cmd.empty?
72
+ puts cmd unless $q
73
+ system cmd
74
+ self.scenarios_to_run = dirty_scenarios_file.readlines.map { |l| l.chomp }
75
+ self.tainted = true unless self.scenarios_to_run == []
76
+ end
77
+ hook :ran_features
78
+ end
79
+
80
+ def make_cucumber_cmd(scenarios_to_run, dirty_scenarios_filename)
81
+ return '' if scenarios_to_run == []
82
+
83
+ profiles = YAML.load_file("cucumber.yml").keys rescue []
84
+
85
+ profile ||= "autotest-all" if profiles.include?("autotest-all") and scenarios_to_run == :all
86
+ profile ||= "autotest" if profiles.include?("autotest")
87
+ profile ||= nil
88
+
89
+ if profile
90
+ args = ["--profile", profile]
91
+ else
92
+ args = %w{features --format} << (scenarios_to_run == :all ? "progress" : "pretty")
93
+ end
94
+ args += %w{--format autotest --out} << dirty_scenarios_filename
95
+ args = args.join(' ')
96
+
97
+ if scenarios_to_run == :all
98
+ scenario_args = nil
99
+ else
100
+ scenario_args = scenarios_to_run.map { |s| "-s '#{s}'" }.join(' ')
101
+ end
102
+
103
+ return "#{cucumber} #{args} #{scenario_args}"
104
+ end
105
+
106
+ def cucumber
107
+ File.exist?("script/cucumber") ? "script/cucumber" : "cucumber"
108
+ end
109
+ end
@@ -0,0 +1,6 @@
1
+ require 'autotest/rails'
2
+ require 'autotest/cucumber_mixin'
3
+
4
+ class Autotest::CucumberRails < Autotest::Rails
5
+ include CucumberMixin
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'autotest/cucumber_mixin'
2
+ require 'autotest/rails_rspec'
3
+
4
+ class Autotest::CucumberRailsRspec < Autotest::RailsRspec
5
+ include CucumberMixin
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'autotest/cucumber_mixin'
2
+ require 'autotest/rspec'
3
+
4
+ class Autotest::CucumberRspec < Autotest::Rspec
5
+ include CucumberMixin
6
+ end
@@ -0,0 +1,9 @@
1
+ Autotest.add_discovery do
2
+ if File.directory?('features')
3
+ if ENV['AUTOFEATURE'] =~ /true/i
4
+ "cucumber"
5
+ else
6
+ puts "(Not running features. To run features in autotest, set AUTOFEATURE=true.)"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ module Cucumber
2
+ class Broadcaster
3
+
4
+ def initialize(receivers = [])
5
+ @receivers = receivers
6
+ end
7
+
8
+ def register(receiver)
9
+ @receivers << receiver
10
+ end
11
+
12
+ def method_missing(method_name, *args)
13
+ @receivers.each do |receiver|
14
+ r = (receiver == STDOUT) ? Kernel: receiver # Needed to make colors work on Windows
15
+ r.__send__(method_name, *args) if receiver.respond_to?(method_name)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,303 @@
1
+ require 'optparse'
2
+ require 'cucumber'
3
+
4
+ module Cucumber
5
+ class CLI
6
+ class << self
7
+ attr_writer :step_mother, :executor, :features
8
+
9
+ def execute
10
+ @execute_called = true
11
+ parse(ARGV).execute!(@step_mother, @executor, @features)
12
+ end
13
+
14
+ def execute_called?
15
+ @execute_called
16
+ end
17
+
18
+ def parse(args)
19
+ cli = new
20
+ cli.parse_options!(args)
21
+ cli
22
+ end
23
+ end
24
+
25
+ attr_reader :options, :paths
26
+ FORMATS = %w{pretty profile progress html autotest}
27
+ DEFAULT_FORMAT = 'pretty'
28
+
29
+ def initialize(out_stream = STDOUT, error_stream = STDERR)
30
+ @out_stream = out_stream
31
+ @error_stream = error_stream
32
+ @paths = []
33
+ @options = {
34
+ :require => nil,
35
+ :lang => 'en',
36
+ :dry_run => false,
37
+ :source => true,
38
+ :snippets => true,
39
+ :formats => {},
40
+ :excludes => [],
41
+ :scenario_names => nil
42
+ }
43
+ @active_format = DEFAULT_FORMAT
44
+ end
45
+
46
+ def parse_options!(args)
47
+ return parse_args_from_profile('default') if args.empty?
48
+ args.extend(OptionParser::Arguable)
49
+
50
+ args.options do |opts|
51
+ opts.banner = "Usage: cucumber [options] FILES|DIRS"
52
+ opts.on("-r LIBRARY|DIR", "--require LIBRARY|DIR", "Require files before executing the features.",
53
+ "If this option is not specified, all *.rb files that",
54
+ "are siblings or below the features will be autorequired",
55
+ "This option can be specified multiple times.") do |v|
56
+ @options[:require] ||= []
57
+ @options[:require] << v
58
+ end
59
+ opts.on("-s SCENARIO", "--scenario SCENARIO", "Only execute the scenario with the given name.",
60
+ "If this option is given more than once, run all",
61
+ "the specified scenarios.") do |v|
62
+ @options[:scenario_names] ||= []
63
+ @options[:scenario_names] << v
64
+ end
65
+ opts.on("-a LANG", "--language LANG", "Specify language for features (Default: #{@options[:lang]})",
66
+ "Available languages: #{Cucumber.languages.join(", ")}",
67
+ "Look at #{Cucumber::LANGUAGE_FILE} for keywords") do |v|
68
+ @options[:lang] = v
69
+ end
70
+ opts.on("-f FORMAT", "--format FORMAT", "How to format features (Default: #{DEFAULT_FORMAT})",
71
+ "Available formats: #{FORMATS.join(", ")}",
72
+ "You can also provide your own formatter classes as long as they have been",
73
+ "previously required using --require or if they are in the folder",
74
+ "structure such that cucumber will require them automatically.",
75
+ "This option can be specified multiple times.") do |v|
76
+
77
+ @options[:formats][v] ||= []
78
+ @options[:formats][v] << @out_stream
79
+ @active_format = v
80
+ end
81
+ opts.on("-o", "--out FILE", "Write output to a file instead of @out_stream.",
82
+ "This option can be specified multiple times, and applies to the previously",
83
+ "specified --format.") do |v|
84
+ @options[:formats][@active_format] ||= []
85
+ if @options[:formats][@active_format].last == @out_stream
86
+ @options[:formats][@active_format][-1] = File.open(v, 'w')
87
+ else
88
+ @options[:formats][@active_format] << File.open(v, 'w')
89
+ end
90
+ end
91
+ opts.on("-c", "--[no-]color", "Use ANSI color in the output, if formatters use it. If",
92
+ "these options are given multiple times, the last one is",
93
+ "used. If neither --color or --no-color is given cucumber",
94
+ "decides based on your platform and the output destination") do |v|
95
+ @options[:color] = v
96
+ end
97
+ opts.on("-e", "--exclude PATTERN", "Don't run features matching a pattern") do |v|
98
+ @options[:excludes] << v
99
+ end
100
+ opts.on("-p", "--profile PROFILE", "Pull commandline arguments from cucumber.yml.") do |v|
101
+ parse_args_from_profile(v)
102
+ end
103
+ opts.on("-d", "--dry-run", "Invokes formatters without executing the steps.") do
104
+ @options[:dry_run] = true
105
+ end
106
+ opts.on("-n", "--no-source", "Don't show the file and line of the step definition with the steps.") do
107
+ @options[:source] = false
108
+ end
109
+ opts.on("-i", "--no-snippets", "Don't show the snippets for pending steps") do
110
+ @options[:snippets] = false
111
+ end
112
+ opts.on("-q", "--quiet", "Don't show any development aid information") do
113
+ @options[:snippets] = false
114
+ @options[:source] = false
115
+ end
116
+ opts.on("-v", "--verbose", "Show the files and features loaded") do
117
+ @options[:verbose] = true
118
+ end
119
+ opts.on_tail("--version", "Show version") do
120
+ @out_stream.puts VERSION::STRING
121
+ Kernel.exit
122
+ end
123
+ opts.on_tail("--help", "You're looking at it") do
124
+ @out_stream.puts opts.help
125
+ Kernel.exit
126
+ end
127
+ end.parse!
128
+
129
+ if @options[:formats].empty?
130
+ @options[:formats][DEFAULT_FORMAT] = [@out_stream]
131
+ end
132
+
133
+ # Whatever is left after option parsing is the FILE arguments
134
+ args = extract_and_store_line_numbers(args)
135
+ @paths += args
136
+ end
137
+
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
+
161
+ def execute!(step_mother, executor, features)
162
+ Term::ANSIColor.coloring = @options[:color] unless @options[:color].nil?
163
+ Cucumber.load_language(@options[:lang])
164
+ require_files
165
+ executor.formatters = build_formatter_broadcaster(step_mother)
166
+ load_plain_text_features(features)
167
+ executor.lines_for_features = @options[:lines_for_features]
168
+ executor.scenario_names = @options[:scenario_names] if @options[:scenario_names]
169
+ executor.visit_features(features)
170
+ exit 1 if executor.failed
171
+ end
172
+
173
+ private
174
+
175
+ def extract_and_store_line_numbers(file_arguments)
176
+ @options[:lines_for_features] = Hash.new{|k,v| k[v] = []}
177
+ file_arguments.map do |arg|
178
+ _, file, lines = */^([\w\W]*?):([\d:]+)$/.match(arg)
179
+ unless file.nil?
180
+ @options[:lines_for_features][file] += lines.split(':').map { |line| line.to_i }
181
+ arg = file
182
+ end
183
+ arg
184
+ end
185
+ end
186
+
187
+ # Requires files - typically step files and ruby feature files.
188
+ def require_files
189
+ ARGV.clear # Shut up RSpec
190
+ require "cucumber/treetop_parser/feature_#{@options[:lang]}"
191
+ require "cucumber/treetop_parser/feature_parser"
192
+
193
+ 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|
200
+ begin
201
+ require lib
202
+ verbose_log(" * #{lib}")
203
+ rescue LoadError => e
204
+ e.message << "\nFailed to load #{lib}"
205
+ raise e
206
+ end
207
+ end
208
+ verbose_log("\n")
209
+ end
210
+
211
+ def feature_files
212
+ potential_feature_files = @paths.map do |path|
213
+ path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
214
+ path = path.chomp('/')
215
+ File.directory?(path) ? Dir["#{path}/**/*.feature"] : path
216
+ end.flatten.uniq
217
+
218
+ @options[:excludes].each do |exclude|
219
+ potential_feature_files.reject! do |path|
220
+ path =~ /#{Regexp.escape(exclude)}/
221
+ end
222
+ end
223
+
224
+ potential_feature_files
225
+ end
226
+
227
+ def feature_dirs
228
+ feature_files.map{|f| File.directory?(f) ? f : File.dirname(f)}.uniq
229
+ end
230
+
231
+ def load_plain_text_features(features)
232
+ parser = TreetopParser::FeatureParser.new
233
+
234
+ verbose_log("Features:")
235
+ feature_files.each do |f|
236
+ features << parser.parse_feature(f)
237
+ verbose_log(" * #{f}")
238
+ end
239
+ verbose_log("\n"*2)
240
+ end
241
+
242
+ def build_formatter_broadcaster(step_mother)
243
+ formatter_broadcaster = Broadcaster.new
244
+ @options[:formats].each do |format, output_list|
245
+ output_broadcaster = build_output_broadcaster(output_list)
246
+ case format
247
+ when 'pretty'
248
+ formatter_broadcaster.register(Formatters::PrettyFormatter.new(output_broadcaster, step_mother, @options))
249
+ when 'progress'
250
+ formatter_broadcaster.register(Formatters::ProgressFormatter.new(output_broadcaster))
251
+ when 'profile'
252
+ formatter_broadcaster.register(Formatters::ProfileFormatter.new(output_broadcaster, step_mother))
253
+ when 'html'
254
+ formatter_broadcaster.register(Formatters::HtmlFormatter.new(output_broadcaster, step_mother))
255
+ when 'autotest'
256
+ formatter_broadcaster.register(Formatters::AutotestFormatter.new(output_broadcaster))
257
+ else
258
+ begin
259
+ formatter_class = Kernel.const_get(format)
260
+ formatter_broadcaster.register(formatter_class.new(output_broadcaster, step_mother, @options))
261
+ rescue NameError => e
262
+ @error_stream.puts "Invalid format: #{format}\n"
263
+ exit_with_help
264
+ rescue Exception => e
265
+ exit_with_error("Error creating formatter: #{format}\n#{e}")
266
+ end
267
+ end
268
+ end
269
+ formatter_broadcaster
270
+ end
271
+
272
+ def build_output_broadcaster(output_list)
273
+ output_broadcaster = Broadcaster.new
274
+ output_list.each do |output|
275
+ output_broadcaster.register(output)
276
+ end
277
+ output_broadcaster
278
+ end
279
+
280
+ private
281
+
282
+ def verbose_log(string)
283
+ @out_stream.puts(string) if @options[:verbose]
284
+ end
285
+
286
+ def exit_with_help
287
+ parse_options!(%w{--help})
288
+ end
289
+
290
+ def exit_with_error(error_message)
291
+ @error_stream << error_message
292
+ Kernel.exit 1
293
+ end
294
+
295
+ end
296
+ end
297
+
298
+ extend Cucumber::StepMethods
299
+ Cucumber::CLI.step_mother = step_mother
300
+ Cucumber::CLI.executor = executor
301
+
302
+ extend Cucumber::Tree
303
+ Cucumber::CLI.features = features
@@ -0,0 +1,69 @@
1
+ module Cucumber
2
+ class ArityMismatchError < StandardError
3
+ end
4
+
5
+ module CoreExt
6
+ # Proc extension that allows a proc to be called in the context of any object.
7
+ # Also makes it possible to tack a name onto a Proc.
8
+ module CallIn
9
+ PROC_PATTERN = /[\d\w]+@(.*):(.*)>/
10
+
11
+ if Proc.new{}.to_s =~ PROC_PATTERN
12
+ def to_backtrace_line
13
+ "#{file_colon_line}:in `#{name}'"
14
+ end
15
+
16
+ def to_comment_line
17
+ "# #{file_colon_line}"
18
+ end
19
+
20
+ def file_colon_line
21
+ path, line = *to_s.match(PROC_PATTERN)[1..2]
22
+ path = File.expand_path(path)
23
+ pwd = Dir.pwd
24
+ path = path[pwd.length+1..-1]
25
+ "#{path}:#{line}"
26
+ end
27
+ else
28
+ # This Ruby implementation doesn't implement Proc#to_s correctly
29
+ STDERR.puts "*** THIS RUBY IMPLEMENTATION DOESN'T REPORT FILE AND LINE FOR PROCS ***"
30
+
31
+ def to_backtrace_line
32
+ nil
33
+ end
34
+
35
+ def to_comment_line
36
+ ""
37
+ end
38
+ end
39
+
40
+ attr_accessor :name
41
+
42
+ def call_in(obj, *args)
43
+ obj.extend(mod)
44
+ if self != StepMother::PENDING && args.length != arity2
45
+ # We have to manually raise when the block has arity -1 (no pipes)
46
+ raise ArityMismatchError.new("expected #{arity2} block argument(s), got #{args.length}")
47
+ else
48
+ obj.__send__(meth, *args)
49
+ end
50
+ end
51
+
52
+ def arity2
53
+ arity == -1 ? 0 : arity
54
+ end
55
+
56
+ def meth
57
+ @meth ||= "__cucumber_#{object_id}"
58
+ end
59
+
60
+ def mod
61
+ p = self
62
+ m = meth
63
+ @mod ||= Module.new do
64
+ define_method(m, &p)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,28 @@
1
+ class String
2
+ def gzub(regexp, format=nil, &proc)
3
+ md = match(regexp)
4
+ raise "#{self.inspect} doesn't match #{regexp.inspect}" if md.nil?
5
+
6
+ s = dup
7
+ pos = 0
8
+ md.captures.each_with_index do |m, n|
9
+ replacement = if block_given?
10
+ proc.call(m)
11
+ else
12
+ format % m
13
+ end
14
+
15
+ if md.offset(n+1)[0]
16
+ s[md.offset(n+1)[0] + pos, m.length] = replacement
17
+ pos += replacement.length - m.length
18
+ end
19
+ end
20
+ s
21
+ end
22
+
23
+ if ENV['CUCUMBER_NO_JCODE']
24
+ alias jlength length
25
+ else
26
+ require 'jcode'
27
+ end
28
+ end