cucumber 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/History.txt +56 -1
  2. data/Manifest.txt +70 -49
  3. data/config/hoe.rb +1 -1
  4. data/cucumber.yml +1 -1
  5. data/examples/i18n/bg/Rakefile +6 -0
  6. data/examples/i18n/bg/features/addition.feature +11 -0
  7. data/examples/i18n/bg/features/consecutive_calculations.feature +18 -0
  8. data/examples/i18n/bg/features/division.feature +16 -0
  9. data/examples/i18n/bg/features/step_definitons/calculator_steps.rb +24 -0
  10. data/examples/i18n/bg/features/support/env.rb +6 -0
  11. data/examples/i18n/bg/features/support/world.rb +8 -0
  12. data/examples/i18n/bg/lib/calculator.rb +24 -0
  13. data/examples/i18n/ru/features/support/world.rb +4 -3
  14. data/examples/i18n/sk/Rakefile +6 -0
  15. data/examples/i18n/sk/features/addition.feature +16 -0
  16. data/examples/i18n/sk/features/division.feature +9 -0
  17. data/examples/i18n/sk/features/step_definitons/calculator_steps.rb +24 -0
  18. data/examples/i18n/sk/lib/calculator.rb +14 -0
  19. data/examples/self_test/features/background/background_with_name.feature +7 -0
  20. data/examples/self_test/features/background/passing_background.feature +2 -2
  21. data/examples/self_test/features/step_definitions/sample_steps.rb +18 -2
  22. data/examples/self_test/features/undefined_multiline_args.feature +12 -0
  23. data/examples/sinatra/features/support/env.rb +2 -6
  24. data/examples/test_unit/features/step_definitions/test_unit_steps.rb +1 -4
  25. data/examples/tickets/Rakefile +1 -1
  26. data/examples/tickets/features/229/tagged_hooks.feature +8 -0
  27. data/examples/tickets/features/229/tagged_hooks.rb +14 -0
  28. data/examples/tickets/features/270/back.feature +14 -0
  29. data/examples/tickets/features/270/back.steps.rb +14 -0
  30. data/examples/tickets/features/279/py_string_indent.feature +25 -0
  31. data/examples/tickets/features/279/py_string_indent.steps.rb +12 -0
  32. data/examples/tickets/features/279/wrong.feature_ +11 -0
  33. data/examples/tickets/features/step_definitons/tickets_steps.rb +0 -7
  34. data/features/background.feature +21 -3
  35. data/features/cucumber_cli.feature +18 -5
  36. data/features/cucumber_cli_outlines.feature +4 -1
  37. data/features/rake_task.feature +132 -0
  38. data/features/snippet.feature +23 -0
  39. data/features/step_definitions/cucumber_steps.rb +46 -15
  40. data/features/support/env.rb +61 -4
  41. data/features/usage.feature +5 -0
  42. data/gem_tasks/deployment.rake +1 -1
  43. data/lib/cucumber/ast/background.rb +14 -4
  44. data/lib/cucumber/ast/examples.rb +0 -12
  45. data/lib/cucumber/ast/feature.rb +2 -12
  46. data/lib/cucumber/ast/feature_element.rb +4 -8
  47. data/lib/cucumber/ast/features.rb +1 -1
  48. data/lib/cucumber/ast/outline_table.rb +20 -20
  49. data/lib/cucumber/ast/py_string.rb +6 -11
  50. data/lib/cucumber/ast/scenario.rb +1 -8
  51. data/lib/cucumber/ast/scenario_outline.rb +1 -11
  52. data/lib/cucumber/ast/step.rb +3 -9
  53. data/lib/cucumber/ast/step_collection.rb +0 -4
  54. data/lib/cucumber/ast/step_invocation.rb +5 -6
  55. data/lib/cucumber/ast/table.rb +5 -22
  56. data/lib/cucumber/ast/tags.rb +9 -9
  57. data/lib/cucumber/ast/visitor.rb +12 -25
  58. data/lib/cucumber/cli/configuration.rb +4 -4
  59. data/lib/cucumber/cli/main.rb +10 -3
  60. data/lib/cucumber/core_ext/instance_exec.rb +17 -4
  61. data/lib/cucumber/formatter/ansicolor.rb +1 -1
  62. data/lib/cucumber/formatter/console.rb +2 -1
  63. data/lib/cucumber/formatter/html.rb +21 -7
  64. data/lib/cucumber/formatter/pretty.rb +27 -20
  65. data/lib/cucumber/formatter/usage.rb +16 -0
  66. data/lib/cucumber/languages.yml +23 -5
  67. data/lib/cucumber/parser/feature.rb +231 -114
  68. data/lib/cucumber/parser/feature.tt +120 -25
  69. data/lib/cucumber/parser/table.rb +37 -25
  70. data/lib/cucumber/parser/table.tt +15 -3
  71. data/lib/cucumber/parser/treetop_ext.rb +48 -9
  72. data/lib/cucumber/rake/task.rb +29 -6
  73. data/lib/cucumber/step_definition.rb +4 -2
  74. data/lib/cucumber/step_mother.rb +143 -26
  75. data/lib/cucumber/version.rb +2 -2
  76. data/rails_generators/cucumber/templates/paths.rb +13 -4
  77. data/rails_generators/cucumber/templates/webrat_steps.rb +16 -0
  78. data/{specs → spec}/cucumber/ast/background_spec.rb +1 -0
  79. data/{specs → spec}/cucumber/ast/feature_factory.rb +1 -1
  80. data/{specs → spec}/cucumber/ast/feature_spec.rb +2 -2
  81. data/{specs → spec}/cucumber/ast/py_string_spec.rb +0 -0
  82. data/{specs → spec}/cucumber/ast/scenario_outline_spec.rb +0 -0
  83. data/{specs → spec}/cucumber/ast/scenario_spec.rb +0 -27
  84. data/{specs → spec}/cucumber/ast/step_collection_spec.rb +0 -0
  85. data/{specs → spec}/cucumber/ast/step_spec.rb +0 -0
  86. data/{specs → spec}/cucumber/ast/table_spec.rb +2 -2
  87. data/{specs → spec}/cucumber/broadcaster_spec.rb +0 -0
  88. data/{specs → spec}/cucumber/cli/configuration_spec.rb +0 -0
  89. data/{specs → spec}/cucumber/cli/main_spec.rb +5 -1
  90. data/spec/cucumber/core_ext/proc_spec.rb +54 -0
  91. data/{specs → spec}/cucumber/core_ext/string_spec.rb +0 -0
  92. data/{specs → spec}/cucumber/formatter/ansicolor_spec.rb +0 -0
  93. data/{specs → spec}/cucumber/formatter/color_io_spec.rb +0 -0
  94. data/{specs → spec}/cucumber/formatter/html/cucumber.css +0 -0
  95. data/{specs → spec}/cucumber/formatter/html/cucumber.js +0 -0
  96. data/{specs → spec}/cucumber/formatter/html/index.html +0 -0
  97. data/{specs → spec}/cucumber/formatter/html/jquery-1.3.min.js +0 -0
  98. data/{specs → spec}/cucumber/formatter/html/jquery.uitableedit.js +0 -0
  99. data/{specs → spec}/cucumber/formatters/profile_formatter_spec.rb +0 -0
  100. data/{specs → spec}/cucumber/parser/feature_parser_spec.rb +43 -41
  101. data/{specs → spec}/cucumber/parser/table_parser_spec.rb +0 -0
  102. data/{specs → spec}/cucumber/rails/stubs/mini_rails.rb +0 -0
  103. data/{specs → spec}/cucumber/rails/stubs/test_help.rb +0 -0
  104. data/{specs → spec}/cucumber/rails/world_spec.rb +0 -0
  105. data/{specs → spec}/cucumber/sell_cucumbers.feature +0 -0
  106. data/{specs → spec}/cucumber/step_definition_spec.rb +0 -0
  107. data/{specs → spec}/cucumber/step_mother_spec.rb +63 -4
  108. data/{specs → spec}/cucumber/treetop_parser/empty_feature.feature +0 -0
  109. data/{specs → spec}/cucumber/treetop_parser/empty_scenario.feature +0 -0
  110. data/{specs → spec}/cucumber/treetop_parser/empty_scenario_outline.feature +0 -0
  111. data/{specs → spec}/cucumber/treetop_parser/fit_scenario.feature +0 -0
  112. data/{specs → spec}/cucumber/treetop_parser/given_scenario.feature +0 -0
  113. data/{specs → spec}/cucumber/treetop_parser/invalid_scenario_outlines.feature +0 -0
  114. data/{specs → spec}/cucumber/treetop_parser/multiline_steps.feature +0 -0
  115. data/{specs → spec}/cucumber/treetop_parser/multiple_tables.feature +0 -0
  116. data/{specs → spec}/cucumber/treetop_parser/scenario_outline.feature +0 -0
  117. data/{specs → spec}/cucumber/treetop_parser/spaces.feature +0 -0
  118. data/{specs → spec}/cucumber/treetop_parser/test_dos.feature +0 -0
  119. data/{specs → spec}/cucumber/treetop_parser/with_comments.feature +0 -0
  120. data/{specs → spec}/cucumber/treetop_parser/with_tags.feature +0 -0
  121. data/{specs → spec}/cucumber/world/pending_spec.rb +0 -0
  122. data/{specs → spec}/spec.opts +0 -0
  123. data/{specs → spec}/spec_helper.rb +2 -11
  124. metadata +72 -51
  125. data/examples/tickets/cucumber.yml +0 -3
  126. data/lib/cucumber/parser/basic.rb +0 -0
  127. data/specs/cucumber/ast/tags_spec.rb +0 -19
  128. data/specs/cucumber/core_ext/proc_spec.rb +0 -37
