aslakhellesoy-cucumber 0.1.99.23 → 0.1.100.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/History.txt +2 -6
  2. data/Manifest.txt +8 -2
  3. data/examples/i18n/et/features/jagamine.feature +9 -0
  4. data/examples/jbehave/README.textile +4 -1
  5. data/examples/jbehave/features/trading.feature +4 -0
  6. data/examples/jbehave/pom.xml +5 -0
  7. data/examples/jbehave/src/main/java/cukes/jbehave/examples/trader/scenarios/TraderSteps.java +6 -1
  8. data/examples/selenium/features/step_definitons/search_steps.rb +13 -0
  9. data/examples/selenium/features/support/env.rb +19 -0
  10. data/examples/selenium_webrat/Rakefile +6 -0
  11. data/examples/selenium_webrat/features/search.feature +9 -0
  12. data/examples/selenium_webrat/features/step_definitons/search_steps.rb +13 -0
  13. data/examples/selenium_webrat/features/support/env.rb +41 -0
  14. data/examples/self_test/features/background/failing_background.feature +1 -0
  15. data/examples/self_test/features/step_definitions/sample_steps.rb +1 -1
  16. data/examples/self_test/features/support/tag_count_formatter.rb +1 -1
  17. data/features/background.feature +9 -11
  18. data/features/cucumber_cli.feature +24 -1
  19. data/features/cucumber_cli_outlines.feature +10 -19
  20. data/features/report_called_undefined_steps.feature +2 -0
  21. data/gem_tasks/rspec.rake +2 -0
  22. data/lib/cucumber.rb +7 -15
  23. data/lib/cucumber/ast.rb +3 -3
  24. data/lib/cucumber/ast/background.rb +28 -66
  25. data/lib/cucumber/ast/examples.rb +15 -3
  26. data/lib/cucumber/ast/feature.rb +20 -24
  27. data/lib/cucumber/ast/feature_element.rb +46 -0
  28. data/lib/cucumber/ast/features.rb +2 -21
  29. data/lib/cucumber/ast/outline_table.rb +46 -14
  30. data/lib/cucumber/ast/py_string.rb +8 -3
  31. data/lib/cucumber/ast/scenario.rb +34 -73
  32. data/lib/cucumber/ast/scenario_outline.rb +40 -42
  33. data/lib/cucumber/ast/step.rb +53 -89
  34. data/lib/cucumber/ast/step_collection.rb +66 -0
  35. data/lib/cucumber/ast/step_invocation.rb +106 -0
  36. data/lib/cucumber/ast/table.rb +38 -19
  37. data/lib/cucumber/ast/tags.rb +4 -11
  38. data/lib/cucumber/ast/visitor.rb +31 -19
  39. data/lib/cucumber/broadcaster.rb +1 -1
  40. data/lib/cucumber/cli/configuration.rb +24 -16
  41. data/lib/cucumber/cli/language_help_formatter.rb +4 -4
  42. data/lib/cucumber/cli/main.rb +5 -4
  43. data/lib/cucumber/core_ext/proc.rb +2 -2
  44. data/lib/cucumber/formatter/ansicolor.rb +0 -1
  45. data/lib/cucumber/formatter/console.rb +18 -34
  46. data/lib/cucumber/formatter/html.rb +13 -10
  47. data/lib/cucumber/formatter/pretty.rb +48 -36
  48. data/lib/cucumber/formatter/profile.rb +6 -6
  49. data/lib/cucumber/formatter/progress.rb +12 -20
  50. data/lib/cucumber/formatter/rerun.rb +5 -5
  51. data/lib/cucumber/jbehave.rb +21 -26
  52. data/lib/cucumber/parser/feature.rb +26 -29
  53. data/lib/cucumber/parser/feature.tt +17 -12
  54. data/lib/cucumber/parser/treetop_ext.rb +13 -13
  55. data/lib/cucumber/platform.rb +0 -1
  56. data/lib/cucumber/rails/world.rb +2 -2
  57. data/lib/cucumber/rake/task.rb +0 -1
  58. data/lib/cucumber/step_definition.rb +21 -12
  59. data/lib/cucumber/step_match.rb +49 -0
  60. data/lib/cucumber/step_mother.rb +98 -80
  61. data/lib/cucumber/version.rb +2 -2
  62. data/lib/cucumber/world.rb +42 -0
  63. data/rails_generators/cucumber/templates/paths.rb +1 -1
  64. data/rails_generators/cucumber/templates/webrat_steps.rb +17 -17
  65. data/rails_generators/feature/feature_generator.rb +4 -0
  66. data/rails_generators/feature/templates/steps.erb +0 -4
  67. data/spec/cucumber/ast/background_spec.rb +32 -41
  68. data/spec/cucumber/ast/feature_factory.rb +10 -1
  69. data/spec/cucumber/ast/feature_spec.rb +7 -30
  70. data/spec/cucumber/ast/scenario_outline_spec.rb +3 -0
  71. data/spec/cucumber/ast/scenario_spec.rb +8 -25
  72. data/spec/cucumber/ast/step_collection_spec.rb +8 -0
  73. data/spec/cucumber/ast/step_spec.rb +37 -29
  74. data/spec/cucumber/ast/tags_spec.rb +2 -18
  75. data/spec/cucumber/cli/configuration_spec.rb +7 -0
  76. data/spec/cucumber/cli/main_spec.rb +1 -1
  77. data/spec/cucumber/parser/feature_parser_spec.rb +6 -5
  78. data/spec/cucumber/step_definition_spec.rb +5 -5
  79. data/spec/cucumber/step_mother_spec.rb +6 -6
  80. data/spec/cucumber/world/pending_spec.rb +1 -1
  81. metadata +17 -5
  82. data/lib/cucumber/ast/filter.rb +0 -22
  83. data/lib/cucumber/ast/steps.rb +0 -13
