cuporter 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/README.textile +123 -44
  2. data/Rakefile +5 -5
  3. data/bin/cuporter +19 -11
  4. data/lib/cuporter.rb +15 -26
  5. data/lib/cuporter/cli/options.rb +95 -22
  6. data/lib/cuporter/document.rb +23 -0
  7. data/lib/cuporter/document/html_document.rb +82 -0
  8. data/lib/cuporter/extensions/nokogiri.rb +46 -0
  9. data/lib/cuporter/feature_parser.rb +38 -19
  10. data/lib/cuporter/filter.rb +1 -0
  11. data/lib/cuporter/formatters/csv.rb +69 -0
  12. data/lib/cuporter/formatters/text.rb +166 -0
  13. data/lib/cuporter/formatters/xml_to_html.xslt +315 -0
  14. data/lib/cuporter/node.rb +7 -83
  15. data/lib/cuporter/node/node_base.rb +114 -0
  16. data/lib/cuporter/node/numbering.rb +32 -0
  17. data/lib/cuporter/node/sorting.rb +27 -0
  18. data/lib/cuporter/node/tagged_node.rb +22 -0
  19. data/lib/cuporter/node/totalling.rb +19 -0
  20. data/lib/cuporter/node/types.rb +158 -0
  21. data/lib/cuporter/node_parser.rb +62 -0
  22. data/lib/cuporter/report/feature_report.rb +23 -0
  23. data/lib/cuporter/report/report_base.rb +69 -0
  24. data/lib/cuporter/report/tag_report.rb +27 -7
  25. data/lib/cuporter/report/tree_report.rb +24 -0
  26. data/lib/cuporter/tag_nodes_parser.rb +94 -0
  27. data/public/images/ajax-loader.gif +0 -0
  28. data/public/images/file.gif +0 -0
  29. data/public/images/folder-closed.gif +0 -0
  30. data/public/images/folder.gif +0 -0
  31. data/public/images/minus.gif +0 -0
  32. data/public/images/plus.gif +0 -0
  33. data/public/images/treeview-black-line.gif +0 -0
  34. data/public/images/treeview-black.gif +0 -0
  35. data/public/images/treeview-default-line.gif +0 -0
  36. data/public/images/treeview-default.gif +0 -0
  37. data/public/images/treeview-famfamfam-line.gif +0 -0
  38. data/public/images/treeview-famfamfam.gif +0 -0
  39. data/public/images/treeview-gray-line.gif +0 -0
  40. data/public/images/treeview-gray.gif +0 -0
  41. data/public/images/treeview-red-line.gif +0 -0
  42. data/public/images/treeview-red.gif +0 -0
  43. data/public/javascripts/expand-collapse.js +47 -0
  44. data/public/javascripts/jquery-min.js +167 -0
  45. data/public/javascripts/jquery.treeview.js +267 -0
  46. data/public/javascripts/treeview-loader.js +11 -0
  47. data/public/stylesheets/cuporter.css +101 -0
  48. data/{lib/cuporter/formatter/name_report/style.css → public/stylesheets/feature_style.css} +17 -18
  49. data/public/stylesheets/jquery.treeview.css +75 -0
  50. data/{lib/cuporter/formatter/tag_report/style.css → public/stylesheets/tag_style.css} +17 -22
  51. data/public/stylesheets/tree_style.css +104 -0
  52. metadata +52 -37
  53. data/lib/cuporter/example_set_node.rb +0 -15
  54. data/lib/cuporter/formatter/csv_text_methods.rb +0 -20
  55. data/lib/cuporter/formatter/cuporter.css +0 -64
  56. data/lib/cuporter/formatter/html_methods.rb +0 -122
  57. data/lib/cuporter/formatter/html_node_writer.rb +0 -104
  58. data/lib/cuporter/formatter/jquery-min.js +0 -154
  59. data/lib/cuporter/formatter/name_report/csv.rb +0 -12
  60. data/lib/cuporter/formatter/name_report/html.rb +0 -48
  61. data/lib/cuporter/formatter/name_report/html_node_writer.rb +0 -18
  62. data/lib/cuporter/formatter/name_report/text.rb +0 -16
  63. data/lib/cuporter/formatter/name_report/text_node_writer.rb +0 -16
  64. data/lib/cuporter/formatter/pretty_text_methods.rb +0 -22
  65. data/lib/cuporter/formatter/tag_report/csv.rb +0 -12
  66. data/lib/cuporter/formatter/tag_report/html.rb +0 -35
  67. data/lib/cuporter/formatter/tag_report/html_node_writer.rb +0 -19
  68. data/lib/cuporter/formatter/tag_report/text.rb +0 -14
  69. data/lib/cuporter/formatter/tag_report/text_node_writer.rb +0 -17
  70. data/lib/cuporter/formatter/text_methods.rb +0 -19
  71. data/lib/cuporter/formatter/writer.rb +0 -33
  72. data/lib/cuporter/name_list_parser.rb +0 -44
  73. data/lib/cuporter/node_numberer.rb +0 -18
  74. data/lib/cuporter/report/name_report.rb +0 -19
  75. data/lib/cuporter/report/report.rb +0 -22
  76. data/lib/cuporter/tag_list_node.rb +0 -54
  77. data/lib/cuporter/tag_list_parser.rb +0 -39