@@ -8,7 +8,7 @@ module Cucumber
8
8
  def initialize(background, comment, tags, line, keyword, name, steps)
9
9
  @background, @comment, @tags, @line, @keyword, @name = background, comment, tags, line, keyword, name
10
10
  attach_steps(steps)
11
-
11
+
12
12
  step_invocations = steps.map{|step| step.step_invocation}
13
13
  if @background
14
14
  @steps = @background.step_collection(step_invocations)
@@ -22,13 +22,6 @@ module Cucumber
22
22
  @background.feature = feature if @background
23
23
  end
24
24
 
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)
30
- end
31
-
32
25
  def accept(visitor)
33
26
  visitor.visit_comment(@comment)
34
27
  visitor.visit_tags(@tags)
@@ -30,16 +30,6 @@ module Cucumber
30
30
  @background.feature = feature if @background
31
31
  end
32
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)
41
- end
42
-
43
33
  def accept(visitor)
44
34
  visitor.visit_comment(@comment)
45
35
  visitor.visit_tags(@tags)
@@ -48,7 +38,7 @@ module Cucumber
48
38
 
49
39
  skip_invoke! if @background && @background.failed?
50
40
  @examples_array.each do |examples|
51
- visitor.visit_examples(examples) if examples.descend?(visitor)
41
+ visitor.visit_examples(examples)
52
42
  end
