kosmas58-cucumber 0.1.16.6 → 0.1.99.21

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 (248) hide show
  1. data/History.txt +126 -2
  2. data/License.txt +1 -1
  3. data/Manifest.txt +0 -270
  4. data/Rakefile +1 -1
  5. data/bin/cucumber +2 -2
  6. data/config/hoe.rb +7 -2
  7. data/examples/cs/features/step_definitons/calculator_steps.rb +1 -1
  8. data/examples/i18n/README.textile +1 -1
  9. data/examples/i18n/Rakefile +3 -3
  10. data/examples/i18n/ar/features/step_definitons/calculator_steps.rb +1 -6
  11. data/examples/i18n/da/features/step_definitons/kalkulator_steps.rb +1 -1
  12. data/examples/i18n/de/features/addition.feature +6 -6
  13. data/examples/i18n/de/features/step_definitons/calculator_steps.rb +2 -2
  14. data/examples/i18n/en/features/addition.feature +6 -6
  15. data/examples/i18n/en/features/step_definitons/calculator_steps.rb +2 -2
  16. data/examples/i18n/es/features/adicion.feature +11 -12
  17. data/examples/i18n/es/features/step_definitons/calculador_steps.rb +7 -10
  18. data/examples/i18n/es/lib/calculador.rb +4 -1
  19. data/examples/i18n/et/features/liitmine.feature +7 -6
  20. data/examples/i18n/et/features/step_definitions/kalkulaator_steps.rb +1 -1
  21. data/examples/i18n/fi/features/step_definitons/laskin_steps.rb +1 -1
  22. data/examples/i18n/fi/features/yhteenlasku.feature +2 -2
  23. data/examples/i18n/fr/features/addition.feature +2 -2
  24. data/examples/i18n/fr/features/step_definitions/calculatrice_steps.rb +1 -1
  25. data/examples/i18n/id/features/addition.feature +6 -6
  26. data/examples/i18n/id/features/step_definitons/calculator_steps.rb +2 -2
  27. data/examples/i18n/it/features/step_definitons/calcolatrice_steps.rb +1 -1
  28. data/examples/i18n/ja/features/step_definitons/calculator_steps.rb +2 -3
  29. data/examples/i18n/ko/features/step_definitons/calculator_steps.rb +1 -1
  30. data/examples/i18n/lt/features/addition.feature +7 -6
  31. data/examples/i18n/lt/features/step_definitons/calculator_steps.rb +2 -2
  32. data/examples/i18n/no/features/step_definitons/kalkulator_steps.rb +7 -7
  33. data/examples/i18n/no/features/summering.feature +1 -0
  34. data/examples/i18n/pt/features/step_definitions/calculadora_steps.rb +1 -1
  35. data/examples/i18n/ro/features/step_definitons/calculator_steps.rb +1 -1
  36. data/examples/i18n/se/features/step_definitons/kalkulator_steps.rb +1 -1
  37. data/examples/i18n/zh-CN/features/step_definitons/calculator_steps.rb +2 -2
  38. data/examples/java/src/cucumber/demo/Hello.java +16 -0
  39. data/examples/jbehave/README.textile +17 -0
  40. data/examples/jbehave/features/support/env.rb +7 -0
  41. data/examples/jbehave/features/trading.feature +24 -0
  42. data/examples/jbehave/pom.xml +48 -0
  43. data/examples/jbehave/src/main/java/cukes/jbehave/examples/trader/converters/TraderConverter.java +32 -0
  44. data/examples/jbehave/src/main/java/cukes/jbehave/examples/trader/model/Stock.java +42 -0
  45. data/examples/jbehave/src/main/java/cukes/jbehave/examples/trader/model/Trader.java +29 -0
  46. data/examples/jbehave/src/main/java/cukes/jbehave/examples/trader/persistence/TraderPersister.java +22 -0
  47. data/examples/jbehave/src/main/java/cukes/jbehave/examples/trader/scenarios/TraderSteps.java +65 -0
  48. data/examples/self_test/README.textile +4 -1
  49. data/examples/{calculator_ruby_features → self_test}/Rakefile +3 -3
  50. data/examples/self_test/features/background/failing_background.feature +10 -0
  51. data/examples/self_test/features/background/failing_background_after_success.feature +11 -0
  52. data/examples/self_test/features/background/multiline_args_background.feature +32 -0
  53. data/examples/self_test/features/background/passing_background.feature +10 -0
  54. data/examples/self_test/features/background/pending_background.feature +10 -0
  55. data/examples/self_test/features/background/scenario_outline_failing_background.feature +16 -0
  56. data/examples/self_test/features/background/scenario_outline_passing_background.feature +16 -0
  57. data/examples/self_test/features/call_undefined_step_from_step_def.feature +7 -0
  58. data/examples/self_test/features/lots_of_undefined.feature +8 -0
  59. data/examples/self_test/features/outline_sample.feature +11 -5
  60. data/examples/self_test/features/sample.feature +8 -3
  61. data/examples/self_test/features/step_definitions/sample_steps.rb +53 -4
  62. data/examples/self_test/features/support/env.rb +1 -0
  63. data/examples/self_test/features/support/tag_count_formatter.rb +25 -0
  64. data/examples/tickets/features/172.feature +28 -0
  65. data/examples/tickets/features/177/1.feature +29 -0
  66. data/examples/tickets/features/177/2.feature +21 -0
  67. data/examples/tickets/features/177/3.feature +18 -0
  68. data/examples/tickets/features/180.feature +7 -0
  69. data/examples/tickets/features/scenario_outline.feature +6 -1
  70. data/examples/tickets/features/step_definitons/tickets_steps.rb +27 -3
  71. data/examples/tickets/features/tickets.feature +4 -0
  72. data/features/background.feature +223 -0
  73. data/features/cucumber_cli.feature +218 -98
  74. data/features/cucumber_cli_outlines.feature +54 -37
  75. data/features/custom_formatter.feature +11 -0
  76. data/features/report_called_undefined_steps.feature +32 -0
  77. data/features/step_definitions/cucumber_steps.rb +7 -3
  78. data/features/step_definitions/extra_steps.rb +1 -1
  79. data/features/support/env.rb +2 -1
  80. data/gem_tasks/flog.rake +1 -1
  81. data/gem_tasks/gemspec.rake +2 -2
  82. data/gem_tasks/jar.rake +67 -0
  83. data/lib/autotest/cucumber_mixin.rb +17 -23
  84. data/lib/cucumber.rb +63 -11
  85. data/lib/cucumber/ast.rb +29 -0
  86. data/lib/cucumber/ast/background.rb +88 -0
  87. data/lib/cucumber/ast/comment.rb +26 -0
  88. data/lib/cucumber/ast/examples.rb +26 -0
  89. data/lib/cucumber/ast/feature.rb +66 -0
  90. data/lib/cucumber/ast/features.rb +39 -0
  91. data/lib/cucumber/ast/filter.rb +22 -0
  92. data/lib/cucumber/ast/outline_table.rb +49 -0
  93. data/lib/cucumber/ast/py_string.rb +52 -0
  94. data/lib/cucumber/ast/scenario.rb +103 -0
  95. data/lib/cucumber/ast/scenario_outline.rb +92 -0
  96. data/lib/cucumber/ast/step.rb +161 -0
  97. data/lib/cucumber/ast/steps.rb +13 -0
  98. data/lib/cucumber/ast/table.rb +218 -0
  99. data/lib/cucumber/ast/tags.rb +40 -0
  100. data/lib/cucumber/ast/visitor.rb +109 -0
  101. data/lib/cucumber/broadcaster.rb +1 -6
  102. data/lib/cucumber/cli/configuration.rb +339 -0
  103. data/lib/cucumber/cli/language_help_formatter.rb +59 -0
  104. data/lib/cucumber/cli/main.rb +100 -0
  105. data/lib/cucumber/core_ext/exception.rb +41 -8
  106. data/lib/cucumber/core_ext/instance_exec.rb +54 -0
  107. data/lib/cucumber/core_ext/proc.rb +29 -65
  108. data/lib/cucumber/core_ext/string.rb +19 -0
  109. data/lib/cucumber/formatter.rb +1 -0
  110. data/lib/cucumber/{formatters → formatter}/ansicolor.rb +14 -16
  111. data/lib/cucumber/formatter/color_io.rb +17 -0
  112. data/lib/cucumber/formatter/console.rb +119 -0
  113. data/lib/cucumber/formatter/cucumber.css +55 -0
  114. data/lib/cucumber/formatter/cucumber.sass +49 -0
  115. data/lib/cucumber/formatter/html.rb +121 -0
  116. data/lib/cucumber/formatter/pretty.rb +167 -0
  117. data/lib/cucumber/formatter/profile.rb +77 -0
  118. data/lib/cucumber/formatter/progress.rb +68 -0
  119. data/lib/cucumber/formatter/rerun.rb +35 -0
  120. data/lib/cucumber/formatter/unicode.rb +35 -0
  121. data/lib/cucumber/formatters/unicode.rb +2 -35
  122. data/lib/cucumber/jbehave.rb +102 -0
  123. data/lib/cucumber/languages.yml +128 -75
  124. data/lib/cucumber/parser.rb +43 -0
  125. data/lib/cucumber/parser/basic.rb +0 -0
  126. data/lib/cucumber/parser/feature.rb +1490 -0
  127. data/lib/cucumber/parser/feature.tt +188 -0
  128. data/lib/cucumber/parser/i18n.tt +31 -0
  129. data/lib/cucumber/parser/table.rb +402 -0
  130. data/lib/cucumber/parser/table.tt +59 -0
  131. data/lib/cucumber/parser/treetop_ext.rb +75 -0
  132. data/lib/cucumber/platform.rb +3 -17
  133. data/lib/cucumber/rails/rspec.rb +5 -2
  134. data/lib/cucumber/step_definition.rb +87 -0
  135. data/lib/cucumber/step_mother.rb +151 -66
  136. data/lib/cucumber/version.rb +2 -2
  137. data/rails_generators/cucumber/USAGE +3 -2
  138. data/rails_generators/cucumber/cucumber_generator.rb +33 -23
  139. data/rails_generators/cucumber/templates/cucumber +2 -1
  140. data/rails_generators/cucumber/templates/cucumber.rake +6 -1
  141. data/rails_generators/cucumber/templates/env.rb +1 -1
  142. data/rails_generators/feature/USAGE +3 -0
  143. data/rails_generators/feature/feature_generator.rb +30 -6
  144. data/spec/cucumber/ast/background_spec.rb +58 -0
  145. data/spec/cucumber/ast/feature_factory.rb +54 -0
  146. data/spec/cucumber/ast/feature_spec.rb +60 -0
  147. data/spec/cucumber/ast/py_string_spec.rb +40 -0
  148. data/spec/cucumber/ast/scenario_outline_spec.rb +64 -0
  149. data/spec/cucumber/ast/scenario_spec.rb +82 -0
  150. data/spec/cucumber/ast/step_spec.rb +45 -0
  151. data/spec/cucumber/ast/table_spec.rb +81 -0
  152. data/spec/cucumber/ast/tags_spec.rb +23 -0
  153. data/spec/cucumber/broadcaster_spec.rb +4 -17
  154. data/spec/cucumber/cli/configuration_spec.rb +239 -0
  155. data/spec/cucumber/cli/main_spec.rb +164 -0
  156. data/spec/cucumber/core_ext/proc_spec.rb +27 -35
  157. data/spec/cucumber/core_ext/string_spec.rb +8 -0
  158. data/spec/cucumber/{formatters → formatter}/ansicolor_spec.rb +2 -2
  159. data/spec/cucumber/formatter/html/cucumber.css +37 -0
  160. data/spec/cucumber/formatter/html/cucumber.js +13 -0
  161. data/spec/cucumber/formatter/html/index.html +45 -0
  162. data/spec/cucumber/formatter/html/jquery-1.3.min.js +19 -0
  163. data/spec/cucumber/formatter/html/jquery.uitableedit.js +100 -0
  164. data/spec/cucumber/formatters/profile_formatter_spec.rb +17 -16
  165. data/spec/cucumber/parser/feature_parser_spec.rb +281 -0
  166. data/spec/cucumber/parser/table_parser_spec.rb +48 -0
  167. data/spec/cucumber/step_definition_spec.rb +62 -0
  168. data/spec/cucumber/step_mother_spec.rb +50 -51
  169. data/spec/cucumber/treetop_parser/empty_feature.feature +1 -1
  170. data/spec/cucumber/treetop_parser/scenario_outline.feature +1 -1
  171. data/spec/cucumber/treetop_parser/spaces.feature +3 -1
  172. data/spec/cucumber/treetop_parser/with_comments.feature +1 -1
  173. data/spec/cucumber/treetop_parser/with_tags.feature +18 -0
  174. data/spec/cucumber/world/pending_spec.rb +13 -12
  175. data/spec/spec_helper.rb +1 -1
  176. metadata +107 -79
  177. data/examples/calculator_ruby_features/features/addition.rb +0 -39
  178. data/examples/calculator_ruby_features/features/step_definitons/calculator_steps.rb +0 -43
  179. data/gem_tasks/treetop.rake +0 -41
  180. data/lib/cucumber/cli.rb +0 -355
  181. data/lib/cucumber/executor.rb +0 -205
  182. data/lib/cucumber/formatters.rb +0 -1
  183. data/lib/cucumber/formatters/autotest_formatter.rb +0 -23
  184. data/lib/cucumber/formatters/cucumber.css +0 -132
  185. data/lib/cucumber/formatters/cucumber.js +0 -11
  186. data/lib/cucumber/formatters/html_formatter.rb +0 -152
  187. data/lib/cucumber/formatters/jquery.js +0 -32
  188. data/lib/cucumber/formatters/pretty_formatter.rb +0 -285
  189. data/lib/cucumber/formatters/profile_formatter.rb +0 -92
  190. data/lib/cucumber/formatters/progress_formatter.rb +0 -61
  191. data/lib/cucumber/model.rb +0 -1
  192. data/lib/cucumber/model/table.rb +0 -32
  193. data/lib/cucumber/step_methods.rb +0 -49
  194. data/lib/cucumber/tree.rb +0 -16
  195. data/lib/cucumber/tree/feature.rb +0 -105
  196. data/lib/cucumber/tree/features.rb +0 -21
  197. data/lib/cucumber/tree/given_scenario.rb +0 -13
  198. data/lib/cucumber/tree/scenario.rb +0 -240
  199. data/lib/cucumber/tree/step.rb +0 -173
  200. data/lib/cucumber/tree/table.rb +0 -26
  201. data/lib/cucumber/tree/top_down_visitor.rb +0 -23
  202. data/lib/cucumber/treetop_parser/feature.treetop.erb +0 -254
  203. data/lib/cucumber/treetop_parser/feature_ar.rb +0 -1951
  204. data/lib/cucumber/treetop_parser/feature_cy.rb +0 -1951
  205. data/lib/cucumber/treetop_parser/feature_da.rb +0 -1951
  206. data/lib/cucumber/treetop_parser/feature_de.rb +0 -1951
  207. data/lib/cucumber/treetop_parser/feature_en-lol.rb +0 -1951
  208. data/lib/cucumber/treetop_parser/feature_en-tx.rb +0 -1951
  209. data/lib/cucumber/treetop_parser/feature_en.rb +0 -1951
  210. data/lib/cucumber/treetop_parser/feature_es.rb +0 -1951
  211. data/lib/cucumber/treetop_parser/feature_et.rb +0 -1951
  212. data/lib/cucumber/treetop_parser/feature_fi.rb +0 -1951
  213. data/lib/cucumber/treetop_parser/feature_fr.rb +0 -1951
  214. data/lib/cucumber/treetop_parser/feature_id.rb +0 -1951
  215. data/lib/cucumber/treetop_parser/feature_it.rb +0 -1951
  216. data/lib/cucumber/treetop_parser/feature_ja.rb +0 -1951
  217. data/lib/cucumber/treetop_parser/feature_ko.rb +0 -1951
  218. data/lib/cucumber/treetop_parser/feature_lt.rb +0 -1951
  219. data/lib/cucumber/treetop_parser/feature_nl.rb +0 -1951
  220. data/lib/cucumber/treetop_parser/feature_no.rb +0 -1951
  221. data/lib/cucumber/treetop_parser/feature_parser.rb +0 -36
  222. data/lib/cucumber/treetop_parser/feature_pl.rb +0 -1951
  223. data/lib/cucumber/treetop_parser/feature_pt.rb +0 -1951
  224. data/lib/cucumber/treetop_parser/feature_ro.rb +0 -1951
  225. data/lib/cucumber/treetop_parser/feature_ro2.rb +0 -1951
  226. data/lib/cucumber/treetop_parser/feature_ru.rb +0 -1951
  227. data/lib/cucumber/treetop_parser/feature_se.rb +0 -1951
  228. data/lib/cucumber/treetop_parser/feature_zh-CN.rb +0 -1951
  229. data/lib/cucumber/world.rb +0 -1
  230. data/lib/cucumber/world/pending.rb +0 -22
  231. data/setup.rb +0 -1585
  232. data/spec/cucumber/cli_spec.rb +0 -521
  233. data/spec/cucumber/executor_spec.rb +0 -382
  234. data/spec/cucumber/formatters/autotest_formatter_spec.rb +0 -26
  235. data/spec/cucumber/formatters/features.html +0 -269
  236. data/spec/cucumber/formatters/html_formatter_spec.rb +0 -110
  237. data/spec/cucumber/formatters/pretty_formatter_spec.rb +0 -410
  238. data/spec/cucumber/formatters/progress_formatter_spec.rb +0 -81
  239. data/spec/cucumber/model/table_spec.rb +0 -32
  240. data/spec/cucumber/tree/feature_spec.rb +0 -122
  241. data/spec/cucumber/tree/row_scenario_outline_spec.rb +0 -73
  242. data/spec/cucumber/tree/row_scenario_spec.rb +0 -55
  243. data/spec/cucumber/tree/row_step_outline_spec.rb +0 -38
  244. data/spec/cucumber/tree/scenario_outline_spec.rb +0 -50
  245. data/spec/cucumber/tree/scenario_spec.rb +0 -134
  246. data/spec/cucumber/tree/step_outline_spec.rb +0 -17
  247. data/spec/cucumber/tree/step_spec.rb +0 -59
  248. data/spec/cucumber/treetop_parser/feature_parser_spec.rb +0 -120
