cucumber 0.4.3 → 0.4.4

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 (69) hide show
  1. data/History.txt +27 -0
  2. data/VERSION.yml +3 -2
  3. data/cucumber.gemspec +11 -5
  4. data/examples/watir/features/support/screenshots.rb +1 -2
  5. data/features/call_many_steps.feature +124 -0
  6. data/features/default_snippets.feature +2 -2
  7. data/features/expand.feature +13 -2
  8. data/features/language_help.feature +1 -1
  9. data/features/report_called_undefined_steps.feature +1 -1
  10. data/features/snippet.feature +2 -2
  11. data/features/support/env.rb +3 -5
  12. data/features/tag_logic.feature +32 -0
  13. data/features/wire_protocol.feature +7 -7
  14. data/lib/cucumber.rb +6 -0
  15. data/lib/cucumber/ast/background.rb +1 -1
  16. data/lib/cucumber/ast/comment.rb +1 -1
  17. data/lib/cucumber/ast/examples.rb +1 -1
  18. data/lib/cucumber/ast/feature.rb +1 -1
  19. data/lib/cucumber/ast/feature_element.rb +2 -3
  20. data/lib/cucumber/ast/features.rb +1 -1
  21. data/lib/cucumber/ast/outline_table.rb +17 -4
  22. data/lib/cucumber/ast/py_string.rb +3 -1
  23. data/lib/cucumber/ast/scenario.rb +3 -1
  24. data/lib/cucumber/ast/scenario_outline.rb +2 -2
  25. data/lib/cucumber/ast/step.rb +1 -1
  26. data/lib/cucumber/ast/step_collection.rb +1 -1
  27. data/lib/cucumber/ast/step_invocation.rb +2 -1
  28. data/lib/cucumber/ast/table.rb +3 -3
  29. data/lib/cucumber/ast/tags.rb +18 -11
  30. data/lib/cucumber/ast/tree_walker.rb +16 -0
  31. data/lib/cucumber/cli/main.rb +3 -2
  32. data/lib/cucumber/cli/options.rb +4 -6
  33. data/lib/cucumber/cli/profile_loader.rb +4 -0
  34. data/lib/cucumber/filter.rb +2 -2
  35. data/lib/cucumber/formatter/ansicolor.rb +8 -0
  36. data/lib/cucumber/formatter/pretty.rb +3 -4
  37. data/lib/cucumber/language_support/language_methods.rb +4 -3
  38. data/lib/cucumber/languages.yml +1 -1
  39. data/lib/cucumber/parser.rb +2 -0
  40. data/lib/cucumber/parser/common.rb +170 -0
  41. data/lib/cucumber/parser/common.tt +21 -0
  42. data/lib/cucumber/parser/feature.rb +7 -291
  43. data/lib/cucumber/parser/feature.tt +7 -43
  44. data/lib/cucumber/parser/i18n.tt +2 -0
  45. data/lib/cucumber/parser/natural_language.rb +9 -0
  46. data/lib/cucumber/parser/py_string.rb +276 -0
  47. data/lib/cucumber/parser/py_string.tt +45 -0
  48. data/lib/cucumber/parser/table.rb +5 -120
  49. data/lib/cucumber/parser/table.tt +2 -13
  50. data/lib/cucumber/platform.rb +3 -2
  51. data/lib/cucumber/rails/active_record.rb +2 -21
  52. data/lib/cucumber/rails/world.rb +2 -1
  53. data/lib/cucumber/rb_support/rb_hook.rb +2 -1
  54. data/lib/cucumber/rb_support/rb_language.rb +8 -6
  55. data/lib/cucumber/rb_support/rb_step_definition.rb +4 -0
  56. data/lib/cucumber/rb_support/rb_world.rb +16 -37
  57. data/lib/cucumber/step_mother.rb +86 -2
  58. data/lib/cucumber/wire_support/wire_language.rb +2 -2
  59. data/lib/cucumber/wire_support/wire_protocol.rb +1 -1
  60. data/rails_generators/cucumber/cucumber_generator.rb +3 -1
  61. data/rails_generators/cucumber/templates/cucumber.rake +4 -2
  62. data/rails_generators/cucumber/templates/webrat_steps.rb +28 -28
  63. data/spec/cucumber/ast/background_spec.rb +2 -1
  64. data/spec/cucumber/ast/scenario_spec.rb +3 -1
  65. data/spec/cucumber/formatter/console_spec.rb +1 -1
  66. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +14 -5
  67. data/spec/cucumber/step_mother_spec.rb +1 -1
  68. data/spec/cucumber/world/pending_spec.rb +1 -1
  69. metadata +8 -3