@@ -20,18 +20,23 @@ module Cucumber
20
20
  def initialize(start_line, end_line, string, quotes_indent)
21
21
  @start_line, @end_line = start_line, end_line
22
22
  @string, @quotes_indent = string.gsub(/\\"/, '"'), quotes_indent
23
+ @status = :passed
24
+ end
25
+
26
+ def status=(status)
27
+ @status = status
23
28
  end
24
29
 
25
30
  def to_s
26
31
  @string.indent(-@quotes_indent)
27
32
  end
28
33
 
29
- def at_lines?(lines)
34
+ def matches_lines?(lines)
30
35
  lines.detect{|l| l >= @start_line && l <= @end_line}
31
36
  end
32
37
 
33
- def accept(visitor, status)
34
- visitor.visit_py_string(to_s, status)
38
+ def accept(visitor)
39
+ visitor.visit_py_string(to_s, @status)
35
40
  end
36
41
 
37
42
  def arguments_replaced(arguments) #:nodoc:
@@ -1,91 +1,51 @@
1
+ require 'cucumber/ast/feature_element'
2
+
1
3
  module Cucumber
2
4
  module Ast
3
5
  class Scenario
4
- attr_writer :feature, :background
5
-
6
- def initialize(comment, tags, line, keyword, name, steps)
7
- @comment, @tags, @line, @keyword, @name = comment, tags, line, keyword, name
8
- steps.each {|step| step.scenario = self}
9
- @steps = steps
10
- @steps_helper = Steps.new(self)
11
- end
12
-
13
- def status
14
- @steps.map{|step| step.status}
6
+ include FeatureElement
7
+
8
+ def initialize(background, comment, tags, line, keyword, name, steps)
9
+ @background, @comment, @tags, @line, @keyword, @name = background, comment, tags, line, keyword, name
10
+ attach_steps(steps)
11
+
12
+ step_invocations = steps.map{|step| step.step_invocation}
13
+ if @background
14
+ @steps = @background.step_collection(step_invocations)
15
+ else
16
+ @steps = StepCollection.new(step_invocations)
17
+ end
15
18
  end
16
19
 
17
- def tagged_with?(tag_names)
18
- @tags.among?(tag_names) || @feature.tagged_with?(tag_names, false)
20
+ def feature=(feature)
21
+ @feature = feature
22
+ @background.feature = feature if @background
19
23
  end
20
24
 
21
- def matches_scenario_names?(scenario_names)
22
- scenario_names.detect{|name| @name == name}
25
+ def descend?(visitor)
26
+ visitor.matches_lines?(self) &&
27
+ visitor.included_by_tags?(self) &&
28
+ !visitor.excluded_by_tags?(self) &&
29
+ visitor.matches_scenario_names?(self)
23
30
  end
24
31
 
25
32
  def accept(visitor)
26
- visitor.visit_background(@background) if @background
27
33
  visitor.visit_comment(@comment)
28
34
  visitor.visit_tags(@tags)
29
- visitor.visit_scenario_name(@keyword, @name, file_line(@line), source_indent(text_length))
30
- visitor.visit_steps(@steps_helper)
31
-
32
- @feature.scenario_executed(self) if @feature && !@executed
33
- @executed = true
34
- end
35
+ visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(text_length))
35
36
 