@@ -0,0 +1,59 @@
1
+ module Cucumber
2
+ module Parser
3
+ # TIP: When you hack on the grammar, just delete feature.rb in this directory.
4
+ # Also make sure you have uninstalled all cucumber gems (don't forget xxx-cucumber
5
+ # github gems).
6
+ #
7
+ # Treetop will then generate the parser in-memory. When you're happy, just generate
8
+ # the rb file with tt feature.tt
9
+ grammar Table
10
+
11
+ rule table
12
+ table_row+ {
13
+ def build
14
+ Ast::Table.new(raw)
15
+ end
16
+
17
+ def raw
18
+ elements.map{|e| e.build}
19
+ end
20
+ }
21
+ end
22
+
23
+ rule table_row
24
+ space* '|' cells:(cell '|')+ space* (eol+ / eof) {
25
+ def build
26
+ row = cells.elements.map do |elt|
27
+ value = elt.cell.text_value.strip
28
+ value.empty? ? nil : value
29
+ end
30
+
31
+ class << row
32
+ attr_accessor :line
33
+ end
34
+ row.line = cells.line
35
+
36
+ row
37
+ end
38
+ }
39
+ end
40
+
41
+ rule cell
42
+ (!('|' / eol) .)*
43
+ end
44
+
45
+ rule space
46
+ [ \t]
47
+ end
48
+
49
+ rule eol
50
+ "\n" / ("\r" "\n"?)
51
+ end
52
+
53
+ rule eof
54
+ !.
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,75 @@
1
+ begin
2
+ require 'treetop'
3
+ require 'treetop/runtime'
4
+ require 'treetop/ruby_extensions'
5
+ rescue LoadError
6
+ require "rubygems"
7
+ gem "treetop"
8
+ require 'treetop'
9
+ require 'treetop/runtime'
10
+ require 'treetop/ruby_extensions'
11
+ end
12
+
13
+ module Cucumber
14
+ module Parser
15
+ module TreetopExt
16
+ FILE_LINE_PATTERN = /^([\w\W]*?):([\d:]+)$/
17
+
18
+ # Parses a file and returns a Cucumber::Ast
19
+ def parse_file(file)
20
+ _, path, lines = *FILE_LINE_PATTERN.match(file)
21
+ if path
22
+ lines = lines.split(':').map { |line| line.to_i }
23
+ else
24
+ path = file
25
+ lines = []
26
+ end
27
+
28
+ loader = lambda { |io| parse_or_fail(io.read, path) }
29
+ feature = if path =~ /^http/
30
+ require 'open-uri'
31
+ open(path, &loader)
32
+ else
33
+ File.open(path, Cucumber.file_mode('r'), &loader)
34
+ end
35
+ feature.lines = lines
36
+ feature
37
+ end
38
+ end
39
+
40
+ class SyntaxError < StandardError
41
+ def initialize(parser, file, line_offset)
42
+ tf = parser.terminal_failures
43
+ expected = tf.size == 1 ? tf[0].expected_string.inspect : "one of #{tf.map{|f| f.expected_string.inspect}.uniq*', '}"
44
+ line = parser.failure_line + line_offset
45
+ message = "#{file}:#{line}:#{parser.failure_column}: Parse error, expected #{expected}."
46
+ super(message)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ module Treetop
53
+ module Runtime
54
+ class SyntaxNode
55
+ def line
56
+ input.line_of(interval.first)
57
+ end
58
+ end
59
+
60
+ class CompiledParser
61
+ include Cucumber::Parser::TreetopExt
62
+
63
+ def parse_or_fail(s, file=nil, line=0)
64
+ parse_tree = parse(s)
65
+ if parse_tree.nil?
66
+ raise Cucumber::Parser::SyntaxError.new(self, file, line)
67
+ else
68
+ ast = parse_tree.build
69
+ ast.file = file
70
+ ast
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,9 +1,11 @@
1
1
  # Detect the platform we're running on so we can tweak behaviour