@@ -23,7 +23,7 @@ module Cucumber
23
23
  end
24
24
 
25
25
  def accept(visitor)
26
- return if $cucumber_interrupted
26
+ return if Cucumber.wants_to_quit
27
27
  visitor.visit_comment(@comment) unless @comment.empty?
28
28
  visitor.visit_background_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
29
29
  with_visitor(hook_context, visitor) do
@@ -17,7 +17,7 @@ module Cucumber
17
17
  end
18
18
 
19
19
  def accept(visitor)
20
- return if $cucumber_interrupted
20
+ return if Cucumber.wants_to_quit
21
21
  @value.strip.split("\n").each do |line|
22
22
  visitor.visit_comment_line(line.strip)
23
23
  end
@@ -6,7 +6,7 @@ module Cucumber
6
6
  end
7
7
 
8
8
  def accept(visitor)
9
- return if $cucumber_interrupted
9
+ return if Cucumber.wants_to_quit
10
10
  visitor.visit_comment(@comment) unless @comment.empty?
11
11
  visitor.visit_examples_name(@keyword, @name)
12
12
  visitor.visit_outline_table(@outline_table)
@@ -16,7 +16,7 @@ module Cucumber
16
16
  end
17
17
 
18
18
  def accept(visitor)
19
- return if $cucumber_interrupted
19
+ return if Cucumber.wants_to_quit
20
20
  visitor.visit_comment(@comment) unless @comment.empty?
21
21
  visitor.visit_tags(@tags)
22
22
  visitor.visit_feature_name(@name)
@@ -49,8 +49,7 @@ module Cucumber
49
49
  end
50
50
 
51
51
  def accept_hook?(hook)
52
- parsed_hook_tag_names = hook.tag_names.map {|tag_string| Cli::Options.parse_tag_arguments(tag_string)}
53
- Tags.matches?(source_tag_names, parsed_hook_tag_names)
52
+ Tags.matches?(source_tag_names, hook.tag_name_lists)
54
53
  end
55
54
 
56
55
  def source_tag_names
@@ -62,7 +61,7 @@ module Cucumber
62
61
  end
63
62
 
64
63
  def language
65
- @feature.language
64
+ @feature.language if @feature
66
65
  end
67
66
  end
68
67
  end
@@ -23,7 +23,7 @@ module Cucumber
23
23
  end
24
24
 
25
25
  def accept(visitor)
26
- return if $cucumber_interrupted
26
+ return if Cucumber.wants_to_quit
27
27
  start = Time.now
28
28
  self.each do |feature|
29
29
  visitor.visit_feature(feature)
@@ -9,7 +9,7 @@ module Cucumber
9
9
  end
10
10
 
11
11
  def accept(visitor)
12
- return if $cucumber_interrupted
12
+ return if Cucumber.wants_to_quit
13
13
  cells_rows.each_with_index do |row, n|
14
14
  if(visitor.options[:expand])
15
15
  row.accept(visitor)
@@ -44,7 +44,11 @@ module Cucumber
44
44
  @scenario_outline.visit_scenario_name(visitor, row)
45
45
  end
46
46
 
47
- class ExampleRow < Cells #:nodoc:
47
+ def language
48
+ @scenario_outline.language
49
+ end
50
+
51
+ class ExampleRow < Cells #:nodoc:
48
52
  class InvalidForHeaderRowError < NoMethodError
49
53
  def initialize(*args)
50
54
  super 'This is a header row and cannot pass or fail'
@@ -53,6 +57,11 @@ module Cucumber
53
57
 
54
58
  attr_reader :scenario_outline # https://rspec.lighthouseapp.com/projects/16211/tickets/342
55
59
 
60
+ def initialize(table, cells)
61
+ super
62
+ @scenario_exception = nil
63
+ end
64
+
56
65
  def create_step_invocations!(scenario_outline)
57
66
  @scenario_outline = scenario_outline
58
67
  @step_invocations = scenario_outline.step_invocations(self)
@@ -65,7 +74,7 @@ module Cucumber
65
74
  end
66
75
 
67
76
  def accept(visitor)
68
- return if $cucumber_interrupted
77
+ return if Cucumber.wants_to_quit
69
78
  visitor.options[:expand] ? accept_expand(visitor) : accept_plain(visitor)
70
79
  end
71
80
 
@@ -141,7 +150,11 @@ module Cucumber
141
150
  def name
142
151
  "| #{@cells.collect{|c| c.value }.join(' | ')} |"
143
152
  end
144
-
153
+
154
+ def language
155
+ @table.language
156
+ end
157
+
145
158
  private
146
159
 
147
160
  def header?