36
- def accept_steps(visitor)
37
- prior_world = @background ? @background.world : nil
38
- visitor.world(self, prior_world) do |world|
39
- previous = @background ? @background.status : :passed
40
- @steps.each do |step|
41
- step.previous = previous
42
- step.world = world
43
- visitor.visit_step(step)
44
- previous = step.status
45
- end
37
+ skip = @background && @background.failed?
38
+ skip_invoke! if skip
39
+ visitor.step_mother.before_and_after(self, skip) do
40
+ visitor.visit_steps(@steps)
46
41
  end
47
42
  end
48
43
 
49
- def source_indent(text_length)
50
- max_line_length - text_length
51
- end
52
-
53
- def max_line_length
54
- lengths = (@steps + [self]).map{|e| e.text_length}
55
- lengths.max
56
- end
57
-
58
- def text_length
59
- @keyword.jlength + @name.jlength
60
- end
61
-
62
- def at_lines?(lines)
63
- at_header_or_step_lines?(lines)
64
- end
65
-
66
- def at_header_or_step_lines?(lines)
67
- lines.empty? || lines.index(@line) || @steps.detect {|step| step.at_lines?(lines)} || @tags.at_lines?(lines)
68
- end
69
-
70
- def undefined?
71
- @steps.empty?
72
- end
73
-
74
- def step_executed(step)
75
- @feature.step_executed(step) if @feature
76
- end
77
-
78
- def backtrace_line(name = "#{@keyword} #{@name}", line = @line)
79
- @feature.backtrace_line(name, line) if @feature
80
- end
81
-
82
- def file_line(line = @line)
83
- @feature.file_line(line) if @feature
84
- end
85
-
86
- def previous_step(step)
87
- i = @steps.index(step) || -1
88
- @steps[i-1]
44
+ def skip_invoke!
45
+ @steps.each{|step_invocation| step_invocation.skip_invoke!}
46
+ @feature.next_feature_element(self) do |next_one|
47
+ next_one.skip_invoke!
48
+ end
89
49
  end
90
50
 
91
51
  def to_sexp
@@ -94,10 +54,11 @@ module Cucumber
94
54
  sexp += [comment] if comment
95
55
  tags = @tags.to_sexp
96
56
  sexp += tags if tags.any?
97
- steps = @steps.map{|step| step.to_sexp}
57
+ steps = @steps.to_sexp
98
58
  sexp += steps if steps.any?
99
59
  sexp
100
60
  end
61
+
101
62
  end
102
63
  end
103
64
  end
@@ -1,15 +1,18 @@
1
1
  module Cucumber
2
2
  module Ast
3
- class ScenarioOutline < Scenario
3
+ class ScenarioOutline
4
+ include FeatureElement
5
+
4
6
  # The +example_sections+ argument must be an Array where each element is another array representing
5
7
  # an Examples section. This array has 3 elements:
6
8
  #
7
9
  # * Examples keyword
8
10
  # * Examples section name
9
11
  # * Raw matrix
10
- def initialize(comment, tags, line, keyword, name, steps, example_sections)
11
- super(comment, tags, line, keyword, name, steps)
12
- steps.each {|step| step.status = :outline}
12
+ def initialize(background, comment, tags, line, keyword, name, steps, example_sections)
13
+ @background, @comment, @tags, @line, @keyword, @name = background, comment, tags, line, keyword, name
14
+ attach_steps(steps)
15
+ @steps = StepCollection.new(steps)
13
16
 