@@ -0,0 +1,23 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ module Document
4
+
5
+ def self.new_xml
6
+ doc = Nokogiri::XML::Document.new
7
+ doc << Nokogiri::XML::Node.new('xml', doc)
8
+ doc.root << Nokogiri::XML::Node.new('body', doc)
9
+ doc
10
+ end
11
+
12
+ def self.new_html(view)
13
+ Nokogiri::XML::Document.send(:include, Cuporter::Document::Html)
14
+ doc = Nokogiri::XML::Document.new
15
+ doc.view = view
16
+ root = Nokogiri::XML::Node.new('html', doc)
17
+ root.create_internal_subset( 'html', "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")
18
+ doc << root
19
+ doc
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,82 @@
1
+ # Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ module Document
4
+ module Html
5
+ attr_accessor :view
6
+
7
+ def add_report(node)
8
+ root << head(node['title'])
9
+ body = new_node('body')
10
+ body << node
11
+ root << body
12
+ end
13
+
14
+ # remove CDATA tags so CSS works
15
+ def to_html(options = {})
16
+ to_xhtml(options).gsub(/(<!\[CDATA\[|\]\]>)/,'')
17
+ end
18
+
19
+ private
20
+
21
+ def head(title_text)
22
+ h = new_node(:head)
23
+ title = new_node(:title)
24
+ title.content = title_text
25
+ h << title
26
+
27
+ case view
28
+ when "tree"
29
+ h << link_js("jquery-min.js")
30
+ h << link_js("jquery.treeview.js")
31
+ h << link_css("jquery.treeview.css")
32
+ h << link_js("treeview-loader.js")
33
+ h << link_css("cuporter.css")
34
+ h << link_css("#{view}_style.css")
35
+ else
36
+ h << script_js("jquery-min.js")
37
+ h << style_css("cuporter.css")
38
+ h << style_css("#{view}_style.css")
39
+ h << script_js("expand-collapse.js")
40
+ end
41
+ h
42
+ end
43
+
44
+ def assets_dir
45
+ @assets_dir ||= File.expand_path('public', File.dirname(__FILE__) + "/../../../")
46
+ end
47
+
48
+ def style_css(file)
49
+ style = new_node('style', 'type' => 'text/css')
50
+ style << file_contents("#{assets_dir}/stylesheets/#{file}")
51
+ style
52
+ end
53
+
54
+ def script_js(file)
55
+ script = new_node('script', 'type' => 'text/javascript')
56
+ script << file_contents("#{assets_dir}/javascripts/#{file}")
57
+ script
58
+ end
59
+
60
+ def link_css(file)
61
+ new_node('link', 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => "#{assets_dir}/stylesheets/#{file}")
62
+ end
63
+
64
+ def link_js(file)
65
+ new_node('script', 'type' => 'text/javascript', 'src' => "#{assets_dir}/javascripts/#{file}")
66
+ end
67
+
68
+ def file_contents(file_name)
69
+ create_cdata("\n#{File.read(file_name)}")
70
+ end
71
+
72
+ def new_node(name, attributes = {})
73
+ n = Nokogiri::XML::Node.new(name.to_s, self)
74
+ attributes.each do | attr, value |
75
+ n[attr.to_s] = value.to_s
76
+ end
77
+ n
78
+ end
79
+ end
80
+ end
81
+ end
82
+
@@ -0,0 +1,46 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ require 'rubygems'
3
+ require 'nokogiri'
4
+ Nokogiri::XML::Node.class_eval do
5
+ # undefine the spaceship (comparison operator used by sort)
6
+ # so our mixed-in versions get used.
7
+ remove_method :<=>
8
+ end
9
+
10
+ module NodeSetExtensions
11
+ def sort
12
+ return self if empty?
13
+ sorted = to_a.sort
14
+ self.class.new(document, sorted)
15
+ end
16
+
17
+ # value equivalence
18
+ def eql?(other)
19
+ return false unless other.is_a?(Nokogiri::XML::NodeSet)
20
+ return false unless length == other.length
21
+ each_with_index do |node, i|
22
+ return false unless node.eql?(other[i])
23
+ end
24
+ true
25
+ end
26
+ end
27
+ Nokogiri::XML::NodeSet.send(:include, NodeSetExtensions)
28
+
29
+ module DocumentExtensions
30
+
31
+ def to_html(options = {})
32
+ to_xml(options)
33
+ end
34
+
35
+ def add_report(report_node)
36
+ root.at(:body) << report_node
37
+ end
38
+
39
+ def add_filter_summary(filter)
40
+ return if filter.empty?
41
+ s = Cuporter::Node.new_node(:FilterSummary, self)
42
+ s.add(filter)
43
+ root.at(:body) << s
44
+ end
45
+ end
46
+ Nokogiri::XML::Document.send(:include, DocumentExtensions)
@@ -2,21 +2,30 @@
2
2
 