@@ -17,6 +17,8 @@ module Cucumber
17
17
  # Note how the indentation from the source is stripped away.
18
18
  #
19
19
  class PyString #:nodoc:
20
+ attr_accessor :file
21
+
20
22
  def self.default_arg_name
21
23
  "string"
22
24
  end
@@ -31,7 +33,7 @@ module Cucumber
31
33
  end
32
34
 
33
35
  def accept(visitor)
34
- return if $cucumber_interrupted
36
+ return if Cucumber.wants_to_quit
35
37
  visitor.visit_py_string(to_s)
36
38
  end
37
39
 
@@ -29,10 +29,12 @@ module Cucumber
29
29
  step_invocations = steps.map{|step| step.step_invocation}
30
30
  @steps = @background.step_collection(step_invocations)
31
31
  @background.feature_elements << self
32
+
33
+ @exception = @executed = nil
32
34
  end
33
35
 
34
36
  def accept(visitor)
35
- return if $cucumber_interrupted
37
+ return if Cucumber.wants_to_quit
36
38
 
37
39
  with_visitor(visitor) do
38
40
  visitor.visit_comment(@comment) unless @comment.empty?
@@ -5,7 +5,7 @@ module Cucumber
5
5
 
6
6
  module ExamplesArray #:nodoc:
7
7
  def accept(visitor)
8
- return if $cucumber_interrupted
8
+ return if Cucumber.wants_to_quit
9
9
  each do |examples|
10
10
  visitor.visit_examples(examples)
11
11
  end
@@ -39,7 +39,7 @@ module Cucumber
39
39
  end
40
40
 
41
41
  def accept(visitor)
42
- return if $cucumber_interrupted
42
+ return if Cucumber.wants_to_quit
43
43
  visitor.visit_comment(@comment) unless @comment.empty?
44
44
  visitor.visit_tags(@tags)
45
45
  visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
@@ -38,7 +38,7 @@ module Cucumber
38
38
  end
39
39
 
40
40
  def accept(visitor)
41
- return if $cucumber_interrupted
41
+ return if Cucumber.wants_to_quit
42
42
  # The only time a Step is visited is when it is in a ScenarioOutline.
43
43
  # Otherwise it's always StepInvocation that gets visited instead.
44
44
  visit_step_result(visitor, first_match(visitor), @multiline_arg, :skipped, nil, nil)
@@ -10,7 +10,7 @@ module Cucumber
10
10
  end
11
11
 
12
12
  def accept(visitor, &proc)
13
- return if $cucumber_interrupted
13
+ return if Cucumber.wants_to_quit
14
14
  @steps.each do |step|
15
15
  visitor.visit_step(step) if proc.nil? || proc.call(step)
16
16
  end
@@ -22,6 +22,7 @@ module Cucumber
22
22
  def initialize(step, name, multiline_arg, matched_cells)
23
23
  @step, @name, @multiline_arg, @matched_cells = step, name, multiline_arg, matched_cells
24
24
  status!(:skipped)
25
+ @skip_invoke = @exception = @step_match = @different_table = @reported_exception = @background = nil
25
26
  end
26
27
 
27
28
  def background?
@@ -33,7 +34,7 @@ module Cucumber
33
34
  end
34
35
 
35
36
  def accept(visitor)
36
- return if $cucumber_interrupted
37
+ return if Cucumber.wants_to_quit
37
38
  invoke(visitor.step_mother, visitor.options)
38
39
  visit_step_result(visitor)
39
40
  end
@@ -148,7 +148,7 @@ module Cucumber
148
148
  end
149
149
 
150
150
  def accept(visitor) #:nodoc:
151
- return if $cucumber_interrupted
151
+ return if Cucumber.wants_to_quit
152
152
  cells_rows.each do |row|
153
153
  visitor.visit_table_row(row)
154
154
  end
@@ -567,7 +567,7 @@ module Cucumber
567
567
  end
568
568
 
569
569
  def accept(visitor)
570
- return if $cucumber_interrupted
570
+ return if Cucumber.wants_to_quit
571
571
  each do |cell|
572
572
  visitor.visit_table_cell(cell)
573
573
  end
@@ -623,7 +623,7 @@ module Cucumber
623
623
  end
624
624
 
625
625
  def accept(visitor)
626
- return if $cucumber_interrupted
626
+ return if Cucumber.wants_to_quit
627
627
  visitor.visit_table_cell_value(value, status)
628
628
  end
629
629
 
@@ -10,9 +10,12 @@ module Cucumber
10
10
  # This gets stored internally as <tt>["invoice", "release_2"]</tt>
11
11
  #