14
17
  @examples_array = example_sections.map do |example_section|
15
18
  examples_line = example_section[0]
@@ -22,59 +25,54 @@ module Cucumber
22
25
  end
23
26
  end
24
27
 
25
- def at_lines?(lines)
26
- super || @examples_array.detect { |examples| examples.at_lines?(lines) }
28
+ def feature=(feature)
29
+ @feature = feature
30
+ @background.feature = feature if @background
31
+ end
32
+
33
+ def descend?(visitor)
34
+ @examples_array.detect { |examples| examples.descend?(visitor) }
35
+ end
36
+
37
+ def matches_tags_and_name?(visitor)
38
+ visitor.included_by_tags?(self) &&
39
+ !visitor.excluded_by_tags?(self) &&
40
+ visitor.matches_scenario_names?(self)
27
41
  end
28
42
 
29
43
  def accept(visitor)
30
- visitor.visit_background(@background) if @background
31
44
  visitor.visit_comment(@comment)
32
45
  visitor.visit_tags(@tags)
33
- visitor.visit_scenario_name(@keyword, @name, file_line(@line), source_indent(text_length))
34
- visitor.visit_steps(@steps_helper)
46
+ visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(text_length))
47
+ visitor.visit_steps(@steps)
35
48
 
49
+ skip_invoke! if @background && @background.failed?
36
50
  @examples_array.each do |examples|
37
- visitor.visit_examples(examples)
51
+ visitor.visit_examples(examples) if examples.descend?(visitor)
38
52
  end
39
53
  end
40
54
 
41
- def each_example_row(&proc)
42
- @examples_array.each do |examples|
43
- examples.each_example_row(&proc)
55
+ def skip_invoke!
56
+ @examples_array.each{|examples| examples.skip_invoke!}
57
+ @feature.next_feature_element(self) do |next_one|
58
+ next_one.skip_invoke!
44
59
  end
45
60
  end
46
61
 
47
- def execute_row(cells, visitor, &proc)
48
- exception = nil
49
-
50
- prior_world = @background ? @background.world : nil
51
- visitor.world(self, prior_world) do |world|
52
-
53
- previous_status = @background ? @background.status : :passed
54
- argument_hash = cells.to_hash
55
- cell_index = 0
56
- @steps.each do |step|
57
- executed_step, previous_status, matched_args =
58
- step.execute_with_arguments(argument_hash, world, previous_status, visitor, cells[0].line)
59
- # There might be steps that don't have any arguments
60
- # If there are no matched args, we'll still iterate once
61
- matched_args = [nil] if matched_args.empty?
62
-
63
- matched_args.each do
64
- cell = cells[cell_index]
65
- if cell
66
- proc.call(cell, previous_status)
67
- cell_index += 1
68
- end
69
- end
70
- exception ||= executed_step.exception
71
- end
62
+ def step_invocations(cells)
63
+ step_invocations = @steps.step_invocations_from_cells(cells)
64
+ if @background
65
+ @background.step_collection(step_invocations)
66
+ else
67
+ StepCollection.new(step_invocations)
72
68
  end
73
- @feature.scenario_executed(self) if @feature
74
- exception
75
69
  end
76
70
 
77
- def pending? ; false ; end
71
+ def each_example_row(&proc)
72
+ @examples_array.each do |examples|
73
+ examples.each_example_row(&proc)
74
+ end
75
+ end
78
76
 
79
77
  def to_sexp
80
78
  sexp = [:scenario_outline, @keyword, @name]
@@ -82,7 +80,7 @@ module Cucumber
82
80
  sexp += [comment] if comment
83
81
  tags = @tags.to_sexp
84
82
  sexp += tags if tags.any?
85
- steps = @steps.map{|step| step.to_sexp}
83
+ steps = @steps.to_sexp
86
84
  sexp += steps if steps.any?
87
85
  sexp += @examples_array.map{|e| e.to_sexp}
88
86
  sexp
@@ -4,63 +4,70 @@ require 'cucumber/core_ext/string'
4
4
  module Cucumber
5
5
  module Ast
6
6
  class Step