53
43
  end
54
44
 
@@ -33,13 +33,11 @@ module Cucumber
33
33
  def accept(visitor)
34
34
  # The only time a Step is visited is when it is in a ScenarioOutline.
35
35
  # Otherwise it's always StepInvocation that gest visited instead.
36
- visit_step_details(visitor, first_match(visitor), @multiline_arg, :skipped, nil, nil)
36
+ visit_step_result(visitor, first_match(visitor), @multiline_arg, :skipped, nil, nil)
37
37
  end
38
38
 
39
- def visit_step_details(visitor, step_match, multiline_arg, status, exception, background)
40
- visitor.visit_step_name(@keyword, step_match, status, source_indent, background)
41
- visitor.visit_multiline_arg(@multiline_arg) if @multiline_arg
42
- visitor.visit_exception(exception, status) if exception
39
+ def visit_step_result(visitor, step_match, multiline_arg, status, exception, background)
40
+ visitor.visit_step_result(@keyword, step_match, @multiline_arg, status, exception, source_indent, background)
43
41
  end
44
42
 
45
43
  def first_match(visitor)
@@ -58,10 +56,6 @@ module Cucumber
58
56
  [:step, @line, @keyword, @name, (@multiline_arg.nil? ? nil : @multiline_arg.to_sexp)].compact