12
12
  class Tags #:nodoc:
13
-
14
13
  class And #:nodoc:
15
14
  def initialize(tag_names)
15
+ if String === tag_names # They still come in as single strings on cuke4duke some times. Not sure why...
16
+ raise "Commas in tags??? #{tag_names.inspect}" if tag_names =~ /,/ # just in case...
17
+ tag_names = [tag_names]
18
+ end
16
19
  @negative_tags, @positive_tags = tag_names.partition{|tag_name| Tags.exclude_tag?(tag_name)}
17
20
  @negative_tags = Tags.strip_negative_char(@negative_tags)
18
21
  end
@@ -47,9 +50,9 @@ module Cucumber
47
50
  class << self
48
51
  EXCLUDE_PATTERN = /^~/
49
52
 
50
- def matches?(source_tag_names, tag_names)
51
- validate_tags(tag_names)
52
- tag_names.empty? ? true : check_if_tags_match(source_tag_names, tag_names)
53
+ def matches?(source_tag_names, tag_name_lists)
54
+ validate_tags(tag_name_lists)
55
+ tag_name_lists.empty? ? true : check_if_tags_match(source_tag_names, tag_name_lists)
53
56
  end
54
57
 
55
58
  def exclude_tag?(tag_name)
@@ -60,22 +63,26 @@ module Cucumber
60
63
  tag_names.map{|name| name[1..-1]}
61
64
  end
62
65
 
66
+ def parse_tags(tags_string)
67
+ tags_string.split(',')
68
+ end
69
+
63
70
  private
64
71
 
65
- def validate_tags(tag_name_list)
66
- all_tag_names = tag_name_list.flatten
72
+ def validate_tags(tag_name_lists)
73
+ all_tag_names = tag_name_lists.flatten
67
74
  exclude_tag_names, include_tag_names = all_tag_names.partition{|tag_name| exclude_tag?(tag_name)}
68
75
  exclude_tag_names = strip_negative_char(exclude_tag_names)
69
76
  check_at_sign_prefix(exclude_tag_names + include_tag_names)
70
77
  end
71
78
 
72
- def check_if_tags_match(source_tag_names, tag_names)
73
- tag_exp = Or.new(tag_names.map{|tag_name_list| And.new(tag_name_list) })
79
+ def check_if_tags_match(source_tag_names, tag_name_lists)
80
+ tag_exp = Or.new(tag_name_lists.map{|tag_name_list| And.new(tag_name_list) })
74
81
  tag_exp.matches?(source_tag_names)
75
82
  end
76
83
 
77
84
  def check_at_sign_prefix(tag_names)
78
- tag_names.each{|tag_name| raise "Tag names must start with an @ sign. The following tag name didn't: #{tag_name}" unless tag_name[0..0] == '@'}
85
+ tag_names.each{|tag_name| raise "Tag names must start with an @ sign. The following tag name didn't: #{tag_name.inspect}" unless tag_name[0..0] == '@'}
79
86
  end
80
87
 
81
88
  end
@@ -87,14 +94,14 @@ module Cucumber
87
94
  end
88
95
 
89
96
  def accept(visitor)
90
- return if $cucumber_interrupted
97
+ return if Cucumber.wants_to_quit
91
98
  @tag_names.each do |tag_name|
92
99
  visitor.visit_tag_name(tag_name)
93
100
  end
94
101
  end
95
102
 
96
103
  def accept_hook?(hook)
97
- self.class.matches?(@tag_names, hook.tag_names)
104
+ self.class.matches?(@tag_names, hook.tag_name_lists)
98
105
  end
99
106
 
100
107
  def count(tag)
@@ -75,6 +75,22 @@ module Cucumber
75
75
  end
76
76
 
77
77
  def visit_examples_name(keyword, name)
78
+ unless keyword =~ /:$/
79
+ message = <<EOS
80
+
81
+
82
+ (::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)
83
+
84
+ DEPRECATION WARNING
85
+
86
+ Future versions of Cucumber will not recognize #{keyword}
87
+ unless it is followed by a colon. Make this change in
88
+ your features now to prevent this warning from appearing.
89
+
90
+ (::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)
91
+ EOS
92
+ announce(message)
93
+ end
78
94
  broadcast(keyword, name)
79
95
  end
80
96
 
@@ -29,6 +29,7 @@ module Cucumber
29
29
  @args = args
30
30
  @out_stream = out_stream == STDOUT ? Formatter::ColorIO.new : out_stream
31
31
  @error_stream = error_stream
32
+ @configuration = nil
32
33
  end
33
34
 
34
35
  def execute!(step_mother)