3
3
  module Cuporter
4
4
  class FeatureParser
5
- FEATURE_LINE = /^\s*(Feature:[^#]+)/
5
+ FEATURE_LINE = /^\s*(Feature:[^#]*)/
6
6
  TAG_LINE = /^\s*(@\w.+)/
7
- SCENARIO_LINE = /^\s*(Scenario:[^#]+)$/
8
- SCENARIO_OUTLINE_LINE = /^\s*(Scenario Outline:[^#]+)$/
7
+ SCENARIO_LINE = /^\s*(Scenario:[^#]*)$/
8
+ SCENARIO_OUTLINE_LINE = /^\s*(Scenario Outline:[^#]*)$/
9
9
  SCENARIO_SET_LINE = /^\s*(Scenarios:[^#]*)$/
10
10
  EXAMPLE_SET_LINE = /^\s*(Examples:[^#]*)$/
11
11
  EXAMPLE_LINE = /^\s*(\|.*\|)\s*$/
12
+ PY_STRING_LINE = /^\s*"""\s*$/
12
13
 
13
- def self.tag_list(file)
14
- TagListParser.new(file).parse_feature
14
+ # adds a node to the doc for each cucumber '@' tag, populated with features and
15
+ # scenarios
16
+ def self.tag_nodes(file, report, filter, root_dir)
17
+ parser = TagNodesParser.new(file, report, filter)
18
+ parser.root = root_dir
19
+ parser.parse_feature
15
20
  end
16
21
 
17
- def self.name_list(file, filter)
18
- NameListParser.new(file, filter).parse_feature
22
+ # returns a feature node populated with scenarios
23
+ def self.node(file, doc, filter, root_dir)
24
+ parser = NodeParser.new(file, doc, filter)
25
+ parser.root = root_dir
26
+ parser.parse_feature
19
27
  end
28
+ attr_writer :root
20
29
 
21
30
  def initialize(file)
22
31
  @file = file
@@ -24,36 +33,46 @@ module Cuporter
24
33
  @lines = File.read(@file).split(/\n/)
25
34
  end
26
35
 
36
+ def file_relative_path
37
+ @file_relative_path ||= @file.sub(/^.*#{@root}\//,"#{@root}/")
38
+ end
39
+
27
40
  def parse_feature
41
+ @open_comment_block = false
42
+
28
43
  @lines.each do |line|
44
+ next if @open_comment_block && line !~ PY_STRING_LINE
45
+
29
46
  case line
30
- when FeatureParser::TAG_LINE
47
+ when PY_STRING_LINE
48
+ # toggle, to declare the multiline comment 'heredoc' open or closed
49
+ @open_comment_block = !@open_comment_block
50
+ when TAG_LINE
31
51
  # may be more than one tag line
32
52
  @current_tags |= $1.strip.split(/\s+/)
33
- when FeatureParser::FEATURE_LINE
34
- @feature = new_feature_node($1)
35
- @feature.file = @file.sub(/^.*features\//,"features/")
53
+ when FEATURE_LINE
54
+ @feature = new_feature_node($1.strip, file_relative_path)
36
55
  @current_tags = []
37
- when FeatureParser::SCENARIO_LINE
56
+ when SCENARIO_LINE
38
57
  # How do we know when we have read all the lines from a "Scenario Outline:"?
39
58
  # One way is when we encounter a "Scenario:"
40
59
  close_scenario_outline
41
60
 
42
- handle_scenario_line($1)
61
+ handle_scenario_line($1.strip)
43
62
  @current_tags = []
44
- when FeatureParser::SCENARIO_OUTLINE_LINE
63
+ when SCENARIO_OUTLINE_LINE
45
64
  # ... another is when we hit a subsequent "Scenario Outline:"
46
65
  close_scenario_outline
47
66
 
48
- @scenario_outline = new_scenario_outline_node($1)
67
+ @scenario_outline = new_scenario_outline_node($1.strip)
49
68
  @current_tags = []
50
- when FeatureParser::EXAMPLE_SET_LINE, FeatureParser::SCENARIO_SET_LINE
69
+ when EXAMPLE_SET_LINE, SCENARIO_SET_LINE
51
70
  handle_example_set_line if @example_set
52
71
 
53
- @example_set = new_example_set_node($1)
72
+ @example_set = new_example_set_node($1.strip)
54
73
  @current_tags = []
55
- when @example_set && FeatureParser::EXAMPLE_LINE
56
- @example_set.add_child(Node.new($1))
74
+ when @example_set && EXAMPLE_LINE
75
+ new_example_line($1.strip)
57
76
  end
58
77
  end
59
78
 
@@ -26,6 +26,7 @@ module Cuporter
26
26
  unless none.empty?
27
27
  pass = false if !(other_tags & none).empty?
28
28
  end
29
+
29
30
  pass
30
31
  end
31
32
 
@@ -0,0 +1,69 @@
1
+ # Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ module Formatters
4
+
5
+ module Csv
6
+ extend Util
7
+ extend NodeFormatters
8
+
9
+ COMMA = ","
10
+
11
+ def tab_stop
12
+ @@tab_stop ||= COMMA
13
+ end
14
+
15
+ def total_column_spacer
16
+ @@total_column_spacer ||= Cuporter.options[:total] ? tab_stop : ""
17
+ end
18
+
19
+ def total_column
20
+ @@total_column ||= total_column_spacer
21
+ end
22
+
23
+ def total(node)
24
+ t = node['total'] ? "[#{node['total']}]," : COMMA
25
+ "#{tab_stop * depth(node)}#{t}"
26
+ end
27
+
28
+ def number(node)
29
+ n = node['number'] ? "#{node['number']})," : COMMA
30
+ "#{tab_stop * depth(node)}#{n}"
31
+ end
32
+
33
+ def blank_for_number(node)
34
+ "#{tab_stop * depth(node)},"
35
+ end
36
+
37
+ def cuke_name(value)
38
+ "#{value},"
39
+ end
40
+
41
+ def fs_name(value)
42
+ "#{value.upcase},"
43
+ end
44
+
45
+ def tags(value)
46
+ return "" unless value
47
+ "#{value},"
48
+ end
49
+
50
+ def file_path(value, length = nil)
51
+ return "" unless value
52
+ "#{value},"
53
+ end
54
+
55
+ def title(value)
56
+ return "" unless value
57
+ "#{value},"
58
+ end
59
+
60
+ def example_name(name)
61
+ name.sub!(/^\s*\|/, '')
62
+ name.sub!(/\|\s*$/, '')
63
+ name.gsub(/\|/,";")
64
+ end
65
+
66
+ extend(Cuporter::Formatters::Csv)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,166 @@
1
+ # Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ module Formatters
4
+
5
+ module Util
6
+ def terminal_width
7
+ @@terminal_width ||= (`tput cols` || 120).to_i
8
+ end
9
+
10
+ def depth(node)
11
+ node.parent.path.sub(/^.*\/report/, 'report').split('/').size#- 2
12
+ end
13
+
14
+ end
15
+
16
+ module NodeFormatters
17
+ def report(l, node)
18
+ return l unless Cuporter.options[:text_summary]
19
+ l += total(node)
20
+ l += cuke_name(node['cuke_name'])
21
+ l += title(node['title'])
22
+ l
23
+ end
24
+ def dir(l, node)
25
+ l += total(node)
26
+ l += fs_name(node['fs_name'])
27
+ l
28
+ end
29
+ def file(l, node)
30
+ l += total(node)
31
+ l += fs_name(node['fs_name'])
32
+ l
33
+ end
34
+ def tag(l, node)
35
+ l += total(node)
36
+ l += cuke_name(node['cuke_name'])
37
+ l
38
+ end
39
+ def feature(l, node)
40
+ l += total(node)
41
+ l += cuke_name(node['cuke_name'])
42
+ l += tags(node['tags'])
43
+ l += file_path(node['file_path'], l.size)
44
+ l
45
+ end
46
+ def scenario(l, node)
47
+ l += total_column_spacer
48
+ l += number(node)
49
+ l += cuke_name(node['cuke_name'])
50
+ l += tags(node['tags'])
51
+ l
52
+ end
53
+ def scenario_outline(l, node)
54
+ l += total(node)
55
+ l += cuke_name(node['cuke_name'])
56
+ l += tags(node['tags'])
57
+ l
58
+ end
59
+ def examples(l, node)
60
+ l += total_column_spacer
61
+ l += normal_indent(node)
62
+ l += cuke_name(node['cuke_name'])
63
+ l += tags(node['tags'])
64
+ l
65
+ end
66
+ def example_header(l, node)
67
+ l += total_column_spacer
68
+ l += blank_for_number(node)
69
+ l += example_name(node['cuke_name'])
70
+ l += tags(node['tags'])
71
+ l
72
+ end
73
+ def example(l, node)
74
+ l += total_column_spacer
75
+ l += number(node)
76
+ l += example_name(node['cuke_name'])
77
+ l += tags(node['tags'])
78
+ l
79
+ end
80
+
81
+ def build_line(node)
82
+ l = send(node.name.to_sym, "", node)
83
+ l += "\n" unless l.empty?
84
+ l
85
+ rescue NoMethodError
86
+ ""
87
+ end
88
+ end
89
+ module Text
90
+ extend Util
91
+ extend NodeFormatters
92
+
93
+
94
+ SPACER = " "
95
+ def tab_stop
96
+ @@tab_stop ||= SPACER * 2
97
+ end
98
+
99
+ def total_column_spacer
100
+ @@total_column_spacer ||= Cuporter.options[:total] ? tab_stop : ""
101
+ end
102
+
103
+ def total_column
104
+ @@total_column ||= Cuporter.options[:total] ? tab_stop * 2: ""
105
+ end
106
+
107
+ def total(node)
108
+ format_number(node, 'total') do |indent_length, value|
109
+ "[#{value}]".ljust(total_column.size, SPACER)
110
+ end
111
+ end
112
+
113
+ def number(node)
114
+ format_number(node, 'number') do |indent_length, value|
115
+ "%#{tab_stop}#{indent_length}s " % "#{value}."
116
+ end
117
+ end
118
+
119
+ def format_number(node, attr)
120
+ d = depth(node)
121
+ indent_length = d * tab_stop.size
122
+ if ( value = node[attr])
123
+ yield(indent_length, value)
124
+ else
125
+ tab_stop * d
126
+ end
127
+ end
128
+ def blank_for_number(node)
129
+ l = tab_stop * depth(node)
130
+ l += SPACER if node.document.at("[@number]")
131
+ l
132
+ end
133
+ def cuke_name(value)
134
+ value || ""
135
+ end
136
+ def fs_name(value)
137
+ return "" unless value
138
+ value.upcase
139
+ end
140
+
141
+ def tags(value)
142
+ return "" unless value
143
+ "#{tab_stop * 2}#{value}"
144
+ end
145
+
146
+ def file_path(value, line_size)
147
+ return "" unless value
148
+ "#{tab_stop}##{value}#{tab_stop * 2}".rjust(terminal_width - line_size)
149
+ end
150
+
151
+ def title(value)
152
+ value || ""
153
+ end
154
+
155
+ def normal_indent(node)
156
+ tab_stop * depth(node)
157
+ end
158
+
159
+ def example_name(value)
160
+ cuke_name value
161
+ end
162
+ extend(Cuporter::Formatters::Text)
163
+ end
164
+
165
+ end
166
+ end