2
2
  # in various places.
3
3
  require 'rbconfig'
4
+ require 'yaml'
4
5
 
5
6
  module Cucumber
6
7
  LANGUAGE_FILE = File.expand_path(File.dirname(__FILE__) + '/languages.yml')
8
+ LANGUAGES = YAML.load_file(LANGUAGE_FILE)
7
9
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
8
10
  JRUBY = defined?(JRUBY_VERSION)
9
11
  IRONRUBY = Config::CONFIG['sitedir'] =~ /IronRuby/
@@ -12,21 +14,5 @@ module Cucumber
12
14
  RAILS = defined?(Rails)
13
15
  RUBY_BINARY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
14
16
  RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
15
-
16
- class << self
17
- attr_reader :language
18
-
19
- def load_language(lang)
20
- @language = config[lang]
21
- end
22
-
23
- def languages
24
- config.keys.sort
25
- end
26
-
27
- def config
28
- require 'yaml'
29
- @config ||= YAML.load_file(LANGUAGE_FILE)
30
- end
31
- end
17
+ EXCEPTION_STATUS = Hash.new(:failed)
32
18
  end
@@ -1,5 +1,8 @@
1
+ require 'cucumber/rails/world'
1
2
  require 'spec/expectations'
2
3
  require 'spec/rails/matchers'