7
- attr_reader :keyword, :name
8
- attr_writer :world, :previous, :options
9
- attr_accessor :status, :scenario, :exception
7
+ attr_reader :line, :keyword, :name, :multiline_arg
8
+ attr_writer :step_collection, :options
9
+ attr_accessor :feature_element, :exception
10
10
 
11
- def initialize(line, keyword, name, *multiline_args)
12
- @line, @keyword, @name, @multiline_args = line, keyword, name, multiline_args
11
+ def initialize(line, keyword, name, multiline_arg=nil)
12
+ @line, @keyword, @name, @multiline_arg = line, keyword, name, multiline_arg
13
13
  end
14
14
 
15
- def execute_with_arguments(argument_hash, world, previous, visitor, row_line)
16
- delimited_arguments = delimit_argument_names(argument_hash)
15
+ def background?
16
+ false
17
+ end
18
+
19
+ def step_invocation
20
+ StepInvocation.new(self, @name, @multiline_arg, [])
21
+ end
22
+
23
+ def step_invocation_from_cells(cells)
24
+ matched_cells = matched_cells(cells)
25
+
26
+ delimited_arguments = delimit_argument_names(cells.to_hash)
17
27
  name = replace_name_arguments(delimited_arguments)
18
- multiline_args = replace_multiline_args_arguments(delimited_arguments)
28
+ multiline_arg = @multiline_arg.nil? ? nil : @multiline_arg.arguments_replaced(delimited_arguments)
19
29
 
20
- execute_twin(world, previous, visitor, row_line, name, *multiline_args)
30
+ StepInvocation.new(self, name, multiline_arg, matched_cells)
21
31
  end
22
-
23
- def execute_as_new(world, previous, visitor, row_line)
24
- execute_twin(world, previous, visitor, row_line, @name, *@multiline_args)
32
+
33
+ def invoke(step_match, world)
34
+ step_match.invoke(world, @multiline_arg)
25
35
  end
26
36
 
27
37
  def accept(visitor)
28
- execute(visitor)
29
-
30
- if @status == :outline
31
- step_definition = find_first_name_and_step_definition_from_examples(visitor)
32
- else
33
- step_definition = @step_definition
34
- end
35
- visitor.visit_step_name(@keyword, @name, @status, step_definition, source_indent)
36
- @multiline_args.each do |multiline_arg|
37
- visitor.visit_multiline_arg(multiline_arg, @status)
38
- end
39
- @exception
38
+ # The only time a Step is visited is when it is in a ScenarioOutline.
39
+ # Otherwise it's always StepInvocation that gest visited instead.
40
+ visit_step_details(visitor, first_match(visitor), @multiline_arg, :skipped, nil, nil)
41
+ end
42
+
43
+ def visit_step_details(visitor, step_match, multiline_arg, status, exception, background)
44
+ visitor.visit_step_name(@keyword, step_match, status, source_indent, background)
45
+ visitor.visit_multiline_arg(@multiline_arg) if @multiline_arg
46
+ visitor.visit_exception(exception, status) if exception
40
47
  end
41
48
 
42
- def find_first_name_and_step_definition_from_examples(visitor)
43
- # @scenario is always a ScenarioOutline in this case
44
- @scenario.each_example_row do |cells|
49
+ def first_match(visitor)
50
+ # @feature_element is always a ScenarioOutline in this case
51
+ @feature_element.each_example_row do |cells|
45
52
  argument_hash = cells.to_hash
46
53
  delimited_arguments = delimit_argument_names(argument_hash)
47
54
  name = replace_name_arguments(delimited_arguments)
48
- step_definition = visitor.step_definition(name) rescue nil
49
- return step_definition if step_definition
55
+ step_match = visitor.step_mother.step_match(name, @name) rescue nil
56
+ return step_match if step_match
50
57
  end
51
- nil
58
+ NoStepMatch.new(self)
52
59
  end
53
60
 
54
61
  def to_sexp
55
- [:step, @line, @keyword, @name, *@multiline_args.map{|arg| arg.to_sexp}]
62
+ [:step, @line, @keyword, @name, (@multiline_arg.nil? ? nil : @multiline_arg.to_sexp)].compact
56
63
  end
57
64
 