59
57
  end
60
58
 
61
- def matches_lines?(lines)
62
- lines.index(@line) || (@multiline_arg && @multiline_arg.matches_lines?(lines))
63
- end
64
-
65
59
  def source_indent
66
60
  @feature_element.source_indent(text_length)
67
61
  end
@@ -41,10 +41,6 @@ module Cucumber
41
41
  @steps[i-1]
42
42
  end
43
43
 
44
- def matches_lines?(lines)
45
- @steps.detect {|step| step.matches_lines?(lines)}
46
- end
47
-
48
44
  def empty?
49
45
  @steps.empty?
50
46
  end
@@ -20,7 +20,7 @@ module Cucumber
20
20
 
21
21
  def accept(visitor)
22
22
  invoke(visitor.step_mother, visitor.options)
23
- @step.visit_step_details(visitor, @step_match, @multiline_arg, @status, @exception, @background)
23
+ @step.visit_step_result(visitor, @step_match, @multiline_arg, @status, @exception, @background)
24
24
  end
25
25
 
26
26
  def invoke(step_mother, options)
@@ -68,7 +68,6 @@ module Cucumber
68
68
 
69
69
  def status!(status)
70
70
  @status = status
71
- @multiline_arg.status = status if @multiline_arg
72
71
  @matched_cells.each do |cell|
73
72
  cell.status = status
74
73
  end
@@ -86,10 +85,6 @@ module Cucumber
86
85
  end
87
86
  end
88
87
 
89
- def matches_lines?(lines)
90
- @step.matches_lines?(lines)
91
- end
92
-
93
88
  def text_length
94
89
  @step.text_length
95
90
  end
@@ -98,6 +93,10 @@ module Cucumber
98
93
  @step.keyword
99
94
  end
100
95
 
96
+ def multiline_arg
97
+ @step.multiline_arg
98
+ end
99
+
101
100
  def file_colon_line
102
101
  @step.file_colon_line
103
102
  end
@@ -12,6 +12,10 @@ module Cucumber
12
12
 
13
13
  attr_accessor :file
14
14
 
15
+ def self.default_arg_name
16
+ "table"
17
+ end
18
+
15
19
  def initialize(raw, conversions = NULL_CONVERSIONS.dup)
16
20
  # Verify that it's square
17
21
  raw.transpose
@@ -101,10 +105,6 @@ module Cucumber
101
105
  cells_rows.each(&proc)
102
106
  end
103
107
 
104
- def matches_lines?(lines)
105
- cells_rows.detect{|row| row.matches_lines?(lines)}
106
- end
107
-
108
108
  def accept(visitor)
109
109
  cells_rows.each do |row|
110
110
  visitor.visit_table_row(row)
@@ -112,12 +112,6 @@ module Cucumber
112
112
  nil
113
113
  end
114
114
 
115
- def status=(status)
116
- cells_rows.each do |row|
117
- row.status = status
118
- end
119
- end
120
-
121
115
  # For testing only
122
116
  def to_sexp #:nodoc:
123
117
  [:table, *cells_rows.map{|row| row.to_sexp}]
@@ -253,10 +247,6 @@ module Cucumber
253
247
  @table, @cells = table, cells
254
248
  end
255
249
 
256
- def matches_lines?(lines)
257
- lines.index(line)
258
- end
259
-
260
250
  def accept(visitor)
261
251
  each do |cell|
262
252
  visitor.visit_table_cell(cell)