3
4
 
4
- ActionController::Integration::Session.send(:include, Spec::Matchers)
5
- ActionController::Integration::Session.send(:include, Spec::Rails::Matchers)
5
+ Cucumber::Rails::World.class_eval do
6
+ include Spec::Matchers
7
+ include Spec::Rails::Matchers
8
+ end
@@ -0,0 +1,87 @@
1
+ require 'cucumber/core_ext/string'
2
+ require 'cucumber/core_ext/proc'
3
+
4
+ module Cucumber
5
+ # A Step Definition holds a Regexp and a Proc, and is created
6
+ # by calling <tt>Given</tt>, <tt>When</tt> or <tt>Then</tt>
7
+ # in the <tt>step_definitions</tt> ruby files - for example:
8
+ #
9
+ # Given /I have (\d+) cucumbers in my belly/ do
10
+ # # some code here
11
+ # end
12
+ #
13
+ class StepDefinition
14
+ def self.snippet_text(step_keyword, step_name)
15
+ escaped = Regexp.escape(step_name).gsub('\ ', ' ').gsub('/', '\/')
16
+ "#{step_keyword} /^#{escaped}$/ do\n pending\nend"
17
+ end
18
+
19
+ class MissingProc < StandardError
20
+ def message
21
+ "Step definitions must always have a proc"
22
+ end
23
+ end
24
+
25
+ attr_reader :regexp
26
+
27
+ def initialize(pattern, &proc)
28
+ raise MissingProc if proc.nil?
29
+ if String === pattern
30
+ p = pattern.gsub(/\$\w+/, '(.*)')
31
+ pattern = Regexp.new("^#{p}$")
32
+ end
33
+ @regexp, @proc = pattern, proc
34
+ end
35
+
36
+ #:stopdoc:
37
+
38
+ def match(step_name)
39
+ case step_name
40
+ when String then @regexp.match(step_name)
41
+ when Regexp then @regexp == step_name
42
+ end
43
+ end
44
+
45
+ # Formats the matched arguments of the associated Step. This method
46
+ # is usually called from visitors, which render output.
47
+ #
48
+ # The +format+ either be a String or a Proc.
49
+ #
50
+ # If it is a String it should be a format string according to
51
+ # <tt>Kernel#sprinf</tt>, for example:
52
+ #
53
+ # '<span class="param">%s</span></tt>'
54
+ #
55
+ # If it is a Proc, it should take one argument and return the formatted
56
+ # argument, for example:
57
+ #
58
+ # lambda { |param| "[#{param}]" }
59
+ #
60
+ def format_args(step_name, format)
61
+ step_name.gzub(@regexp, format)
62
+ end
63
+
64
+ def matched_args(step_name)
65
+ step_name.match(@regexp).captures
66
+ end
67
+
68
+ def execute(step_name, world, *args)
69
+ args = args.map{|arg| Ast::PyString === arg ? arg.to_s : arg}
70
+ begin
71
+ world.cucumber_instance_exec(true, @regexp.inspect, *args, &@proc)
72
+ rescue Cucumber::ArityMismatchError => e
73
+ e.backtrace.unshift(self.to_backtrace_line)
74
+ raise e
75
+ end
76
+ end
77
+
78
+ def to_backtrace_line
79
+ "#{file_colon_line}:in `#{@regexp.inspect}'"
80
+ end
81
+
82
+ def file_colon_line
83
+ @proc.file_colon_line
84
+ end
85
+
86
+ end
87
+ end
@@ -1,98 +1,183 @@
1
- require 'cucumber/tree/top_down_visitor'
1
+ require 'cucumber/step_definition'
2
+ require 'cucumber/core_ext/instance_exec'
2
3
 
