cuporter 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +123 -44
- data/Rakefile +5 -5
- data/bin/cuporter +19 -11
- data/lib/cuporter.rb +15 -26
- data/lib/cuporter/cli/options.rb +95 -22
- data/lib/cuporter/document.rb +23 -0
- data/lib/cuporter/document/html_document.rb +82 -0
- data/lib/cuporter/extensions/nokogiri.rb +46 -0
- data/lib/cuporter/feature_parser.rb +38 -19
- data/lib/cuporter/filter.rb +1 -0
- data/lib/cuporter/formatters/csv.rb +69 -0
- data/lib/cuporter/formatters/text.rb +166 -0
- data/lib/cuporter/formatters/xml_to_html.xslt +315 -0
- data/lib/cuporter/node.rb +7 -83
- data/lib/cuporter/node/node_base.rb +114 -0
- data/lib/cuporter/node/numbering.rb +32 -0
- data/lib/cuporter/node/sorting.rb +27 -0
- data/lib/cuporter/node/tagged_node.rb +22 -0
- data/lib/cuporter/node/totalling.rb +19 -0
- data/lib/cuporter/node/types.rb +158 -0
- data/lib/cuporter/node_parser.rb +62 -0
- data/lib/cuporter/report/feature_report.rb +23 -0
- data/lib/cuporter/report/report_base.rb +69 -0
- data/lib/cuporter/report/tag_report.rb +27 -7
- data/lib/cuporter/report/tree_report.rb +24 -0
- data/lib/cuporter/tag_nodes_parser.rb +94 -0
- data/public/images/ajax-loader.gif +0 -0
- data/public/images/file.gif +0 -0
- data/public/images/folder-closed.gif +0 -0
- data/public/images/folder.gif +0 -0
- data/public/images/minus.gif +0 -0
- data/public/images/plus.gif +0 -0
- data/public/images/treeview-black-line.gif +0 -0
- data/public/images/treeview-black.gif +0 -0
- data/public/images/treeview-default-line.gif +0 -0
- data/public/images/treeview-default.gif +0 -0
- data/public/images/treeview-famfamfam-line.gif +0 -0
- data/public/images/treeview-famfamfam.gif +0 -0
- data/public/images/treeview-gray-line.gif +0 -0
- data/public/images/treeview-gray.gif +0 -0
- data/public/images/treeview-red-line.gif +0 -0
- data/public/images/treeview-red.gif +0 -0
- data/public/javascripts/expand-collapse.js +47 -0
- data/public/javascripts/jquery-min.js +167 -0
- data/public/javascripts/jquery.treeview.js +267 -0
- data/public/javascripts/treeview-loader.js +11 -0
- data/public/stylesheets/cuporter.css +101 -0
- data/{lib/cuporter/formatter/name_report/style.css → public/stylesheets/feature_style.css} +17 -18
- data/public/stylesheets/jquery.treeview.css +75 -0
- data/{lib/cuporter/formatter/tag_report/style.css → public/stylesheets/tag_style.css} +17 -22
- data/public/stylesheets/tree_style.css +104 -0
- metadata +52 -37
- data/lib/cuporter/example_set_node.rb +0 -15
- data/lib/cuporter/formatter/csv_text_methods.rb +0 -20
- data/lib/cuporter/formatter/cuporter.css +0 -64
- data/lib/cuporter/formatter/html_methods.rb +0 -122
- data/lib/cuporter/formatter/html_node_writer.rb +0 -104
- data/lib/cuporter/formatter/jquery-min.js +0 -154
- data/lib/cuporter/formatter/name_report/csv.rb +0 -12
- data/lib/cuporter/formatter/name_report/html.rb +0 -48
- data/lib/cuporter/formatter/name_report/html_node_writer.rb +0 -18
- data/lib/cuporter/formatter/name_report/text.rb +0 -16
- data/lib/cuporter/formatter/name_report/text_node_writer.rb +0 -16
- data/lib/cuporter/formatter/pretty_text_methods.rb +0 -22
- data/lib/cuporter/formatter/tag_report/csv.rb +0 -12
- data/lib/cuporter/formatter/tag_report/html.rb +0 -35
- data/lib/cuporter/formatter/tag_report/html_node_writer.rb +0 -19
- data/lib/cuporter/formatter/tag_report/text.rb +0 -14
- data/lib/cuporter/formatter/tag_report/text_node_writer.rb +0 -17
- data/lib/cuporter/formatter/text_methods.rb +0 -19
- data/lib/cuporter/formatter/writer.rb +0 -33
- data/lib/cuporter/name_list_parser.rb +0 -44
- data/lib/cuporter/node_numberer.rb +0 -18
- data/lib/cuporter/report/name_report.rb +0 -19
- data/lib/cuporter/report/report.rb +0 -22
- data/lib/cuporter/tag_list_node.rb +0 -54
- data/lib/cuporter/tag_list_parser.rb +0 -39
@@ -0,0 +1,32 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Node
|
4
|
+
module Numbering
|
5
|
+
class Numberer
|
6
|
+
|
7
|
+
attr_reader :total
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@total = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def number(node)
|
14
|
+
node.children.each do |child|
|
15
|
+
case child.node_name
|
16
|
+
when 'scenario', 'example'
|
17
|
+
child["number"] = (@total += 1).to_s
|
18
|
+
end
|
19
|
+
number(child)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def number_all_descendants
|
26
|
+
@numberer = Numberer.new
|
27
|
+
@numberer.number(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Node
|
4
|
+
|
5
|
+
module Sorting
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
def sort_all_descendants!
|
9
|
+
sort!
|
10
|
+
children.each {|child| child.sort_all_descendants! }
|
11
|
+
end
|
12
|
+
|
13
|
+
# see #NodeSetExtensions in extensions/nokogiri.rb
|
14
|
+
def sort!
|
15
|
+
return unless has_children?
|
16
|
+
sorted_children = children.sort
|
17
|
+
self.children = sorted_children
|
18
|
+
end
|
19
|
+
|
20
|
+
def <=>(other)
|
21
|
+
(short_cuke_name || name) <=> (other.short_cuke_name || other.name)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Node
|
4
|
+
# a node with a list of tags that can be applied to all children
|
5
|
+
module Tagged
|
6
|
+
|
7
|
+
attr_accessor :filter
|
8
|
+
|
9
|
+
def has_tags?
|
10
|
+
tags.size > 0
|
11
|
+
end
|
12
|
+
def tags
|
13
|
+
@tags ||= attributes["tags"].to_s.split(/,\s*/) || []
|
14
|
+
end
|
15
|
+
def filter_child(node, context_tags = [])
|
16
|
+
add_child(node) if @filter.pass?(context_tags | tags | node.tags)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Node
|
4
|
+
|
5
|
+
module Totalling
|
6
|
+
def total
|
7
|
+
total!
|
8
|
+
children.each {|child| child.total }
|
9
|
+
end
|
10
|
+
|
11
|
+
def total!
|
12
|
+
t = search("scenario,example").size
|
13
|
+
self["total"] = t.to_s if t > 0
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Node
|
4
|
+
module Types
|
5
|
+
class Report < NodeBase
|
6
|
+
def tag_node(tag)
|
7
|
+
at("tag[cuke_name='#{tag}']")
|
8
|
+
end
|
9
|
+
|
10
|
+
# remove leaf nodes, i.e., the scenario and scenario outline children
|
11
|
+
def defoliate!
|
12
|
+
leaves = search("feature > scenario, feature > scenario_outline")
|
13
|
+
leaves.remove
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_files!
|
17
|
+
search(:feature).each {|f| f.delete('file_path') }
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove_tags!
|
21
|
+
search("*[@tags]").each {|e| e.delete('tags') }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class FilterSummary < NodeBase
|
26
|
+
def add(filter)
|
27
|
+
self << filter_node(:all, filter.all.join(' AND ')) unless filter.all.empty?
|
28
|
+
self << filter_node(:any, filter.any.join(' OR ')) unless filter.any.empty?
|
29
|
+
self << filter_node(:none, filter.none.join(', ')) unless filter.none.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def filter_node(name, text)
|
33
|
+
fn = NodeBase.new(name.to_s, document)
|
34
|
+
fn['tags'] = text
|
35
|
+
fn
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
module FileSystemNode
|
41
|
+
def fs_name
|
42
|
+
@fs_name ||= self['fs_name']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Dir < NodeBase
|
47
|
+
include FileSystemNode
|
48
|
+
|
49
|
+
def <=>(other)
|
50
|
+
return -1 if other.is_a? File # ensure folders sort higher
|
51
|
+
fs_name <=> (other['fs_name'] || other['cuke_name'])
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class File < NodeBase
|
57
|
+
include FileSystemNode
|
58
|
+
|
59
|
+
# just total self, don't let any children total themselves. This is so
|
60
|
+
# the feature won't re-total itself when its containing file already has.
|
61
|
+
def total
|
62
|
+
total!
|
63
|
+
end
|
64
|
+
|
65
|
+
def <=>(other)
|
66
|
+
return 1 if other.is_a? Dir # ensure folders sort higher
|
67
|
+
fs_name <=> (other['fs_name'] || other['cuke_name'])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Tag < NodeBase
|
72
|
+
def feature_node(feature)
|
73
|
+
at("feature[cuke_name='#{feature[:cuke_name]}'][file_path='#{feature[:file_path]}']")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class ScenarioOutline < NodeBase
|
78
|
+
include Tagged
|
79
|
+
|
80
|
+
def example_set_node(es)
|
81
|
+
at("examples[cuke_name='#{es[:cuke_name]}']")
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class Feature < NodeBase
|
87
|
+
include Tagged
|
88
|
+
|
89
|
+
def scenario_outline_node(scenario_outline)
|
90
|
+
at("scenario_outline[cuke_name='#{scenario_outline[:cuke_name]}']")
|
91
|
+
end
|
92
|
+
|
93
|
+
def file_path
|
94
|
+
self["file_path"]
|
95
|
+
end
|
96
|
+
|
97
|
+
def file_name
|
98
|
+
file_path.split(/\//).last
|
99
|
+
end
|
100
|
+
|
101
|
+
# sort on: file path, name, substring of name after any ':'
|
102
|
+
def <=>(other)
|
103
|
+
if other.respond_to?(:file_path)
|
104
|
+
file_path <=> other.file_path
|
105
|
+
else
|
106
|
+
super(other)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def eql?(other)
|
111
|
+
if other.respond_to? :file_path
|
112
|
+
return false if file_path != other.file_path
|
113
|
+
end
|
114
|
+
super(other)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# The set of examples in a scenario outline
|
119
|
+
class Examples < NodeBase
|
120
|
+
include Tagged
|
121
|
+
|
122
|
+
# don't sort scenario outline examples
|
123
|
+
def sort!
|
124
|
+
# no op
|
125
|
+
end
|
126
|
+
|
127
|
+
# don't total at the example set level
|
128
|
+
def total
|
129
|
+
# no op
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_child(other)
|
133
|
+
cn = other['cuke_name'].dup
|
134
|
+
cn.sub!(/^\|/, '')
|
135
|
+
cn.split('|').each do |cell_text|
|
136
|
+
cell = Nokogiri::XML::Node.new("span", document)
|
137
|
+
cell.content = cell_text.strip
|
138
|
+
other << cell
|
139
|
+
end
|
140
|
+
super(other)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
Scenarios = Examples
|
144
|
+
|
145
|
+
# Leaf Nodes: won't have children
|
146
|
+
class Scenario < NodeBase
|
147
|
+
include Tagged
|
148
|
+
end
|
149
|
+
|
150
|
+
class Example < NodeBase
|
151
|
+
end
|
152
|
+
|
153
|
+
class ExampleHeader < NodeBase
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
|
3
|
+
module Cuporter
|
4
|
+
class NodeParser < FeatureParser
|
5
|
+
|
6
|
+
# ++sub_expression++ is the paren group in the regex, dereferenced with $1 in the caller
|
7
|
+
def new_feature_node(sub_expression, file)
|
8
|
+
f = Node.new_node(:Feature, @doc, :cuke_name => sub_expression, :tags => @current_tags, :file_path => file)
|
9
|
+
f.filter = @filter
|
10
|
+
f
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle_scenario_line(sub_expression)
|
14
|
+
if @filter.pass?(@current_tags | @feature.tags)
|
15
|
+
@feature.add_child(Node.new_node(:Scenario, @doc, :cuke_name => sub_expression, :tags => @current_tags))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def new_scenario_outline_node(sub_expression)
|
20
|
+
so = Node.new_node(:ScenarioOutline, @doc, :cuke_name => sub_expression, :tags => @current_tags)
|
21
|
+
so.filter = @filter
|
22
|
+
so
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_example_set_line
|
26
|
+
if @filter.pass?(@feature.tags | @scenario_outline.tags | @example_set.tags)
|
27
|
+
@scenario_outline.add_child @example_set
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def new_example_set_node(sub_expression)
|
32
|
+
es = Node.new_node(:Examples, @doc, :cuke_name => sub_expression, :tags => @current_tags)
|
33
|
+
es.filter = @filter
|
34
|
+
es
|
35
|
+
end
|
36
|
+
|
37
|
+
def new_example_line(sub_expression)
|
38
|
+
example_type = :ExampleHeader
|
39
|
+
# if the example set has a child already, then it must be the header
|
40
|
+
example_type = :Example if @example_set.has_children?
|
41
|
+
@example_set.add_child(Node.new_node(example_type, @doc, :cuke_name => sub_expression))
|
42
|
+
end
|
43
|
+
|
44
|
+
def close_scenario_outline
|
45
|
+
if @scenario_outline
|
46
|
+
if @example_set
|
47
|
+
handle_example_set_line
|
48
|
+
@example_set = nil
|
49
|
+
end
|
50
|
+
@feature.add_child(@scenario_outline) if @scenario_outline.has_children?
|
51
|
+
@scenario_outline = nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(file, doc, filter)
|
56
|
+
super(file)
|
57
|
+
@filter = filter
|
58
|
+
@doc = doc
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
class FeatureReport < ReportBase
|
4
|
+
|
5
|
+
def report_node
|
6
|
+
unless @report
|
7
|
+
@report = Cuporter::Node.new_node(:Report, @doc, :title => title, :view => view)
|
8
|
+
files.each do |file|
|
9
|
+
feature = FeatureParser.node(file, @doc, @filter, root_dir)
|
10
|
+
if feature && feature.has_children?
|
11
|
+
@report.add_child feature
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@report
|
16
|
+
end
|
17
|
+
|
18
|
+
def title
|
19
|
+
@title || "Cucumber Features, List View"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
class ReportBase
|
4
|
+
|
5
|
+
attr_reader :filter, :doc, :root_dir, :view
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@input_file_pattern = options[:input_file_pattern]
|
9
|
+
@doc = Cuporter::Document.new_xml
|
10
|
+
@view = options[:report]
|
11
|
+
@root_dir = options[:root_dir]
|
12
|
+
@filter = Filter.new(options[:filter_args])
|
13
|
+
@title = options[:title]
|
14
|
+
@sort = options[:sort]
|
15
|
+
@total = options[:total]
|
16
|
+
@number = options[:number]
|
17
|
+
@leaves = options[:leaves]
|
18
|
+
@show_files = options[:show_files]
|
19
|
+
@show_tags = options[:show_tags]
|
20
|
+
end
|
21
|
+
|
22
|
+
def no_leaves?
|
23
|
+
!@leaves
|
24
|
+
end
|
25
|
+
|
26
|
+
def sort?
|
27
|
+
@sort
|
28
|
+
end
|
29
|
+
|
30
|
+
def total?
|
31
|
+
@total
|
32
|
+
end
|
33
|
+
|
34
|
+
def number?
|
35
|
+
@number
|
36
|
+
end
|
37
|
+
|
38
|
+
def show_files?
|
39
|
+
@show_files
|
40
|
+
end
|
41
|
+
|
42
|
+
def show_tags?
|
43
|
+
@show_tags
|
44
|
+
end
|
45
|
+
|
46
|
+
def files
|
47
|
+
Dir[@input_file_pattern].collect {|f| File.expand_path f}
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.create(options)
|
51
|
+
klass = Cuporter.const_get("#{options[:report].downcase}Report".to_class_name)
|
52
|
+
klass.new(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def build
|
56
|
+
report_node.sort_all_descendants! if sort?
|
57
|
+
report_node.number_all_descendants if number?
|
58
|
+
report_node.total if total?
|
59
|
+
report_node.defoliate! if no_leaves?
|
60
|
+
report_node.remove_files! unless show_files?
|
61
|
+
report_node.remove_tags! unless show_tags?
|
62
|
+
|
63
|
+
doc.add_filter_summary(@filter)
|
64
|
+
doc.add_report report_node
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -1,15 +1,35 @@
|
|
1
1
|
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
2
|
module Cuporter
|
3
|
-
class TagReport <
|
3
|
+
class TagReport < ReportBase
|
4
4
|
|
5
|
-
def
|
6
|
-
tags = TagListNode.new("report",[])
|
5
|
+
def build_report_node
|
7
6
|
files.each do |file|
|
8
|
-
|
9
|
-
tags.merge_tag_nodes(feature) if feature
|
7
|
+
FeatureParser.tag_nodes(file, report, @filter, root_dir)
|
10
8
|
end
|
11
|
-
|
12
|
-
|
9
|
+
end
|
10
|
+
|
11
|
+
def report
|
12
|
+
@report ||= begin
|
13
|
+
r = Cuporter::Node.new_node(:Report, doc, :title => title, :view => view)
|
14
|
+
doc.add_filter_summary(@filter)
|
15
|
+
doc.add_report r
|
16
|
+
r
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def title
|
21
|
+
@title || "Cucumber Tags"
|
22
|
+
end
|
23
|
+
|
24
|
+
def build
|
25
|
+
build_report_node
|
26
|
+
report.sort_all_descendants! if sort?
|
27
|
+
report.search(:tag).each {|f| f.number_all_descendants } if number?
|
28
|
+
report.total if total?
|
29
|
+
report.defoliate! if no_leaves?
|
30
|
+
report.remove_files! unless show_files?
|
31
|
+
report.remove_tags! unless show_tags?
|
32
|
+
self
|
13
33
|
end
|
14
34
|
|
15
35
|
end
|