@@ -110,8 +111,8 @@ module Cucumber
110
111
 
111
112
  def trap_interrupt
112
113
  trap('INT') do
113
- exit!(1) if $cucumber_interrupted
114
- $cucumber_interrupted = true
114
+ exit!(1) if Cucumber.wants_to_quit
115
+ Cucumber.wants_to_quit = true
115
116
  STDERR.puts "\nExiting... Interrupt again to exit immediately."
116
117
  end
117
118
  end
@@ -48,10 +48,6 @@ module Cucumber
48
48
  new(out_stream, error_stream, options).parse!(args)
49
49
  end
50
50
 
51
- def self.parse_tag_arguments(tags_string)
52
- tags_string.split(',')
53
- end
54
-
55
51
  def initialize(out_stream = STDOUT, error_stream = STDERR, options = {})
56
52
  @out_stream = out_stream
57
53
  @error_stream = error_stream
@@ -60,7 +56,9 @@ module Cucumber
60
56
  @skip_profile_information = options[:skip_profile_information]
61
57
  @profiles = []
62
58
  @overridden_paths = []
63
- @options = default_options
59
+ @options = default_options
60
+
61
+ @quiet = @disable_profile_loading = nil
64
62
  end
65
63
 
66
64
  def [](key)
@@ -292,7 +290,7 @@ module Cucumber
292
290
  end
293
291
 
294
292
  def parse_tags(tag_string)
295
- tag_names = Options.parse_tag_arguments(tag_string)
293
+ tag_names = Ast::Tags.parse_tags(tag_string)
296
294
  parse_tag_limits(tag_names)
297
295
  end
298
296
 
@@ -3,6 +3,10 @@ module Cucumber
3
3
 
4
4
  class ProfileLoader
5
5
 
6
+ def initialize
7
+ @cucumber_yml = nil
8
+ end
9
+
6
10
  def args_from(profile)
7
11
  unless cucumber_yml.has_key?(profile)
8
12
  raise(ProfileNotFound, <<-END_OF_ERROR)
@@ -3,7 +3,7 @@ module Cucumber
3
3
  class Filter #:nodoc:
4
4
  def initialize(lines, options)
5
5
  @lines = lines
6
- @tag_names = options[:tag_names] ? options[:tag_names].map{|tags_with_limit| tags_with_limit.keys } : []
6
+ @tag_name_lists = options[:tag_names] ? options[:tag_names].map{|tags_with_limit| tags_with_limit.keys } : []
7
7
  @name_regexps = options[:name_regexps] || []
8
8
  end
9
9
 
@@ -27,7 +27,7 @@ module Cucumber
27
27
  end
28
28
 
29
29
  def matches_tags?(syntax_node)
30
- syntax_node.matches_tags?(@tag_names)
30
+ syntax_node.matches_tags?(@tag_name_lists)
31
31
  end
32
32
 
33
33
  def outline_matches_names?(syntax_node)
@@ -1,6 +1,14 @@
1
1
  require 'term/ansicolor'
2
2
  require 'cucumber/platform'
3
3
 
4
+ if Cucumber::IRONRUBY
5
+ begin
6
+ require 'iron-term-ansicolor'
7
+ rescue LoadError
8
+ STDERR.puts %{*** WARNING: You must "igem install iron-term-ansicolor" to get coloured ouput in on IronRuby}
9
+ end
10
+ end
11
+
4
12
  if Cucumber::WINDOWS_MRI
5
13
  begin
6
14
  require 'Win32/Console/ANSI'
@@ -178,13 +178,13 @@ module Cucumber
178
178
  end
179
179
 
180
180
  def before_table_row(table_row)
181
- return unless @table
181
+ return if !@table || @hide_this_step
182
182
  @col_index = 0
183
183
  @io.print ' |'.indent(@indent-2)
184
184
  end
185
185
 
186
186
  def after_table_row(table_row)
187
- return unless @table
187
+ return if !@table || @hide_this_step
188
188
  print_table_row_announcements
189
189
  @io.puts
190
190
  if table_row.exception && !@exceptions.include?(table_row.exception)
@@ -198,7 +198,7 @@ module Cucumber
198
198
  end
199
199
 
200
200
  def table_cell_value(value, status)
201
- return unless @table
201
+ return if !@table || @hide_this_step
202
202
  status ||= @status || :passed
203
203
  width = @table.col_width(@col_index)
204
204
  cell_text = value.to_s || ''
@@ -234,7 +234,6 @@ module Cucumber
234
234
  print_passing_wip(@options)
235
235
  print_tag_limit_warnings(@options)
236
236
  end
237
-
238
237
  end
239
238
  end
240
239
  end