3
4
  module Cucumber
4
- class Pending < StandardError
5
- end
5
+ class Undefined < StandardError
6
+ attr_reader :step_name
6
7
 
7
- class ForcedPending < Pending
8
+ def initialize(step_name)
9
+ super %{Undefined step: "#{step_name}"}
10
+ @step_name = step_name
11
+ end
12
+ Cucumber::EXCEPTION_STATUS[self] = :undefined
8
13
  end
9
14
 
10
- class Duplicate < StandardError
15
+ class Pending < StandardError
16
+ Cucumber::EXCEPTION_STATUS[self] = :pending
11
17
  end
12
18
 
13
- class Multiple < StandardError
19
+ # Raised when a step matches 2 or more StepDefinition
20
+ class Ambiguous < StandardError
21
+ def initialize(step_name, step_definitions)
22
+ message = "Ambiguous match of \"#{step_name}\":\n\n"
23
+ message << step_definitions.map{|sd| sd.to_backtrace_line}.join("\n")
24
+ message << "\n\n"
25
+ super(message)
26
+ end
14
27
  end
15
28
 
16
- class MissingProc < StandardError
17
- def message
18
- "Step definitions must always have a proc"
29
+ # Raised when 2 or more StepDefinition have the same Regexp
30
+ class Redundant < StandardError
31
+ def initialize(step_def_1, step_def_2)
32
+ message = "Multiple step definitions have the same Regexp:\n\n"
33
+ message << step_def_1.to_backtrace_line << "\n"
34
+ message << step_def_2.to_backtrace_line << "\n\n"
35
+ super(message)
19
36
  end