@@ -266,7 +256,7 @@ module Cucumber
266
256
 
267
257
  # For testing only
268
258
  def to_sexp #:nodoc:
269
- [:row, *@cells.map{|cell| cell.to_sexp}]
259
+ [:row, line, *@cells.map{|cell| cell.to_sexp}]
270
260
  end
271
261
 
272
262
  def to_hash #:nodoc:
@@ -289,12 +279,6 @@ module Cucumber
289
279
  "row_#{line}"
290
280
  end
291
281
 
292
- def status=(status)
293
- each do |cell|
294
- cell.status = status
295
- end
296
- end
297
-
298
282
  private
299
283
 
300
284
  def index
@@ -316,7 +300,6 @@ module Cucumber
316
300
 
317
301
  def initialize(value, table, row, col, line)
318
302
  @value, @table, @row, @col, @line = value, table, row, col, line
319
- @status = :passed
320
303
  end
321
304
 
322
305
  def accept(visitor)
@@ -7,24 +7,24 @@ module Cucumber
7
7
  # This gets stored internally as <tt>["invoice", "release_2"]</tt>
8
8
  #
9
9
  class Tags
10
+ def self.strip_prefix(tag_name)
11
+ tag_name =~ /^@(.*)/ ? $1 : tag_name
12
+ end
13
+
10
14
  def initialize(line, tag_names)
11
15
  @line, @tag_names = line, tag_names
12
16
  end
13
17
 
14
- def has_tags?(tags)
15
- (@tag_names & tags).any?
16
- end
17
-
18
- def matches_lines?(lines)
19
- lines.index(@line)
20
- end
21
-
22
18
  def accept(visitor)
23
19
  @tag_names.each do |tag_name|
24
20
  visitor.visit_tag_name(tag_name)
25
21
  end
26
22
  end
27
-
23
+
24
+ def accept_hook?(hook)
25
+ hook.matches_tag_names?(@tag_names)
26
+ end
27
+
28
28
  def to_sexp
29
29
  @tag_names.map{|tag_name| [:tag, tag_name]}
30
30
  end
@@ -8,25 +8,6 @@ module Cucumber
8
8
  def initialize(step_mother)
9
9
  @options = {}
10
10
  @step_mother = step_mother
11
- @current_feature_lines = []
12
- end
13
-
14
- def current_feature_lines=(lines)
15
- @current_feature_lines = lines
16
- end
17
-
18
- def matches_lines?(node)
19
- @current_feature_lines.empty? || node.matches_lines?(@current_feature_lines)
20
- end
21
-
22
- def included_by_tags?(node)
23
- tags = options[:include_tags] || []
24
- tags.empty? || node.has_tags?(tags)
25
- end
26
-
27
- def excluded_by_tags?(node)
28
- tags = options[:exclude_tags] || []
29
- tags.any? && node.has_tags?(tags)
30
11
  end
31
12
 
32
13
  def matches_scenario_names?(node)
@@ -93,14 +74,23 @@ module Cucumber
93
74
  step.accept(self)
94
75
  end
95
76
 
96
- def visit_step_name(keyword, step_match, status, source_indent, background)
77
+ def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
78
+ visit_step_name(keyword, step_match, status, source_indent, background)
79
+ visit_multiline_arg(multiline_arg) if multiline_arg
80
+ visit_exception(exception, status) if exception
81
+ end
82
+
83
+ def visit_step_name(keyword, step_match, status, source_indent, background) #:nodoc:
97
84
  end
98
85
 
99
- def visit_multiline_arg(multiline_arg)
86
+ def visit_multiline_arg(multiline_arg) #:nodoc:
100
87
  multiline_arg.accept(self)
101
88
  end
102
89
 
103
- def visit_py_string(string, status)
90
+ def visit_exception(exception, status) #:nodoc:
91
+ end
92
+
93
+ def visit_py_string(string)
104
94
  end
105
95
 
106
96
  def visit_table_row(table_row)
@@ -114,9 +104,6 @@ module Cucumber
114
104
  def visit_table_cell_value(value, width, status)
