kosmas58-cucumber 0.3.11.3 → 0.3.11.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/History.txt +36 -0
  2. data/Manifest.txt +9 -0
  3. data/config/hoe.rb +1 -2
  4. data/examples/i18n/ar/Rakefile +1 -1
  5. data/examples/i18n/ar/features/addition.feature +1 -0
  6. data/examples/i18n/bg/Rakefile +1 -1
  7. data/examples/i18n/bg/features/addition.feature +1 -0
  8. data/examples/i18n/bg/features/consecutive_calculations.feature +1 -0
  9. data/examples/i18n/bg/features/division.feature +1 -0
  10. data/examples/i18n/cat/Rakefile +1 -1
  11. data/examples/i18n/cat/features/suma.feature +1 -0
  12. data/examples/i18n/da/Rakefile +1 -1
  13. data/examples/i18n/da/features/summering.feature +1 -0
  14. data/examples/i18n/de/Rakefile +1 -1
  15. data/examples/i18n/de/features/addition.feature +1 -0
  16. data/examples/i18n/de/features/division.feature +1 -0
  17. data/examples/i18n/en/Rakefile +1 -1
  18. data/examples/i18n/en/features/addition.feature +1 -0
  19. data/examples/i18n/en/features/division.feature +1 -0
  20. data/examples/i18n/en-lol/features/stuffing.feature +1 -1
  21. data/examples/i18n/es/Rakefile +1 -1
  22. data/examples/i18n/es/features/adicion.feature +1 -0
  23. data/examples/i18n/et/Rakefile +1 -1
  24. data/examples/i18n/et/features/jagamine.feature +1 -0
  25. data/examples/i18n/et/features/liitmine.feature +1 -0
  26. data/examples/i18n/fi/Rakefile +1 -1
  27. data/examples/i18n/fi/features/jakolasku.feature +1 -0
  28. data/examples/i18n/fr/Rakefile +1 -1
  29. data/examples/i18n/fr/features/addition.feature +2 -1
  30. data/examples/i18n/he/Rakefile +1 -1
  31. data/examples/i18n/he/features/addition.feature +1 -0
  32. data/examples/i18n/he/features/division.feature +1 -0
  33. data/examples/i18n/hu/Rakefile +1 -1
  34. data/examples/i18n/hu/features/addition.feature +1 -0
  35. data/examples/i18n/hu/features/division.feature +1 -0
  36. data/examples/i18n/id/Rakefile +1 -1
  37. data/examples/i18n/id/features/addition.feature +1 -0
  38. data/examples/i18n/id/features/division.feature +1 -0
  39. data/examples/i18n/it/Rakefile +1 -1
  40. data/examples/i18n/it/features/somma.feature +1 -0
  41. data/examples/i18n/ja/Rakefile +1 -1
  42. data/examples/i18n/ja/features/addition.feature +1 -0
  43. data/examples/i18n/ja/features/division.feature +1 -0
  44. data/examples/i18n/ko/Rakefile +1 -1
  45. data/examples/i18n/ko/features/addition.feature +1 -0
  46. data/examples/i18n/ko/features/division.feature +1 -0
  47. data/examples/i18n/lt/Rakefile +1 -1
  48. data/examples/i18n/lt/features/addition.feature +1 -0
  49. data/examples/i18n/lt/features/division.feature +1 -0
  50. data/examples/i18n/lv/Rakefile +1 -1
  51. data/examples/i18n/lv/features/addition.feature +1 -0
  52. data/examples/i18n/lv/features/division.feature +1 -0
  53. data/examples/i18n/no/Rakefile +1 -1
  54. data/examples/i18n/no/features/summering.feature +1 -0
  55. data/examples/i18n/pl/Rakefile +1 -1
  56. data/examples/i18n/pl/features/addition.feature +1 -0
  57. data/examples/i18n/pl/features/division.feature +1 -0
  58. data/examples/i18n/pt/Rakefile +1 -1
  59. data/examples/i18n/pt/features/adicao.feature +1 -0
  60. data/examples/i18n/ro/Rakefile +1 -1
  61. data/examples/i18n/ro/features/suma.feature +1 -0
  62. data/examples/i18n/ru/Rakefile +1 -1
  63. data/examples/i18n/ru/features/addition.feature +1 -0
  64. data/examples/i18n/ru/features/consecutive_calculations.feature +1 -0
  65. data/examples/i18n/ru/features/division.feature +1 -0
  66. data/examples/i18n/se/Rakefile +1 -1
  67. data/examples/i18n/se/features/summering.feature +1 -0
  68. data/examples/i18n/sk/Rakefile +1 -1
  69. data/examples/i18n/sk/features/addition.feature +1 -0
  70. data/examples/i18n/sk/features/division.feature +1 -0
  71. data/examples/i18n/zh-CN/features/addition.feature +1 -0
  72. data/examples/i18n/zh-TW/features/addition.feature +1 -0
  73. data/examples/i18n/zh-TW/features/division.feature +1 -0
  74. data/examples/sinatra/features/step_definitions/add_steps.rb +1 -1
  75. data/examples/sinatra/features/support/env.rb +13 -5
  76. data/features/after_block_exceptions.feature +4 -1
  77. data/features/after_step_block_exceptions.feature +4 -1
  78. data/features/background.feature +6 -0
  79. data/features/cucumber_cli.feature +11 -1
  80. data/features/cucumber_cli_diff_disabled.feature +7 -1
  81. data/features/drb_server_integration.feature +5 -4
  82. data/features/expand.feature +2 -1
  83. data/features/html_formatter/a.html +12 -14
  84. data/features/junit_formatter.feature +4 -4
  85. data/features/step_definitions/cucumber_steps.rb +2 -2
  86. data/features/support/env.rb +7 -4
  87. data/features/work_in_progress.feature +3 -0
  88. data/lib/cucumber/ast/comment.rb +1 -1
  89. data/lib/cucumber/ast/feature.rb +2 -2
  90. data/lib/cucumber/ast/feature_element.rb +4 -0
  91. data/lib/cucumber/ast/scenario.rb +5 -3
  92. data/lib/cucumber/ast/scenario_outline.rb +6 -1
  93. data/lib/cucumber/ast/step.rb +4 -0
  94. data/lib/cucumber/ast/step_invocation.rb +6 -1
  95. data/lib/cucumber/cli/configuration.rb +37 -26
  96. data/lib/cucumber/cli/main.rb +4 -5
  97. data/lib/cucumber/formatter/console.rb +12 -0
  98. data/lib/cucumber/formatter/html.rb +3 -2
  99. data/lib/cucumber/formatter/junit.rb +3 -6
  100. data/lib/cucumber/formatter/pretty.rb +2 -4
  101. data/lib/cucumber/formatter/profile.rb +1 -1
  102. data/lib/cucumber/languages.yml +3 -3
  103. data/lib/cucumber/parser/feature.rb +12 -16
  104. data/lib/cucumber/parser/feature.tt +1 -3
  105. data/lib/cucumber/parser/i18n.tt +30 -23
  106. data/lib/cucumber/parser/treetop_ext.rb +12 -83
  107. data/lib/cucumber/parser.rb +1 -33
  108. data/lib/cucumber/platform.rb +6 -0
  109. data/lib/cucumber/step_definition.rb +6 -0
  110. data/lib/cucumber/step_mother.rb +3 -3
  111. data/lib/cucumber/version.rb +1 -1
  112. data/lib/cucumber.rb +0 -57
  113. data/rails_generators/cucumber/templates/cucumber.rake +4 -0
  114. data/spec/cucumber/ast/step_collection_spec.rb +5 -4
  115. data/spec/cucumber/cli/configuration_spec.rb +42 -9
  116. data/spec/cucumber/cli/main_spec.rb +2 -10
  117. data/spec/cucumber/parser/feature_parser_spec.rb +11 -9
  118. data/spec/cucumber/parser/table_parser_spec.rb +1 -1
  119. data/spec/spec_helper.rb +0 -1
  120. metadata +3 -13