20
37
  end
21
38
 
22
- class StepMother
23
- PENDING = lambda do |*_|
24
- raise Pending
39
+ # This is the main interface for registering step definitions, which is done
40
+ # from <tt>*_steps.rb</tt> files. This module is included right at the top-level
41
+ # so #register_step_definition (and more interestingly - its aliases) are
42
+ # available from the top-level.
43
+ module StepMother
44
+ attr_writer :snippet_generator
45
+ attr_writer :options
46
+
47
+ # Registers a new StepDefinition. This method is aliased
48
+ # to <tt>Given</tt>, <tt>When</tt> and <tt>Then</tt>.
49
+ #
50
+ # See Cucumber#alias_steps for details on how to
51
+ # create your own aliases.
52
+ #
53
+ # The +&proc+ gets executed in the context of a <tt>world</tt>
54
+ # object, which is defined by #World. A new <tt>world</tt>
55
+ # object is created for each scenario and is shared across
56
+ # step definitions within that scenario.
57
+ def register_step_definition(regexp, &proc)
58
+ step_definition = StepDefinition.new(regexp, &proc)
59
+ step_definitions.each do |already|
60
+ raise Redundant.new(already, step_definition) if already.match(regexp)
61
+ end
62
+ step_definitions << step_definition
63
+ step_definition
25
64
  end
