cucumber 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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