@@ -25,12 +25,14 @@ module Cucumber
25
25
  visitor.visit_tags(@tags)
26
26
  visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
27
27
 
28
- skip = @background && @background.failed?
29
- skip_invoke! if skip
30
- visitor.step_mother.before_and_after(self, skip) do
28
+ background_failed = @background && @background.failed?
29
+ skip_invoke! if background_failed
30
+ skip_hooks = background_failed || @executed
31
+ visitor.step_mother.before_and_after(self, skip_hooks) do
31
32
  visitor.visit_steps(@steps)
32
33
  end
33
34
  visitor.visit_exception(@exception, :failed) if @exception
35
+ @executed = true
34
36
  end
35
37
 
36
38
  # Returns true if one or more steps failed
@@ -69,7 +69,12 @@ module Cucumber
69
69
  end
70
70
 
71
71
  def visit_scenario_name(visitor, row)
72
- visitor.visit_scenario_name(Cucumber.scenario_keyword, row.name, file_colon_line(row.line), source_indent(first_line_length))
72
+ visitor.visit_scenario_name(
73
+ @feature.language.scenario_keyword,
74
+ row.name,
75
+ file_colon_line(row.line),
76
+ source_indent(first_line_length)
77
+ )
73
78
  end
74
79
 
75
80
  def to_sexp