58
- def at_lines?(lines)
59
- lines.empty? || lines.index(@line) || @multiline_args.detect{|a| a.at_lines?(lines)}
65
+ def matches_lines?(lines)
66
+ lines.index(@line) || (@multiline_arg && @multiline_arg.matches_lines?(lines))
60
67
  end
61
68
 
62
69
  def source_indent
63
- @scenario.source_indent(text_length)
70
+ @feature_element.source_indent(text_length)
64
71
  end
65
72
 
66
73
  def text_length
@@ -68,11 +75,11 @@ module Cucumber
68
75
  end
69
76
 
70
77
  def backtrace_line
71
- @backtrace_line ||= @scenario.backtrace_line("#{@keyword} #{@name}", @line) unless @scenario.nil?
78
+ @backtrace_line ||= @feature_element.backtrace_line("#{@keyword} #{@name}", @line) unless @feature_element.nil?
72
79
  end
73
80
 
74
- def file_line
75
- @file_line ||= @scenario.file_line(@line) unless @scenario.nil?
81
+ def file_colon_line
82
+ @file_colon_line ||= @feature_element.file_colon_line(@line) unless @feature_element.nil?
76
83
  end
77
84
 
78
85
  def actual_keyword
@@ -85,56 +92,25 @@ module Cucumber
85
92
 
86
93
  protected
87
94
 
95
+ # TODO: Remove when we use StepCollection everywhere
88
96
  def previous_step
89
- @scenario.previous_step(self)
97
+ @feature_element.previous_step(self)
90
98
  end
91
99
 
92
100
  private
93
101
 
94
- def execute(visitor)
95
- matched_args = []
96
- if @status.nil?
97
- begin
98
- @step_definition = visitor.step_definition(@name)
99
- matched_args = @step_definition.matched_args(@name)
100
- if @previous == :passed && !visitor.options[:dry_run]
101
- @world.__cucumber_current_step = self
102
- @step_definition.execute(@name, @world, *(matched_args + @multiline_args))
103
- @status = :passed
104
- else
105
- @status = :skipped
106
- end
107
- rescue Undefined => exception
108
- if visitor.options[:strict]
109
- exception.set_backtrace([])
110
- failed(exception)
111
- else
112
- @status = :undefined
113
- end
114
- rescue Pending => exception
115
- visitor.options[:strict] ? failed(exception) : @status = :pending
116
- rescue Exception => exception
117
- failed(exception)
118
- end
119
- @scenario.step_executed(self) if @scenario
102
+ def matched_cells(cells)
103
+ cells.select do |cell|
104
+ @name.index(delimited(cell.header_cell.value))
120
105
  end
121
- [self, @status, matched_args]
122
106
  end
123
107
 
124
- def execute_twin(world, previous, visitor, line, name, *multiline_args)
125
- # We'll create a new step and execute that
126
- step = Step.new(line, @keyword, name, *multiline_args)
127
- step.scenario = @scenario
128
- step.world = world
129
- step.previous = previous
130
- step.__send__(:execute, visitor)
108
+ def delimit_argument_names(argument_hash)
109
+ argument_hash.inject({}) { |h,(name,value)| h[delimited(name)] = value; h }
131
110
  end
132
111
 
133
- ARGUMENT_START = '<'
134
- ARGUMENT_END = '>'
135
-
136
- def delimit_argument_names(argument_hash)
137
- argument_hash.inject({}) { |h,(k,v)| h["#{ARGUMENT_START}#{k}#{ARGUMENT_END}"] = v; h }
112
+ def delimited(s)
113
+ "<#{s}>"
138
114
  end
139
115
 
140
116
  def replace_name_arguments(argument_hash)
@@ -144,18 +120,6 @@ module Cucumber
144
120
  end
145
121
  name_with_arguments_replaced
146
122
  end
147
-
148
- def replace_multiline_args_arguments(arguments)
149
- @multiline_args.map do |arg|
150
- arg.arguments_replaced(arguments)
151
- end
152
- end
153
-
154
- def failed(exception)
155
- @status = :failed
156
- @exception = exception
157
- @exception.backtrace << backtrace_line unless backtrace_line.nil?
158
- end
159
123
  end
160
124
  end
161
125
  end