26
- require 'cucumber/core_ext/proc'
27
- PENDING.extend(CoreExt::CallIn)
28
- PENDING.name = "PENDING"
29
65
 
30
- def initialize
31
- @step_procs = Hash.new(PENDING)
66
+ def world(scenario, prior_world = nil, &proc)
67
+ world = prior_world || new_world
68
+ begin
69
+ (@before_procs ||= []).each do |proc|
70
+ world.cucumber_instance_exec(false, 'Before', scenario, &proc)
71
+ end
72
+ yield world
73
+ ensure
74
+ (@after_procs ||= []).each do |proc|
75
+ world.cucumber_instance_exec(false, 'After', scenario, &proc)
76
+ end
77
+ end
32
78
  end
33
79
 
34
- def register_step_proc(key, &proc)
35
- raise MissingProc if proc.nil?
36
- regexp = case(key)
37
- when String
38
- # Replace the $foo and $bar style parameters
39
- pattern = key.gsub(/\$\w+/, '(.*)')
40
- Regexp.new("^#{pattern}$")
41
- when Regexp
42
- key
43
- else
44
- raise "Step patterns must be Regexp or String, but was: #{key.inspect}"
45
- end
46
- proc.extend(CoreExt::CallIn)
47
- proc.name = key.inspect
80
+ # Registers a Before proc. You can call this method as many times as you
81
+ # want (typically from ruby scripts under <tt>support</tt>).
82
+ def Before(&proc)
83
+ (@before_procs ||= []) << proc
84
+ end
48
85
 
