kosmas58-cucumber 0.3.102 → 0.3.103

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/History.txt +8 -2
  2. data/Manifest.txt +6 -1
  3. data/examples/java/README.textile +3 -3
  4. data/examples/self_test/features/sample.feature +1 -1
  5. data/examples/self_test/features/search_sample.feature +1 -1
  6. data/features/custom_formatter.feature +4 -4
  7. data/lib/cucumber/ast.rb +1 -0
  8. data/lib/cucumber/ast/table.rb +4 -4
  9. data/lib/cucumber/ast/visitor.rb +2 -106
  10. data/lib/cucumber/cli/configuration.rb +28 -28
  11. data/lib/cucumber/cli/language_help_formatter.rb +5 -7
  12. data/lib/cucumber/cli/main.rb +3 -3
  13. data/lib/cucumber/formatter/html.rb +203 -113
  14. data/lib/cucumber/formatter/junit.rb +29 -23
  15. data/lib/cucumber/formatter/pdf.rb +74 -69
  16. data/lib/cucumber/formatter/pretty.rb +93 -78
  17. data/lib/cucumber/formatter/profile.rb +2 -2
  18. data/lib/cucumber/formatter/progress.rb +16 -10
  19. data/lib/cucumber/formatter/rerun.rb +4 -5
  20. data/lib/cucumber/formatter/steps.rb +2 -3
  21. data/lib/cucumber/formatter/tag_cloud.rb +7 -6
  22. data/lib/cucumber/formatter/usage.rb +4 -7
  23. data/lib/cucumber/language_support/step_definition_methods.rb +2 -2
  24. data/lib/cucumber/rb_support/rb_language.rb +5 -0
  25. data/lib/cucumber/rb_support/rb_step_definition.rb +3 -3
  26. data/lib/cucumber/rb_support/regexp_argument_matcher.rb +21 -0
  27. data/lib/cucumber/step_argument.rb +9 -0
  28. data/lib/cucumber/step_match.rb +12 -14
  29. data/lib/cucumber/version.rb +1 -1
  30. data/spec/cucumber/ast/background_spec.rb +1 -2
  31. data/spec/cucumber/ast/scenario_outline_spec.rb +3 -2
  32. data/spec/cucumber/ast/scenario_spec.rb +1 -1
  33. data/spec/cucumber/formatter/html_spec.rb +221 -2
  34. data/spec/cucumber/formatter/progress_spec.rb +9 -4
  35. data/spec/cucumber/parser/feature_parser_spec.rb +31 -27
  36. data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +18 -0
  37. data/spec/cucumber/step_match_spec.rb +40 -0
  38. metadata +6 -3
  39. data/lib/cucumber/rb_support/rb_group.rb +0 -32
data/History.txt CHANGED
@@ -1,4 +1,4 @@
1
- == (In Git)
1
+ == 2009-09-24
2
2
 
3
3
  This release gives you back some of the control over the Rails environment that was accidentally taken away from you in the
4
4
  previous release.
@@ -6,10 +6,16 @@ previous release.
6
6
  Using this release on a Rails project requires a rerun of script/generate cucumber.
7
7
 
8
8
  === New Features
9
- * Added a new @no-txn tag to selectively turn off transactions for a particluar scenario.
9
+ * Added a new @no-txn tag to selectively turn off transactions for a particlular scenario.
10
10
  * Added back a way to globally turn off transactions.
11
11
  * Renamed @allow_rescue tag to @allow-rescue.
12
12
 