@@ -74,6 +74,10 @@ module Cucumber
74
74
  @file_colon_line ||= @feature_element.file_colon_line(@line) unless @feature_element.nil?
75
75
  end
76
76
 
77
+ def language
78
+ @feature_element.language
79
+ end
80
+
77
81
  def dom_id
78
82
  @dom_id ||= file_colon_line.gsub(/\//, '_').gsub(/\./, '_').gsub(/:/, '_')
79
83
  end
@@ -88,7 +88,8 @@ module Cucumber
88
88
  end
89
89
 
90
90
  def actual_keyword
91
- if [Cucumber.keyword_hash['and'], Cucumber.keyword_hash['but']].index(@step.keyword) && previous
91
+ repeat_keywords = [language.but_keywords, language.and_keywords].flatten
92
+ if repeat_keywords.index(@step.keyword) && previous
92
93
  previous.actual_keyword
93
94
  else
94
95
  keyword
@@ -123,6 +124,10 @@ module Cucumber
123
124
  @step.backtrace_line
124
125
  end
125
126
 
127
+ def language
128
+ @step.language
129
+ end
130
+
126
131
  def to_sexp
127
132
  [:step_invocation, @step.line, @step.keyword, @name, (@multiline_arg.nil? ? nil : @multiline_arg.to_sexp)].compact
128
133
  end
@@ -4,15 +4,16 @@ module Cucumber
4
4
 
5
5
  class Configuration
6
6
  BUILTIN_FORMATS = {
7
- 'html' => 'Cucumber::Formatter::Html',
8
- 'pretty' => 'Cucumber::Formatter::Pretty',
9
- 'profile' => 'Cucumber::Formatter::Profile',
10
- 'progress' => 'Cucumber::Formatter::Progress',
11
- 'rerun' => 'Cucumber::Formatter::Rerun',
12
- 'usage' => 'Cucumber::Formatter::Usage',
13
- 'junit' => 'Cucumber::Formatter::Junit'
7
+ 'html' => 'Cucumber::Formatter::Html',
8
+ 'pretty' => 'Cucumber::Formatter::Pretty',
9
+ 'profile' => 'Cucumber::Formatter::Profile',
10
+ 'progress' => 'Cucumber::Formatter::Progress',
11
+ 'rerun' => 'Cucumber::Formatter::Rerun',
12
+ 'usage' => 'Cucumber::Formatter::Usage',
13
+ 'junit' => 'Cucumber::Formatter::Junit',
14
+ 'tag_cloud' => 'Cucumber::Formatter::TagCloud',
15
+ 'steps' => 'Cucumber::Formatter::Steps'
14
16
  }
15
- DEFAULT_FORMAT = 'pretty'
16
17
  DRB_FLAG = '--drb'
17
18
  PROFILE_SHORT_FLAG = '-p'
18
19
  PROFILE_LONG_FLAG = '--profile'
@@ -26,15 +27,21 @@ module Cucumber
26
27
 
27
28
  @paths = []
28
29
  @options = default_options
29
-
30
- @active_format = DEFAULT_FORMAT
31
30
  end
32
31
 
33
32
  def parse!(args)
34
- @args = args.empty? ? args_from_profile('default') : args
33
+ args.concat(%w{--profile default}) if args.empty?
34
+ @args = args
35
35
  expand_profiles_into_args
36
36
  return if parse_drb
37
37
 
38
+ @args.each do |arg|
39
+ if arg =~ /^(\w+)=(.*)$/
40
+ ENV[$1] = $2
41
+ @args.delete(arg)
42
+ end
43
+ end
44
+
38
45
  @args.extend(::OptionParser::Arguable)
39
46
 
40
47
  @args.options do |opts|
@@ -69,7 +76,7 @@ module Cucumber
69
76
  end
70
77
  end
71
78
  opts.on("-f FORMAT", "--format FORMAT",
72
- "How to format features (Default: #{DEFAULT_FORMAT})",
79
+ "How to format features (Default: pretty)",
73
80
  "Available formats: #{BUILTIN_FORMATS.keys.sort.join(", ")}",
74
81
  "FORMAT can also be the fully qualified class name of",
75
82
  "your own custom formatter. If the class isn't loaded,",
@@ -79,7 +86,7 @@ module Cucumber
79
86
  "foo/bar_zap.rb. You can place the file with this relative",
80
87
  "path underneath your features/support directory or anywhere",
81
88
  "on Ruby's LOAD_PATH, for example in a Ruby gem.") do |v|
82
- @options[:formats][v] = @out_stream
89
+ @options[:formats] << [v, @out_stream]
83
90
  @active_format = v
84
91
  end
85
92
  opts.on("-o", "--out [FILE|DIR]",
@@ -87,7 +94,8 @@ module Cucumber
87
94
  "applies to the previously specified --format, or the",
88
95
  "default format if no format is specified. Check the specific",
89
96
  "formatter's docs to see whether to pass a file or a dir.") do |v|
90
- @options[:formats][@active_format] = v
97
+ @options[:formats] << ['pretty', nil] if @options[:formats].empty?
98
+ @options[:formats][-1][1] = v
91
99
  end
92
100
  opts.on("-t TAGS", "--tags TAGS",
93
101
  "Only execute the features or scenarios with the specified tags.",
@@ -176,7 +184,7 @@ module Cucumber
176
184
  end
177
185
  end.parse!
178
186
 
179
- @options[:formats]['pretty'] = @out_stream if @options[:formats].empty?
187
+ arrange_formats
180
188
 
181
189
  @options[:snippets] = true if !@quiet && @options[:snippets].nil?
182
190
  @options[:source] = true if !@quiet && @options[:source].nil?
@@ -211,14 +219,6 @@ module Cucumber
211
219
  @drb
212
220
  end
213
221
 
214
- def load_language
215
- if Cucumber.language_incomplete?(@options[:lang])
216
- list_keywords_and_exit(@options[:lang])
217
- else
218
- Cucumber.load_language(@options[:lang])
219
- end
220
- end
221
-
222
222
  def parse_tags(tag_string)
223
223
  tag_names = tag_string.split(",")
224
224
  excludes, includes = tag_names.partition{|tag| tag =~ /^~/}
@@ -232,7 +232,9 @@ module Cucumber
232
232
 
233
233
  def build_formatter_broadcaster(step_mother)
234
234
  return Formatter::Pretty.new(step_mother, nil, @options) if @options[:autoformat]
235
- formatters = @options[:formats].map do |format, out|
235
+ formatters = @options[:formats].map do |format_and_out|
236
+ format = format_and_out[0]
237
+ out = format_and_out[1]
236
238
  if String === out # file name
237
239
  unless File.directory?(out)
238
240
  out = File.open(out, Cucumber.file_mode('w'))
@@ -292,6 +294,14 @@ module Cucumber
292
294
 
293
295
  protected
294
296
 
297
+ def arrange_formats
298
+ @options[:formats] << ['pretty', @out_stream] if @options[:formats].empty?
299
+ @options[:formats] = @options[:formats].sort_by{|f| f[1] == @out_stream ? -1 : 1}
300
+ if @options[:formats].length > 1 && @options[:formats][1][1] == @out_stream
301
+ raise "All but one formatter must use --out, only one can print to STDOUT"
302
+ end
303
+ end
304
+
295
305
  def remove_excluded_files_from(files)
296
306
  files.reject! {|path| @options[:excludes].detect {|pattern| path =~ pattern } }
297
307
  end
@@ -377,6 +387,7 @@ Defined profiles in cucumber.yml:
377
387
  return @cucumber_yml
378
388
  end
379
389
 
390
+ # TODO: Move to Language
380
391
  def list_keywords_and_exit(lang)
381
392
  unless Cucumber::LANGUAGES[lang]
382
393
  raise("No language with key #{lang}")
@@ -398,9 +409,9 @@ Defined profiles in cucumber.yml:
398
409
  {
399
410
  :strict => false,
400
411
  :require => nil,
401
- :lang => 'en',
412
+ :lang => nil,
402
413
  :dry_run => false,
403
- :formats => {},
414
+ :formats => [],
404
415
  :excludes => [],
405
416
  :include_tags => [],
406
417
  :exclude_tags => [],
@@ -2,6 +2,7 @@ require 'optparse'
2
2
  require 'cucumber'
3
3
  require 'ostruct'
4
4
  require 'cucumber/parser'
5
+ require 'cucumber/feature_file'
5
6
  require 'cucumber/formatter/color_io'
6
7
  require 'cucumber/cli/language_help_formatter'
7
8
  require 'cucumber/cli/configuration'
@@ -41,13 +42,11 @@ module Cucumber
41
42
  configuration.parse!(@args)
42
43
  end
43
44
  end
44
- configuration.load_language
45
45
  step_mother.options = configuration.options
46
46
 
47
+ features = load_plain_text_features
47
48
  require_files
48
49
  enable_diffing
49
-
50
- features = load_plain_text_features
51
50
 
52
51
  visitor = configuration.build_formatter_broadcaster(step_mother)
53
52
  step_mother.visitor = visitor # Needed to support World#announce
@@ -63,11 +62,11 @@ module Cucumber
63
62
 
64
63
  def load_plain_text_features
65
64
  features = Ast::Features.new
66
- parser = Parser::FeatureParser.new
67
65
 
68
66
  verbose_log("Features:")
69
67
  configuration.feature_files.each do |f|
70
- feature = parser.parse_file(f, configuration.options)
68
+ feature_file = FeatureFile.new(f)
69
+ feature = feature_file.parse(configuration.options)
71
70
  if feature
72
71
  features.add_feature(feature)
73
72
  verbose_log(" * #{f}")
@@ -59,6 +59,18 @@ module Cucumber
59
59
  end
60
60
 
61
61
  def print_stats(features)
62
+
63
+ @failures = step_mother.scenarios(:failed).select { |s| s.is_a?(Cucumber::Ast::Scenario) }
64
+
65
+ if !@failures.empty?
66
+ @io.puts format_string("Failing Scenarios:", :failed)
67
+ @failures.each do |failure|
68
+ @io.puts format_string("cucumber " + failure.file_colon_line, :failed) +
69
+ format_string(" # Scenario: " + failure.name, :comment)
70
+ end
71
+ @io.puts
72
+ end
73
+
62
74
  @io.print dump_count(step_mother.scenarios.length, "scenario")
63
75
  print_status_counts{|status| step_mother.scenarios(status)}
64
76
 
@@ -54,7 +54,8 @@ module Cucumber
54
54
  end
55
55
 
56
56
  def visit_comment_line(comment_line)
57
- @builder.text!(comment_line.strip + "\n")
57
+ @builder.text!(comment_line)
58
+ @builder.br
58
59
  end
59
60
 
60
61
  def visit_feature(feature)
@@ -73,7 +74,7 @@ module Cucumber
73
74
  @builder.h2 do |h2|
74
75
  @builder.span(lines[0], :class => 'val')
75
76
  end
76
- @builder.p do
77
+ @builder.p(:class => 'narrative') do
77
78
  lines[1..-1].each do |line|
78
79
  @builder.text!(line.strip)
79
80
  @builder.br
@@ -31,13 +31,14 @@ module Cucumber
31
31
  @testsuite << @builder.target!
32
32
  end
33
33
 
34
- File.open(@feature_filename, 'w') { |file| file.write(@testsuite.target!) }
34
+ basename = File.basename(feature.file)[0...-File.extname(feature.file).length]
35
+ feature_filename = File.join(@reportdir, "TEST-#{basename}.xml")
36
+ File.open(feature_filename, 'w') { |file| file.write(@testsuite.target!) }
35
37
  end
36
38
 
37
39
  def visit_feature_name(name)
38
40
  lines = name.split(/\r?\n/)
39
41
  @feature_name = lines[0].sub(/Feature\:/, '').strip
40
- @feature_filename = convert_to_file_name(@feature_name)
41
42
  end
42
43
 
43
44
  def visit_scenario_name(keyword, name, file_colon_line, source_indent)
@@ -65,10 +66,6 @@ module Cucumber
65
66
 
66
67
  private
67
68
 
68
- def convert_to_file_name(feature_name)
69
- File.join(@reportdir, "TEST-" + feature_name.gsub(/[^\w_\.]/, '_') + ".xml")
70
- end
71
-
72
69
  def format_exception(exception)
73
70
  (["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n")
74
71
  end
@@ -48,10 +48,8 @@ module Cucumber
48
48
  end
49
49
 
50
50
  def visit_comment_line(comment_line)
51
- unless comment_line.blank?
52
- @io.puts(comment_line.indent(@indent))
53
- @io.flush
54
- end
51
+ @io.puts(comment_line.indent(@indent))
52
+ @io.flush
55
53
  end
56
54
 
57
55
  def visit_tags(tags)
@@ -17,7 +17,7 @@ module Cucumber
17
17
  super
18
18
  end
19
19
 
20
- def visit_step_name(keyword, step_match, status, source_indent, background)
20
+ def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
21
21
  duration = Time.now - @step_duration
22
22
  super
23
23
 
@@ -91,14 +91,14 @@
91
91
  native: Česky
92
92
  encoding: UTF-8
93
93
  feature: Požadavek
94
- background: Pozadí
94
+ background: Pozadí|Kontext
95
95
  scenario: Scénář
96
- scenario_outline: Náčrt Scénáře
96
+ scenario_outline: Náčrt Scénáře|Osnova scénáře
97
97
  examples: Příklady
98
98
  given: Pokud
99
99
  when: Když
100
100
  then: Pak
101
- and: A
101
+ and: A|A také
102
102
  but: Ale
103
103
  space_after_keyword: true
104
104
  "da":
@@ -10,17 +10,13 @@ module Cucumber
10
10
  include Treetop::Runtime
11
11
 
12
12
  def root
13
- @root || :feature
13
+ @root || :feature_sub
14
14
  end
15
15
 
16
- include I18n
17
-
18
- include Table
19
-
20
- module Feature0
16
+ module FeatureSub0
21
17
  end
22
18
 
23
- module Feature1
19
+ module FeatureSub1
24
20
  def white
25
21
  elements[0]
26
22
  end
@@ -55,7 +51,7 @@ module Cucumber
55
51
 
56
52
  end
57
53
 
58
- module Feature2
54
+ module FeatureSub2
59
55
  def has_tags?(tag_names)
60
56
  tags.has_tags?(tag_names)
61
57
  end
@@ -74,10 +70,10 @@ module Cucumber
74
70
  end
75
71
  end
76
72
 
77
- def _nt_feature
73
+ def _nt_feature_sub
78
74
  start_index = index
79
- if node_cache[:feature].has_key?(index)
80
- cached = node_cache[:feature][index]
75
+ if node_cache[:feature_sub].has_key?(index)
76
+ cached = node_cache[:feature_sub][index]
81
77
  @index = cached.interval.end if cached
82
78
  return cached
83
79
  end
@@ -139,7 +135,7 @@ module Cucumber
139
135
  end
140
136
  if s7.last
141
137
  r7 = instantiate_node(SyntaxNode,input, i7...index, s7)
142
- r7.extend(Feature0)
138
+ r7.extend(FeatureSub0)
143
139
  else
144
140
  self.index = i7
145
141
  r7 = nil
@@ -181,14 +177,14 @@ module Cucumber
181
177
  end
182
178
  if s0.last
183
179
  r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
184
- r0.extend(Feature1)
185
- r0.extend(Feature2)
180
+ r0.extend(FeatureSub1)
181
+ r0.extend(FeatureSub2)
186
182
  else
187
183
  self.index = i0
188
184
  r0 = nil
189
185
  end
190
186
 
191
- node_cache[:feature][start_index] = r0
187
+ node_cache[:feature_sub][start_index] = r0
192
188
 
193
189
  return r0
194
190
  end
@@ -1346,7 +1342,7 @@ module Cucumber
1346
1342
 
1347
1343
  module LinesToKeyword2
1348
1344
  def build
1349
- self.text_value.split("\n").map{|s| s.strip }.join("\n")
1345
+ self.text_value.split("\n").map{|s| s.strip}.join("\n")
1350
1346
  end
1351
1347
  end
1352
1348
 
@@ -7,10 +7,8 @@ module Cucumber
7
7
  # Treetop will then generate the parser in-memory. When you're happy, just generate
8
8
  # the rb file with tt feature.tt
9
9
  grammar Feature
10
- include I18n
11
- include Table
12
10
 
13
- rule feature
11
+ rule feature_sub
14
12
  white
15
13
  comment
16
14
  white
@@ -1,35 +1,42 @@
1
1
  module Cucumber
2
2
  module Parser
3
- grammar I18n
3
+ module I18n
4
+ grammar <%= keywords('grammar_name', true) %>
5
+ include Feature
6
+ include Table
4
7
 
5
- rule background_keyword
6
- (<%= keywords('background') %>) ':'
7
- end
8
+ rule feature
9
+ feature_sub
10
+ end
8
11
 
9
- rule scenario_keyword
10
- (<%= keywords('scenario') %>) ':'
11
- end
12
+ rule background_keyword
13
+ (<%= keywords('background') %>) ':'
14
+ end
12
15
 
13
- rule scenario_outline_keyword
14
- (<%= keywords('scenario_outline') %>) ':'
15
- end
16
+ rule scenario_keyword
17
+ (<%= keywords('scenario') %>) ':'
18
+ end
16
19
 
17
- rule step_keyword
18
- (<%= keywords('given') %>) /
19
- (<%= keywords('when') %>) /
20
- (<%= keywords('then') %>) /
21
- (<%= keywords('and') %>) /
22
- (<%= keywords('but') %>)
23
- end
20
+ rule scenario_outline_keyword
21
+ (<%= keywords('scenario_outline') %>) ':'
22
+ end
24
23
 
25
- rule keyword_space
26
- <%= keywords('space_after_keyword', true) ? 'space+' : 'space*' %>
27
- end
24
+ rule step_keyword
25
+ (<%= keywords('given') %>) /
26
+ (<%= keywords('when') %>) /
27
+ (<%= keywords('then') %>) /
28
+ (<%= keywords('and') %>) /
29
+ (<%= keywords('but') %>)
30
+ end
28
31
 
29
- rule examples_keyword
30
- (<%= keywords('examples') %>) ':'?
31
- end
32
+ rule keyword_space
33
+ <%= keywords('space_after_keyword', true) ? 'space+' : 'space*' %>
34
+ end
32
35
 
36
+ rule examples_keyword
37
+ (<%= keywords('examples') %>) ':'?
38
+ end
39
+ end
33
40
  end
34
41
  end
35
42
  end
@@ -12,80 +12,19 @@ end
12
12
 
13
13
  module Cucumber
14
14
  module Parser
15
- class Filter
16
- def initialize(lines, options)
17
- @lines = lines
18
- @include_tags = options[:include_tags] || []
19
- @exclude_tags = options[:exclude_tags] || []
20
- @name_regexps = options[:name_regexps] || []
21
- end
22
-
23
- def accept?(syntax_node)
24
- at_line?(syntax_node) &&
25
- matches_tags?(syntax_node) &&
26
- matches_names?(syntax_node)
27
- end
28
-
29
- def accept_example?(syntax_node, outline)
30
- (at_line?(syntax_node) || outline_at_line?(outline)) &&
31
- (matches_names?(syntax_node) || outline_matches_names?(outline))
32
- end
33
-
34
- def at_line?(syntax_node)
35
- @lines.nil? || @lines.empty? || @lines.detect{|line| syntax_node.at_line?(line)}
36
- end
37
-
38
- def outline_at_line?(syntax_node)
39
- @lines.nil? || @lines.empty? || @lines.detect{|line| syntax_node.outline_at_line?(line)}
40
- end
41
-
42
- def matches_tags?(syntax_node)
43
- !excluded_by_tags?(syntax_node) &&
44
- included_by_tags?(syntax_node)
45
- end
46
-
47
- def included_by_tags?(syntax_node)
48
- @include_tags.empty? || syntax_node.has_tags?(@include_tags)
49
- end
50
-
51
- def excluded_by_tags?(syntax_node)
52
- @exclude_tags.any? && syntax_node.has_tags?(@exclude_tags)
53
- end
54
-
55
- def outline_matches_names?(syntax_node)
56
- @name_regexps.nil? || @name_regexps.empty? || @name_regexps.detect{|name_regexp| syntax_node.outline_matches_name?(name_regexp)}
57
- end
58
-
59
- def matches_names?(syntax_node)
60
- @name_regexps.nil? || @name_regexps.empty? || @name_regexps.detect{|name_regexp| syntax_node.matches_name?(name_regexp)}
15
+ class SyntaxError < StandardError
16
+ def initialize(parser, file, line_offset)
17
+ tf = parser.terminal_failures
18
+ expected = tf.size == 1 ? tf[0].expected_string.inspect : "one of #{tf.map{|f| f.expected_string.inspect}.uniq*', '}"
19
+ line = parser.failure_line + line_offset
20
+ message = "#{file}:#{line}:#{parser.failure_column}: Parse error, expected #{expected}."
21
+ super(message)
61
22
  end
62
23
  end
63
-
64
- module TreetopExt
65
- FILE_COLON_LINE_PATTERN = /^([\w\W]*?):([\d:]+)$/
66
-
67
- # Parses a file and returns a Cucumber::Ast
68
- def parse_file(file, options)
69
- _, path, lines = *FILE_COLON_LINE_PATTERN.match(file)
70
- if path
71
- lines = lines.split(':').map { |line| line.to_i }
72
- else
73
- path = file
74
- end
75
- filter = Filter.new(lines, options)
76
-
77
- loader = lambda { |io| parse_or_fail(io.read, filter, path) }
78
- feature = if path =~ /^http/
79
- require 'open-uri'
80
- open(path, &loader)
81
- else
82
- File.open(path, Cucumber.file_mode('r'), &loader)
83
- end
84
- feature
85
- end
86
-
87
- def parse_or_fail(string, filter=nil, file=nil, line_offset=0)
88
- parse_tree = parse(string)
24
+
25
+ module TreetopExt
26
+ def parse_or_fail(source, file=nil, filter=nil, line_offset=0)
27
+ parse_tree = parse(source)
89
28
  if parse_tree.nil?
90
29
  raise Cucumber::Parser::SyntaxError.new(self, file, line_offset)
91
30
  else
@@ -95,16 +34,6 @@ module Cucumber
95
34
  end
96
35
  end
97
36
  end
98
-
99
- class SyntaxError < StandardError
100
- def initialize(parser, file, line_offset)
101
- tf = parser.terminal_failures
102
- expected = tf.size == 1 ? tf[0].expected_string.inspect : "one of #{tf.map{|f| f.expected_string.inspect}.uniq*', '}"
103
- line = parser.failure_line + line_offset
104
- message = "#{file}:#{line}:#{parser.failure_column}: Parse error, expected #{expected}."
105
- super(message)
106
- end
107
- end
108
37
  end
109
38
  end
110
39
 
@@ -115,7 +44,7 @@ module Treetop
115
44
  input.line_of(interval.first)
116
45
  end
117
46
  end
118
-
47
+
119
48
  class CompiledParser
120
49
  include Cucumber::Parser::TreetopExt
121
50
  end
@@ -3,36 +3,4 @@ require 'cucumber/platform'
3
3
  require 'cucumber/ast'
4
4
  require 'cucumber/parser/treetop_ext'
5
5
  require 'cucumber/parser/table'
6
-
7
- module Cucumber
8
- # Classes in this module parse feature files and translate the parse tree
9
- # (concrete syntax tree) into an abstract syntax tree (AST) using
10
- # <a href="http://martinfowler.com/dslwip/EmbeddedTranslation.html">Embedded translation</a>.
11
- #
12
- # The AST is built by the various <tt>#build</tt> methods in the parse tree.
13
- #
14
- # The AST classes are defined in the Cucumber::Ast module.
15
- module Parser
16
- def self.load_parser(keywords)
17
- Loader.new(keywords)
18
- end
19
-
20
- class Loader
21
- def initialize(keywords)
22
- @keywords = keywords
23
- i18n_tt = File.expand_path(File.dirname(__FILE__) + '/parser/i18n.tt')
24
- template = File.open(i18n_tt, Cucumber.file_mode('r')).read
25
- erb = ERB.new(template)
26
- grammar = erb.result(binding)
27
- Treetop.load_from_string(grammar)
28
- require 'cucumber/parser/feature'
29
- end
30
-
31
- def keywords(key, raw=false)
32
- return @keywords[key] if raw
33
- values = @keywords[key].split('|')
34
- values.map{|value| "'#{value}'"}.join(" / ")
35
- end
36
- end
37
- end
38
- end
6
+ require 'cucumber/parser/feature'