49
- if @step_procs.has_key?(regexp)
50
- first_proc = @step_procs[regexp]
51
- message = %{Duplicate step definitions:
86
+ def After(&proc)
87
+ (@after_procs ||= []) << proc
88
+ end
52
89
 
53
- #{first_proc.to_backtrace_line}
54
- #{proc.to_backtrace_line}
90
+ # Registers a World proc. You can call this method as many times as you
91
+ # want (typically from ruby scripts under <tt>support</tt>).
92
+ def World(&proc)
93
+ (@world_procs ||= []) << proc
94
+ end
55
95
 
56
- }
57
- raise Duplicate.new(message)
96
+ # Creates a new world instance
97
+ def new_world #:nodoc:
98
+ world = Object.new
99
+ (@world_procs ||= []).each do |proc|
100
+ world = proc.call(world)
58
101
  end
59
102
 
60
- @step_procs[regexp] = proc
61
- end
103
+ world.extend(WorldMethods)
104
+ world.__cucumber_step_mother = self
62
105
 
63
- def regexp_args_proc(step_name)
64
- candidates = @step_procs.map do |regexp, proc|
65
- if step_name =~ regexp
66
- [regexp, $~.captures, proc]
67
- end
68
- end.compact
69
-
70
- case(candidates.length)
71
- when 0
72
- [nil, [], PENDING]
73
- when 1
74
- candidates[0]
75
- else
76
- message = %{Multiple step definitions match #{step_name.inspect}:
106
+ world.extend(::Spec::Matchers) if defined?(::Spec::Matchers)
107
+ world
108
+ end
77
109
 
78
- #{candidates.map{|regexp, args, proc| proc.to_backtrace_line}.join("\n")}
110
+ # Looks up the StepDefinition that matches +step_name+
111
+ def step_definition(step_name) #:nodoc:
112
+ found = step_definitions.select do |step_definition|
113
+ step_definition.match(step_name)
114
+ end
115
+ raise Undefined.new(step_name) if found.empty?
116
+ found = best_matches(step_name, found) if found.size > 1 && options[:guess]
117
+ raise Ambiguous.new(step_name, found) if found.size > 1
118
+ found[0]
119
+ end
79
120
 
80
- }
81
- raise Multiple.new(message)
121
+ def best_matches(step_name, step_definitions)
122
+ top_group_score = step_definitions.map {|s| s.match(step_name).captures.length }.sort.last
123
+ top_groups = step_definitions.select {|s| s.match(step_name).captures.length == top_group_score }
124
+ if top_groups.size > 1
125
+ shortest_capture_length = top_groups.map {|s| s.match(step_name).captures.inject(0) {|sum, c| sum + c.length } }.sort.first
126
+ top_groups.select {|s| s.match(step_name).captures.inject(0) {|sum, c| sum + c.length } == shortest_capture_length }
127
+ else
128
+ top_groups
82
129
  end
83
- end
84
-
85
- def proc_for(regexp)
86
- @step_procs[regexp]
87
130
  end
88
131
 
89
- def has_step_definition?(step_name)
90
- _, _, proc = regexp_args_proc(step_name)
91
- proc != PENDING
132
+ def step_definitions
133
+ @step_definitions ||= []
92
134
  end
135
+
136
+ def snippet_text(step_keyword, step_name)
137
+ @snippet_generator.snippet_text(step_keyword, step_name)
138
+ end
139
+
140
+ private
93
141
 
94
- # TODO - move execute here?
95
- def execute(step)
142
+ def options
143
+ @options || {}
144
+ end
145
+
146
+ module WorldMethods #:nodoc:
147
+ attr_writer :__cucumber_step_mother, :__cucumber_current_step
148
+
149
+ # Call a step from within a step definition
150
+ def __cucumber_invoke(name, *multiline_arguments)
151
+ begin
152
+ # TODO: Very similar to code in Step. Refactor. Get back StepInvocation?
153
+ # Make more similar to JBehave?
154
+ step_definition = @__cucumber_step_mother.step_definition(name)
155
+ matched_args = step_definition.matched_args(name)
156
+ args = (matched_args + multiline_arguments)
157
+ step_definition.execute(name, self, *args)
158
+ rescue Exception => e
159
+ @__cucumber_current_step.exception = e
160
+ raise e
161
+ end
162
+ end
163
+
164
+ def table(text, file=nil, line=0)
165
+ @table_parser ||= Parser::TableParser.new
166
+ @table_parser.parse_or_fail(text.strip, file, line)
167
+ end
168
+
169
+ def pending(message = "TODO")
170
+ if block_given?
171
+ begin
172
+ yield
173
+ rescue Exception => e
174
+ raise Pending.new(message)
175
+ end
176
+ raise Pending.new("Expected pending '#{message}' to fail. No Error was raised. No longer pending?")
177
+ else
178
+ raise Pending.new(message)
179
+ end
180
+ end
96
181
  end
97
182
  end
98
183
  end