115
105
  end
116
106
 
117
- def visit_exception(exception, status)
118
- end
119
-
120
107
  def announce(announcement)
121
108
  end
122
109
 
@@ -187,8 +187,8 @@ module Cucumber
187
187
  excludes = excludes.map{|tag| tag[1..-1]}
188
188
 
189
189
  # Strip @
190
- includes = includes.map{|tag| tag =~ /^@(.*)/ ? $1 : tag}
191
- excludes = excludes.map{|tag| tag =~ /^@(.*)/ ? $1 : tag}
190
+ includes = includes.map{|tag| Ast::Tags.strip_prefix(tag)}
191
+ excludes = excludes.map{|tag| Ast::Tags.strip_prefix(tag)}
192
192
  [includes, excludes]
193
193
  end
194
194
 
@@ -257,11 +257,11 @@ module Cucumber
257
257
 
258
258
  potential_feature_files
259
259
  end
260
-
260
+
261
261
  protected
262
262
 
263
263
  def feature_dirs
264
- feature_files.map { |f| File.directory?(f) ? f : File.dirname(f) }.uniq
264
+ @paths.map { |f| File.directory?(f) ? f : File.dirname(f) }.uniq
265
265
  end
266
266
 
267
267
  def constantize(camel_cased_word)
@@ -52,8 +52,11 @@ module Cucumber
52
52
 
53
53
  verbose_log("Features:")
54
54
  configuration.feature_files.each do |f|
55
- features.add_feature(parser.parse_file(f))
56
- verbose_log(" * #{f}")
55
+ feature = parser.parse_file(f, configuration.options)
56
+ if feature
57
+ features.add_feature(feature)
58
+ verbose_log(" * #{f}")
59
+ end
57
60
  end
58
61
  verbose_log("\n"*2)
59
62
  features
@@ -89,7 +92,11 @@ module Cucumber
89
92
 
90
93
  def enable_diffing
91
94
  if configuration.diff_enabled? && defined?(::Spec)
92
- require 'spec/expectations/differs/default'
95
+ begin
96
+ require 'spec/runner/differs/default' # RSpec >=1.2.4
97
+ rescue ::LoadError
98
+ require 'spec/expectations/differs/default' # RSpec <=1.2.3
99
+ end
93
100
  options = OpenStruct.new(:diff_format => :unified, :context_lines => 3)
94
101
  ::Spec::Expectations.differ = ::Spec::Expectations::Differs::Default.new(options)
95
102
  end
@@ -7,12 +7,14 @@ end
7
7
 
8
8
  class Object
9
9
  def cucumber_instance_exec(check_arity, pseudo_method, *args, &block)
10
- arity = block.arity
11
- arity = 0 if arity == -1
12
10
  cucumber_run_with_backtrace_filtering(pseudo_method) do
13
- if check_arity && args.length != arity
11
+ if check_arity && !cucumber_compatible_arity?(args, block)
14
12
  instance_exec do
15
- raise Cucumber::ArityMismatchError.new("expected #{arity} block argument(s), got #{args.length}")
13
+ s1 = cucumber_arity(block) == 1 ? "" : "s"
14
+ s2 = args.length == 1 ? "" : "s"
15
+ raise Cucumber::ArityMismatchError.new(
16
+ "Your block takes #{cucumber_arity(block)} argument#{s1}, but the Regexp matched #{args.length} argument#{s2}."
17
+ )
16
18
  end
17
19
  else
18
20
  instance_exec(*args, &block)
@@ -20,6 +22,17 @@ class Object
20
22
  end
21
23
  end
22
24
 
25
+ def cucumber_arity(block)
26
+ a = block.arity
27
+ Cucumber::RUBY_1_9 ? a : (a == -1 ? 0 : a)
28
+ end
29
+
30
+ def cucumber_compatible_arity?(args, block)
31
+ a = cucumber_arity(block)
32
+ return true if (a == -1) && Cucumber::RUBY_1_9
33
+ a == args.length
34
+ end
35
+
23
36
  def cucumber_run_with_backtrace_filtering(pseudo_method)