13
+ === Bugfixes
14
+ * Gracefully handle cases when optional regexp groups are not matched. Ex: /should( not)? be flashed '([^']*?)'$/ (Aslak Hellesøy)
15
+
16
+ === Changed Features
17
+ * The Formatter API has completely changed. Formatters are no longer a double-dispacth visitor - just a single-dispatch listener (#438 Matt Wynne)
18
+
13
19
  == 2009-09-22
14
20
 
15
21
  This release has some changes in the Rails support, so make sure you run "script/generate cucumber" after you upgrade.
data/Manifest.txt CHANGED
@@ -329,6 +329,7 @@ lib/cucumber/ast/step_collection.rb
329
329
  lib/cucumber/ast/step_invocation.rb
330
330
  lib/cucumber/ast/table.rb
331
331
  lib/cucumber/ast/tags.rb
332
+ lib/cucumber/ast/tree_walker.rb
332
333
  lib/cucumber/ast/visitor.rb
333
334
  lib/cucumber/broadcaster.rb
334
335
  lib/cucumber/cli/configuration.rb
@@ -385,13 +386,14 @@ lib/cucumber/rails/test_unit.rb
385
386
  lib/cucumber/rails/world.rb
386
387
  lib/cucumber/rake/task.rb
387
388
  lib/cucumber/rb_support/rb_dsl.rb
388
- lib/cucumber/rb_support/rb_group.rb
389
389
  lib/cucumber/rb_support/rb_hook.rb
390
390
  lib/cucumber/rb_support/rb_language.rb
391
391
  lib/cucumber/rb_support/rb_step_definition.rb
392
392
  lib/cucumber/rb_support/rb_transform.rb
393
393
  lib/cucumber/rb_support/rb_world.rb
394
+ lib/cucumber/rb_support/regexp_argument_matcher.rb
394
395
  lib/cucumber/rspec_neuter.rb
396
+ lib/cucumber/step_argument.rb
395
397
  lib/cucumber/step_match.rb
396
398
  lib/cucumber/step_mother.rb
397
399
  lib/cucumber/version.rb
@@ -422,6 +424,7 @@ spec/cucumber/ast/scenario_spec.rb
422
424
  spec/cucumber/ast/step_collection_spec.rb
423
425
  spec/cucumber/ast/step_spec.rb
424
426
  spec/cucumber/ast/table_spec.rb
427
+ spec/cucumber/ast/tree_walker_spec.rb
425
428
  spec/cucumber/broadcaster_spec.rb
426
429
  spec/cucumber/cli/configuration_spec.rb
427
430
  spec/cucumber/cli/drb_client_spec.rb
@@ -437,7 +440,9 @@ spec/cucumber/formatter/progress_spec.rb
437
440
  spec/cucumber/parser/feature_parser_spec.rb
438
441
  spec/cucumber/parser/table_parser_spec.rb
439
442
  spec/cucumber/rb_support/rb_step_definition_spec.rb
443
+ spec/cucumber/rb_support/regexp_argument_matcher_spec.rb
440
444
  spec/cucumber/sell_cucumbers.feature
445
+ spec/cucumber/step_match_spec.rb
441
446
  spec/cucumber/step_mother_spec.rb
442
447
  spec/cucumber/treetop_parser/empty_feature.feature
443
448
  spec/cucumber/treetop_parser/empty_scenario.feature
@@ -11,8 +11,8 @@ h2. Running the scenarios
11
11
 
12
12
  Open a shell in this directory (java) and execute the following command:
13
13
 
14
- <pre><code>
15
- ant
16
- </code></pre>
14
+ <pre>ant</pre>
17
15
 
18
16
  There is a deliberate error. See if you can fix it!
17
+
18
+ Also see "Cuke4Duke":http://wiki.github.com/aslakhellesoy/cuke4duke for more powerful JVM and Java support for Cucumber.
@@ -12,7 +12,7 @@ Feature: Sample
12
12
  Given passing
13
13
  |a|b|
14
14
  |c|d|
15
-
15
+
16
16
  @four
17
17
  Scenario: Failing
18
18
  Given failing
@@ -20,7 +20,7 @@ Feature: search examples
20
20
  Examples:
21
21
  | state |
22
22
  | passing |
23
-
23
+
24
24
  Scenario Outline: no match in name but in examples
25
25
  Given <state> without a table
26
26
  Examples: Hantu Pisang
@@ -8,7 +8,7 @@ Feature: Custom Formatter
8
8
  | 1 | 1 | 1 | 1 | 1 | 2 | 1 | 2 | 1 | 2 | 1 |
9
9
 
10
10
  """
11
-
11
+
12
12
  Scenario: my own formatter
13
13
  Given a standard Cucumber project directory structure
14
14
  And a file named "features/f.feature" with:
@@ -25,13 +25,13 @@ Feature: Custom Formatter
25
25
  And a file named "features/support/ze/formator.rb" with:
26
26
  """
27
27
  module Ze
28
- class Formator < Cucumber::Ast::Visitor
28
+ class Formator
29
29
  def initialize(step_mother, io, options)
30
- super(step_mother)
30
+ @step_mother = step_mother
31
31
  @io = io
32
32
  end
33
33
 
34
- def visit_scenario_name(keyword, name, file_colon_line, source_indent)
34
+ def scenario_name(keyword, name, file_colon_line, source_indent)
35
35
  @io.puts "$ #{name.upcase}"
36
36
  end
37
37
  end
data/lib/cucumber/ast.rb CHANGED
@@ -13,6 +13,7 @@ require 'cucumber/ast/py_string'
13
13
  require 'cucumber/ast/outline_table'
14
14
  require 'cucumber/ast/examples'
15
15
  require 'cucumber/ast/visitor'
16
+ require 'cucumber/ast/tree_walker'
16
17
 
17
18
  module Cucumber
18
19
  # Classes in this module represent the Abstract Syntax Tree (AST)
@@ -389,11 +389,11 @@ module Cucumber
389
389
 
390
390
  c = Term::ANSIColor.coloring?
391
391
  Term::ANSIColor.coloring = options[:color]
392
- f = Formatter::Pretty.new(nil, io, options)
393
- f.instance_variable_set('@indent', options[:indent])
394
- f.visit_multiline_arg(self)
392
+ formatter = Formatter::Pretty.new(nil, io, options)
393
+ formatter.instance_variable_set('@indent', options[:indent])
394
+ TreeWalker.new(nil, [formatter]).visit_multiline_arg(self)
395
+
395
396
  Term::ANSIColor.coloring = c
396
-
397
397
  io.rewind
398
398
  s = "\n" + io.read + (" " * (options[:indent] - 2))
399
399
  s
@@ -1,115 +1,11 @@
1
1
  module Cucumber
2
2
  module Ast
3
- # Base class for formatters. This class just walks the tree depth first.
4
- # Just override the methods you care about. Remember to call super if you
5
- # override a method.
6
3
  class Visitor
7
- attr_accessor :options #:nodoc:
8
- attr_reader :step_mother #:nodoc:
4
+ DEPRECATION_WARNING = "Cucumber::Ast::Visitor is deprecated and will be removed. You no longer need to inherit from this class."
9
5
 
10
6
  def initialize(step_mother)
11
- @options = {}
12
- @step_mother = step_mother
7
+ raise(DEPRECATION_WARNING)
13
8
  end
14
-
15
- def visit_features(features)
16
- features.accept(self)
17
- end
18
-
19
- def visit_feature(feature)
20
- feature.accept(self)
21
- end
22
-
23
- def visit_comment(comment)
24
- comment.accept(self)
25
- end
26
-
27
- def visit_comment_line(comment_line)
28
- end
29
-
30
- def visit_tags(tags)
31
- tags.accept(self)
32
- end
33
-
34
- def visit_tag_name(tag_name)
35
- end
36
-
37
- def visit_feature_name(name)
38
- end
39
-
40
- # +feature_element+ is either Scenario or ScenarioOutline
41
- def visit_feature_element(feature_element)
42
- feature_element.accept(self)
43
- end
44
-
45
- def visit_background(background)
46
- background.accept(self)
47
- end
48
-
49
- def visit_background_name(keyword, name, file_colon_line, source_indent)
50
- end
51
-
52
- def visit_examples_array(examples_array)
53
- examples_array.accept(self)
54
- end
55
-
56
- def visit_examples(examples)
57
- examples.accept(self)
58
- end
59
-
60
- def visit_examples_name(keyword, name)
61
- end
62
-
63
- def visit_outline_table(outline_table)
64
- @table = outline_table
65
- outline_table.accept(self)
66
- end
67
-
68
- def visit_scenario_name(keyword, name, file_colon_line, source_indent)
69
- end
70
-
71
- def visit_steps(steps)
72
- steps.accept(self)
73
- end
74
-
75
- def visit_step(step)
76
- step.accept(self)
77
- end
78
-
79
- def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
80
- visit_step_name(keyword, step_match, status, source_indent, background)
81
- visit_multiline_arg(multiline_arg) if multiline_arg
82
- visit_exception(exception, status) if exception
83
- end
84
-
85
- def visit_step_name(keyword, step_match, status, source_indent, background) #:nodoc:
86
- end
87
-
88
- def visit_multiline_arg(multiline_arg) #:nodoc:
89
- multiline_arg.accept(self)
90
- end
91
-
92
- def visit_exception(exception, status) #:nodoc:
93
- end
94
-
95
- def visit_py_string(string)
96
- end
97
-
98
- def visit_table_row(table_row)
99
- table_row.accept(self)
100
- end
101
-
102
- def visit_table_cell(table_cell)
103
- table_cell.accept(self)
104
- end
105
-
106
- def visit_table_cell_value(value, status)
107
- end
108
-
109
- # Print +announcement+. This method can be called from within StepDefinitions.
110
- def announce(announcement)
111
- end
112
-
113
9
  end
114
10
  end
115
11
  end
@@ -57,34 +57,9 @@ module Cucumber
57
57
  def drb_port
58
58
  @options[:drb_port].to_i if @options[:drb_port]
59
59
  end
60
-
61
- def build_formatter_broadcaster(step_mother)
62
- return Formatter::Pretty.new(step_mother, nil, @options) if @options[:autoformat]
63
- formatters = @options[:formats].map do |format_and_out|
64
- format = format_and_out[0]
65
- out = format_and_out[1]
66
- if String === out # file name
67
- unless File.directory?(out)
68
- out = File.open(out, Cucumber.file_mode('w'))
69
- at_exit do
70
- out.flush
71
- out.close
72
- end
73
- end
74
- end
75
-
76
- begin
77
- formatter_class = formatter_class(format)
78
- formatter_class.new(step_mother, out, @options)
79
- rescue Exception => e
80
- e.message << "\nError creating formatter: #{format}"
81
- raise e
82
- end
83
- end
84
-
85
- broadcaster = Broadcaster.new(formatters)
86
- broadcaster.options = @options
87
- return broadcaster
60
+
61
+ def build_runner(step_mother, io)
62
+ Ast::TreeWalker.new(step_mother, formatters(step_mother), @options, io)
88
63
  end
89
64
 
90
65
  def formatter_class(format)
@@ -150,6 +125,31 @@ module Cucumber
150
125
  end
151
126
 
152
127
  private
128
+
129
+ def formatters(step_mother)
130
+ return [Formatter::Pretty.new(step_mother, nil, @options)] if @options[:autoformat]
131
+ @options[:formats].map do |format_and_out|
132
+ format = format_and_out[0]
133
+ out = format_and_out[1]
134
+ if String === out # file name
135
+ unless File.directory?(out)
136
+ out = File.open(out, Cucumber.file_mode('w'))
137
+ at_exit do
138
+ out.flush
139
+ out.close
140
+ end
141
+ end
142
+ end
143
+
144
+ begin
145
+ formatter_class = formatter_class(format)
146
+ formatter_class.new(step_mother, out, @options)
147
+ rescue Exception => e
148
+ e.message << "\nError creating formatter: #{format}"
149
+ raise e
150
+ end
151
+ end
152
+ end
153
153
 
154
154
  class LogFormatter < ::Logger::Formatter
155
155
  def call(severity, time, progname, msg)
@@ -19,7 +19,8 @@ http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
19
19
  [lang, Cucumber::LANGUAGES[lang]['name'], Cucumber::LANGUAGES[lang]['native']]
20
20
  end
21
21
  table = Ast::Table.new(raw)
22
- new(nil, io, {:check_lang=>true}).visit_multiline_arg(table)
22
+ formatter = new(nil, io, {:check_lang=>true})
23
+ Ast::TreeWalker.new(nil, [formatter]).visit_multiline_arg(table)
23
24
  end
24
25
 
25
26
  def self.list_keywords(io, lang)
@@ -31,19 +32,17 @@ http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
31
32
  new(nil, io, {:incomplete => language.incomplete?}).visit_multiline_arg(table)
32
33
  end
33
34
 
34
- def visit_multiline_arg(table)
35
+ def before_visit_multiline_arg(table)
35
36
  if @options[:incomplete]
36
37
  @io.puts(format_string(INCOMPLETE, :failed))
37
38
  end
38
- super
39
39
  end
40
40
 
41
- def visit_table_row(table_row)
41
+ def before_visit_table_row(table_row)
42
42
  @col = 1
43
- super
44
43
  end
45
44
 
46
- def visit_table_cell_value(value, status)
45
+ def before_visit_table_cell_value(value, status)
47
46
  if @col == 1
48
47
  if(@options[:check_lang])
49
48
  @incomplete = Parser::NaturalLanguage.get(nil, value).incomplete?
@@ -54,7 +53,6 @@ http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
54
53
  end
55
54
 
56
55
  @col += 1
57
- super(value, status)
58
56
  end
59
57
  end
60
58
  end
@@ -50,9 +50,9 @@ module Cucumber
50
50
 
51
51
  enable_diffing
52
52
 
53
- visitor = configuration.build_formatter_broadcaster(step_mother)
54
- step_mother.visitor = visitor # Needed to support World#announce
55
- visitor.visit_features(features)
53
+ runner = configuration.build_runner(step_mother, @out_stream)
54
+ step_mother.visitor = runner # Needed to support World#announce
55
+ runner.visit_features(features)
56
56
 
57
57
  failure = if exceeded_tag_limts?(features)
58
58
  FAILURE
@@ -1,214 +1,278 @@
1
1
  require 'cucumber/formatter/ordered_xml_markup'
2
2
  require 'cucumber/formatter/duration'
3
+ require 'xml'
4
+ require 'ruby-debug'
3
5
 
4
6
  module Cucumber
5
7
  module Formatter
6
8
  # The formatter used for <tt>--format html</tt>
7
- class Html < Ast::Visitor
9
+ class Html
8
10
  include ERB::Util # for the #h method
9
11
  include Duration
10
12
 
11
13
  def initialize(step_mother, io, options)
12
- super(step_mother)
14
+ @io = io
13
15
  @options = options
14
- @builder = create_builder(io)
16
+ @buffer = {}
17
+ @current_builder = create_builder(@io)
15
18
  end
16
19
 
17
- def create_builder(io)
18
- OrderedXmlMarkup.new(:target => io, :indent => 0)
20
+ def before_features(features)
21
+ start_buffering :features
19
22
  end
20
23
 
21
- def visit_features(features)
24
+ def after_features(features)
25
+ stop_buffering :features
22
26
  # <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
23
- @builder.declare!(
27
+ builder.declare!(
24
28
  :DOCTYPE,
25
29
  :html,
26
30
  :PUBLIC,
27
31
  '-//W3C//DTD XHTML 1.0 Strict//EN',
28
32
  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
29
33
  )
30
- @builder.html(:xmlns => 'http://www.w3.org/1999/xhtml') do
31
- @builder.head do
32
- @builder.meta(:content => 'text/html;charset=utf-8')
33
- @builder.title 'Cucumber'
34
+ builder.html(:xmlns => 'http://www.w3.org/1999/xhtml') do
35
+ builder.head do
36
+ builder.meta(:content => 'text/html;charset=utf-8')
37
+ builder.title 'Cucumber'
34
38
  inline_css
35
39
  end
36
- @builder.body do
37
- @builder.div(:class => 'cucumber') do
38
- super
39
- @builder.div(format_duration(features.duration), :class => 'duration')
40
+ builder.body do
41
+ builder.div(:class => 'cucumber') do
42
+ builder << buffer(:features)
43
+ builder.div(format_duration(features.duration), :class => 'duration')
40
44
  end
41
45
  end
42
46
  end
43
47
  end
44
-
45
- def visit_comment(comment)
46
- @builder.pre(:class => 'comment') do
47
- super
48
+
49
+ def before_feature(feature)
50
+ start_buffering :feature
51
+ @exceptions = []
52
+ end
53
+
54
+ def after_feature(feature)
55
+ stop_buffering :feature
56
+ builder.div(:class => 'feature') do
57
+ builder << buffer(:feature)
48
58
  end
49
59
  end
50
60
 
51
- def visit_comment_line(comment_line)
52
- @builder.text!(comment_line)
53
- @builder.br
61
+ def before_comment(comment)
62
+ start_buffering :comment
54
63
  end
55
64
 
56
- def visit_feature(feature)
57
- @exceptions = []
58
- @builder.div(:class => 'feature') do
59
- super
65
+ def after_comment(comment)
66
+ stop_buffering :comment
67
+ builder.pre(:class => 'comment') do
68
+ builder << buffer(:comment)
60
69
  end
61
70
  end
62
71
 
63
- def visit_tags(tags)
64
- super
72
+ def comment_line(comment_line)
73
+ builder.text!(comment_line)
74
+ builder.br
75
+ end
76
+
77
+ def after_tags(tags)
65
78
  @tag_spacer = nil
66
79
  end
67
-
68
- def visit_tag_name(tag_name)
69
- @builder.text!(@tag_spacer) if @tag_spacer
80
+
81
+ def tag_name(tag_name)
82
+ builder.text!(@tag_spacer) if @tag_spacer
70
83
  @tag_spacer = ' '
71
- @builder.span(tag_name, :class => 'tag')
84
+ builder.span(tag_name, :class => 'tag')
72
85
  end
73
86
 
74
- def visit_feature_name(name)
87
+ def feature_name(name)
75
88
  lines = name.split(/\r?\n/)
76
89
  return if lines.empty?
77
- @builder.h2 do |h2|
78
- @builder.span(lines[0], :class => 'val')
90
+ builder.h2 do |h2|
91
+ builder.span(lines[0], :class => 'val')
79
92
  end
80
- @builder.p(:class => 'narrative') do
93
+ builder.p(:class => 'narrative') do
81
94
  lines[1..-1].each do |line|
82
- @builder.text!(line.strip)
83
- @builder.br
95
+ builder.text!(line.strip)
96
+ builder.br
84
97
  end
85
98
  end
86
99
  end
87
100
 
88
- def visit_background(background)
89
- @builder.div(:class => 'background') do
90
- @in_background = true
91
- super
92
- @in_background = nil
101
+ def before_background(background)
102
+ @in_background = true
103
+ start_buffering :background
104
+ end
105
+
106
+ def after_background(background)
107
+ stop_buffering :background
108
+ @in_background = nil
109
+ builder.div(:class => 'background') do
110
+ builder << buffer(:background)
93
111
  end
94
112
  end
95
113
 
96
- def visit_background_name(keyword, name, file_colon_line, source_indent)
114
+ def background_name(keyword, name, file_colon_line, source_indent)
97
115
  @listing_background = true
98
- @builder.h3 do |h3|
99
- @builder.span(keyword, :class => 'keyword')
100
- @builder.text!(' ')
101
- @builder.span(name, :class => 'val')
116
+ builder.h3 do |h3|
117
+ builder.span(keyword, :class => 'keyword')
118
+ builder.text!(' ')
119
+ builder.span(name, :class => 'val')
102
120
  end
103
121
  end
104
122
 
105
- def visit_feature_element(feature_element)
123
+ def before_feature_element(feature_element)
124
+ start_buffering :feature_element
125
+ end
126
+
127
+ def after_feature_element(feature_element)
128
+ stop_buffering :feature_element
106
129
  css_class = {
107
130
  Ast::Scenario => 'scenario',
108
131
  Ast::ScenarioOutline => 'scenario outline'
109
132
  }[feature_element.class]
110
- @builder.div(:class => css_class) do
111
- super
133
+
134
+ builder.div(:class => css_class) do
135
+ builder << buffer(:feature_element)
112
136
  end
113
137
  @open_step_list = true
114
138
  end
115
-
116
- def visit_scenario_name(keyword, name, file_colon_line, source_indent)
139
+
140
+ def scenario_name(keyword, name, file_colon_line, source_indent)
117
141
  @listing_background = false
118
- @builder.h3 do
119
- @builder.span(keyword, :class => 'keyword')
120
- @builder.text!(' ')
121
- @builder.span(name, :class => 'val')
142
+ builder.h3 do
143
+ builder.span(keyword, :class => 'keyword')
144
+ builder.text!(' ')
145
+ builder.span(name, :class => 'val')
122
146
  end
123
147
  end
124
-
125
- def visit_outline_table(outline_table)
148
+
149
+ def before_outline_table(outline_table)
126
150
  @outline_row = 0
127
- @builder.table do
128
- super(outline_table)
151
+ start_buffering :outline_table
152
+ end
153
+
154
+ def after_outline_table(outline_table)
155
+ stop_buffering :outline_table
156
+ builder.table do
157
+ builder << buffer(:outline_table)
129
158
  end
130
159
  @outline_row = nil
131
160
  end
132
161
 
133
- def visit_examples(examples)
134
- @builder.div(:class => 'examples') do
135
- super(examples)
162
+ def before_examples(examples)
163
+ start_buffering :examples
164
+ end
165
+
166
+ def after_examples(examples)
167
+ stop_buffering :examples
168
+ builder.div(:class => 'examples') do
169
+ builder << buffer(:examples)
136
170
  end
137
171
  end
138
172
 
139
- def visit_examples_name(keyword, name)
140
- @builder.h4 do
141
- @builder.span(keyword, :class => 'keyword')
142
- @builder.text!(' ')
143
- @builder.span(name, :class => 'val')
173
+ def examples_name(keyword, name)
174
+ builder.h4 do
175
+ builder.span(keyword, :class => 'keyword')
176
+ builder.text!(' ')
177
+ builder.span(name, :class => 'val')
144
178
  end
145
179
  end
146
180
 
147
- def visit_steps(steps)
148
- @builder.ol do
149
- super
181
+ def before_steps(steps)
182
+ start_buffering :steps
183
+ end
184
+
185
+ def after_steps(steps)
186
+ stop_buffering :steps
187
+ builder.ol do
188
+ builder << buffer(:steps)
150
189
  end
151
190
  end
152
-
153
- def visit_step(step)
191
+
192
+ def before_step(step)
154
193
  @step_id = step.dom_id
155
- super
156
194
  end
157
195
 
158
- def visit_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
196
+ def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
197
+ start_buffering :step_result
198
+ @hide_this_step = false
159
199
  if exception
160
- return if @exceptions.index(exception)
200
+ if @exceptions.include?(exception)
201
+ @hide_this_step = true
202
+ return
203
+ end
161
204
  @exceptions << exception
162
205
  end
163
- return if status != :failed && @in_background ^ background
206
+ if status != :failed && @in_background ^ background
207
+ @hide_this_step = true
208
+ return
209
+ end
164
210
  @status = status
165
- @builder.li(:id => @step_id, :class => "step #{status}") do
166
- super(keyword, step_match, multiline_arg, status, exception, source_indent, background)
211
+ end
212
+
213
+ def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
214
+ stop_buffering :step_result
215
+ return if @hide_this_step
216
+ builder.li(:id => @step_id, :class => "step #{status}") do
217
+ builder << buffer(:step_result)
167
218
  end
168
219
  end
169
220
 
170
- def visit_step_name(keyword, step_match, status, source_indent, background)
221
+ def step_name(keyword, step_match, status, source_indent, background)
171
222
  @step_matches ||= []
172
223
  background_in_scenario = background && !@listing_background
173
224
  @skip_step = @step_matches.index(step_match) || background_in_scenario
174
225
  @step_matches << step_match
175
-
226
+
176
227
  unless @skip_step
177
228
  build_step(keyword, step_match, status)
178
229
  end
179
230
  end
180
231
 
181
- def visit_exception(exception, status)
182
- @builder.pre(format_exception(exception), :class => status)
232
+ def exception(exception, status)
233
+ return if @hide_this_step
234
+ builder.pre(format_exception(exception), :class => status)
235
+ end
236
+
237
+ def before_multiline_arg(multiline_arg)
238
+ start_buffering :multiline_arg
183
239
  end
184
240
 
185
- def visit_multiline_arg(multiline_arg)
186
- return if @skip_step
241
+ def after_multiline_arg(multiline_arg)
242
+ stop_buffering :multiline_arg
243
+ return if @hide_this_step || @skip_step
187
244
  if Ast::Table === multiline_arg
188
- @builder.table do
189
- super
245
+ builder.table do
246
+ builder << buffer(:multiline_arg)
190
247
  end
191
248
  else
192
- super
249
+ builder << buffer(:multiline_arg)
193
250
  end
194
251
  end
195
252
 
196
- def visit_py_string(string)
197
- @builder.pre(:class => 'val') do |pre|
198
- @builder << string.gsub("\n", '&#x000A;')
253
+ def py_string(string)
254
+ return if @hide_this_step
255
+ builder.pre(:class => 'val') do |pre|
256
+ builder << string.gsub("\n", '&#x000A;')
199
257
  end
200
258
  end
201
259
 
202
- def visit_table_row(table_row)
260
+ def before_table_row(table_row)
203
261
  @row_id = table_row.dom_id
204
262
  @col_index = 0
205
- @builder.tr(:id => @row_id) do
206
- super
263
+ start_buffering :table_row
264
+ end
265
+
266
+ def after_table_row(table_row)
267
+ stop_buffering :table_row
268
+ return if @hide_this_step
269
+ builder.tr(:id => @row_id) do
270
+ builder << buffer(:table_row)
207
271
  end
208
272
  if table_row.exception
209
- @builder.tr do
210
- @builder.td(:colspan => @col_index.to_s, :class => 'failed') do
211
- @builder.pre do |pre|
273
+ builder.tr do
274
+ builder.td(:colspan => @col_index.to_s, :class => 'failed') do
275
+ builder.pre do |pre|
212
276
  pre << format_exception(table_row.exception)
213
277
  end
214
278
  end
@@ -217,45 +281,71 @@ module Cucumber
217
281
  @outline_row += 1 if @outline_row
218
282
  end
219
283
 
220
- def visit_table_cell_value(value, status)
284
+ def table_cell_value(value, status)
285
+ return if @hide_this_step
286
+
221
287
  cell_type = @outline_row == 0 ? :th : :td
222
288
  attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'val'}
223
289
  attributes[:class] += " #{status}" if status
224
290
  build_cell(cell_type, value, attributes)
225
291
  @col_index += 1
226
292
  end
227
-
293
+
228
294
  def announce(announcement)
229
- @builder.pre(announcement, :class => 'announcement')
295
+ builder.pre(announcement, :class => 'announcement')
230
296
  end
231
-
232
- protected
297
+
298
+ private
233
299
 
234
300
  def build_step(keyword, step_match, status)
235
301
  step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
236
- @builder.div do |div|
237
- @builder.span(keyword, :class => 'keyword')
238
- @builder.text!(' ')
239
- @builder.span(:class => 'step val') do |name|
302
+ builder.div do |div|
303
+ builder.span(keyword, :class => 'keyword')
304
+ builder.text!(' ')
305
+ builder.span(:class => 'step val') do |name|
240
306
  name << h(step_name).gsub(/&lt;span class=&quot;(.*?)&quot;&gt;/, '<span class="\1">').gsub(/&lt;\/span&gt;/, '</span>')
241
307
  end
242
308
  end
243
309
  end
244
-
310
+
245
311
  def build_cell(cell_type, value, attributes)
246
- @builder.__send__(cell_type, value, attributes)
312
+ builder.__send__(cell_type, value, attributes)
247
313
  end
248
-
314
+
249
315
  def inline_css
250
- @builder.style(:type => 'text/css') do
251
- @builder.text!(File.read(File.dirname(__FILE__) + '/cucumber.css'))
316
+ builder.style(:type => 'text/css') do
317
+ builder.text!(File.read(File.dirname(__FILE__) + '/cucumber.css'))
252
318
  end
253
319
  end
254
-
320
+
255
321
  def format_exception(exception)
256
322
  h((["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n"))
257
323
  end
258
324
 
325
+ def builder
326
+ @current_builder
327
+ end
328
+
329
+ def buffer(label)
330
+ result = @buffer[label]
331
+ @buffer[label] = ''
332
+ result
333
+ end
334
+
335
+ def start_buffering(label)
336
+ @buffer[label] ||= ''
337
+ @parent_builder ||= {}
338
+ @parent_builder[label] = @current_builder
339
+ @current_builder = create_builder(@buffer[label])
340
+ end
341
+
342
+ def stop_buffering(label)
343
+ @current_builder = @parent_builder[label]
344
+ end
345
+
346
+ def create_builder(io)
347
+ OrderedXmlMarkup.new(:target => io, :indent => 0)
348
+ end
259
349
  end
260
350
  end
261
351
  end