24
37
  begin
25
38
  yield
@@ -19,7 +19,7 @@ elsif Cucumber::WINDOWS && Cucumber::JRUBY
19
19
  end
20
20
  end
21
21
 
22
- Term::ANSIColor.coloring = false if !STDOUT.tty?
22
+ Term::ANSIColor.coloring = false if !STDOUT.tty? and not ENV.has_key?("AUTOTEST")
23
23
 
24
24
  module Cucumber
25
25
  module Formatter
@@ -74,7 +74,8 @@ module Cucumber
74
74
  return if undefined.empty?
75
75
  snippets = undefined.map do |step|
76
76
  step_name = Undefined === step.exception ? step.exception.step_name : step.name
77
- snippet = @step_mother.snippet_text(step.actual_keyword, step_name)
77
+ step_multiline_class = step.multiline_arg ? step.multiline_arg.class : nil
78
+ snippet = @step_mother.snippet_text(step.actual_keyword, step_name, step_multiline_class)
78
79
  snippet
79
80
  end.compact.uniq
80
81
 
@@ -1,3 +1,4 @@
1
+ require 'erb'
1
2
  begin
2
3
  require 'builder'
3
4
  rescue LoadError
@@ -8,6 +9,8 @@ end
8
9
  module Cucumber
9
10
  module Formatter
10
11
  class Html < Ast::Visitor
12
+ include ERB::Util # for the #h method
13
+
11
14
  def initialize(step_mother, io, options)
12
15
  super(step_mother)
13
16
  @builder = Builder::XmlMarkup.new(:target => io, :indent => 2)
@@ -74,9 +77,11 @@ module Cucumber
74
77
  end
75
78
 
76
79
  def visit_outline_table(outline_table)
80
+ @outline_row = 0
77
81
  @builder.table do
78
82
  super(outline_table)
79
83
  end
84
+ @outline_row = nil
80
85
  end
81
86
 
82
87
  def visit_examples_name(keyword, name)
@@ -91,8 +96,13 @@ module Cucumber
91
96
 
92
97
  def visit_step(step)
93
98
  @step_id = step.dom_id
94
- @builder.li(:id => @step_id) do
95
- super
99
+ super
100
+ end
101
+
102
+ def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
103
+ @status = status
104
+ @builder.li(:id => @step_id, :class => status) do
105
+ super(keyword, step_match, multiline_arg, status, exception, source_indent, background)
96
106
  end
97
107
  end
98
108
 
@@ -103,8 +113,8 @@ module Cucumber
103
113
 
104
114
  unless @skip_step
105
115
  step_name = step_match.format_args(lambda{|param| "<span>#{param}</span>"})
106
- @builder.div(:class => status) do |div|
107
- div << "#{keyword} #{step_name}"
116
+ @builder.div do |div|
117
+ div << h("#{keyword} #{step_name}")
108
118
  end
109
119
  end
110
120
  end
@@ -124,8 +134,8 @@ module Cucumber
124
134
  end
125
135
  end
126
136
 
127
- def visit_py_string(string, status)
128
- @builder.pre(:class => status) do |pre|
137
+ def visit_py_string(string)
138
+ @builder.pre(:class => @status) do |pre|
129
139
  pre << string
130
140
  end
131
141
  end
@@ -145,10 +155,14 @@ module Cucumber
145
155
  end
146
156
  end
147
157
  end
158
+ @outline_row += 1 if @outline_row
148
159
  end
149
160
 
150
161
  def visit_table_cell_value(value, width, status)
151
- @builder.td(value, :class => status, :id => "#{@row_id}_#{@col_index}")
162
+ cell_type = @outline_row == 0 ? :th : :td
163
+ attributes = {:id => "#{@row_id}_#{@col_index}"}
164
+ attributes[:class] = status if status
165
+ @builder.__send__(cell_type, value, attributes)
152
166
  @col_index += 1
153
167
  end
154
168