cuporter 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,13 +1,24 @@
1
1
  h1. Cuporter
2
2
 
3
- Scrapes your feature files and shows scenarios per tag, with their context. Formats HTML, CSV, and Pretty Text.
3
+ Scrapes your feature files and shows 2 possible reports:
4
4
 
5
- Consider this a stop-gap until we get this functionality in a proper cucumber formatter.
5
+ # scenarios and examples per tag
6
+ ** in Feature and Scenario Outline context, as appropriate
7
+ ** sorted alphbetically
8
+ ** optionally numbered, counting scenarios and outline examples per tag
9
+ # feature, scenario and example names
10
+ ** may be filtered by tags, using same CLI syntax as Cucumber
11
+ ** sorted alphbetically
12
+ ** optionally numbered, using 1 count of all scenarios and outline examples for the report
13
+
14
+ Either report may be formatted in HTML, CSV, or Pretty Text.
6
15
 
7
- In version 0.2.0 and later you can number the Scenarios and Example rows with the @-n@ or @--numbers@ option.
16
+ * HTML reports do jQuery expand-collapse, with acklowedgment due to the Cucumber HTML formatter.
17
+
18
+ Consider this a stop-gap until we get this functionality in a proper cucumber formatter.
8
19
 
9
20
  ---------
10
- h3. Example Output: numbered pretty text
21
+ h3. Example Tag Report Output: numbered pretty text
11
22
 
12
23
  <pre>
13
24
  @failing
@@ -63,28 +74,44 @@ h4. help
63
74
  $ ./bin/cuporter -h
64
75
 
65
76
  Usage: cuporter [options]
66
-
77
+
67
78
  -i, --in DIR directory of *.feature files
68
- Default: features/**/*.feature
69
-
70
- --input-file FILE full file name with extension: 'path/to/file.feature'
71
-
79
+ Default: features/**/*.feature
80
+
81
+ -I, --input-file FILE full file name with extension: 'path/to/file.feature'
82
+
72
83
  -o, --out FILE Output file path
73
-
84
+
74
85
  -f, --format [pretty|html|csv] Output format
75
- Default: pretty text
86
+ Default: pretty text
76
87
 
77
88
  -n, --numbers number scenarios and examples
89
+
90
+ -t, --tags TAG_EXPRESSION Filter on tags for name report.
91
+ TAG_EXPRESSION rules: see '$ cucumber --help' and http://github.com/aslakhellesoy/cucumber/wiki/Tags
92
+
93
+ -r, --report [tag|name] type of report
94
+ Default: tag
95
+
96
+ -T, --title STRING title of name report
97
+ Default: 'Cucumber Scenario Inventory'
98
+
78
99
  </pre>
79
100
 
80
101
  h4. run script directly
81
102
 
82
103
  <pre>
83
- $ ./bin/cuporter -i fixtures/self_text # pretty-print demo report to stdout
104
+ # pretty-print demo report to stdout
105
+ $ ./bin/cuporter -i fixtures/self_text
106
+
107
+ # default input features/**/*.feature to named output file
108
+ $ ./bin/cuporter -f html -o feature_tag_report.html
84
109
 
85
- $ ./bin/cuporter -f html -o feature_tag_report.html # default input features/**/*.feature to named output file
110
+ # same, but number the scenarios and example rows
111
+ $ ./bin/cuporter -f html --numbers -o feature_tag_report.html
86
112
 
87
- $ ./bin/cuporter -f html --numbers -o feature_tag_report.html # same, but number the scenarios and example rows
113
+ # filtered html name report, with non-default title
114
+ $ ./bin/cuporter -f html -o active_customer.html -r name -n -T "Active Customer Test Inventory" -t ~@wip -t @customer,@client
88
115
  </pre>
89
116
 
90
117
  h4. run via rake
data/Rakefile CHANGED
@@ -56,14 +56,14 @@ namespace :cuporter do
56
56
 
57
57
  spec = Gem::Specification.new do |s|
58
58
  s.name = 'cuporter'
59
- s.version = '0.2.7'
59
+ s.version = '0.2.8'
60
60
  s.rubyforge_project = s.name
61
61
 
62
62
  s.platform = Gem::Platform::RUBY
63
63
  s.has_rdoc = true
64
64
  s.extra_rdoc_files = XTRA_RDOC
65
65
  s.rdoc_options += RDOC_OPTS
66
- s.summary = "Scrapes Cucumber *.feature files to build report on tag usage"
66
+ s.summary = "Scrapes Cucumber *.feature files to build reports on tag usage and test inventory"
67
67
  s.description = s.summary
68
68
  s.author = "Tim Camper"
69
69
  s.email = 'twcamper@thoughtworks.com'
data/bin/cuporter CHANGED
@@ -3,9 +3,15 @@
3
3
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
4
4
  require 'cuporter'
5
5
 
6
- tag_report = Cuporter::TagReport.new(Cuporter::Options[:input_file] || Cuporter::Options[:input_dir])
6
+ filter_args = Cuporter::CLI::FilterArgsBuilder.new(Cuporter::CLI::Options[:tags]).args
7
7
 
8
- formatter = Cuporter::Formatters.const_get(Cuporter::Options[:format])
9
- formatter.new(tag_report.scenarios_per_tag,
10
- Cuporter::Options[:output],
11
- Cuporter::Options[:numbers]).write
8
+ report = Cuporter::Report.create(Cuporter::CLI::Options[:report],
9
+ Cuporter::CLI::Options[:input_file] || Cuporter::CLI::Options[:input_dir],
10
+ Cuporter::CLI::Options[:numbers],
11
+ filter_args,
12
+ Cuporter::CLI::Options[:title])
13
+
14
+ formatter = Cuporter::Formatter::Writer.create(Cuporter::CLI::Options[:format],
15
+ report,
16
+ Cuporter::CLI::Options[:output])
17
+ formatter.write
@@ -0,0 +1,37 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+
3
+ module Cuporter
4
+ module CLI
5
+ class FilterArgsBuilder
6
+ def initialize(cli_tags = [])
7
+ @cli_tags = cli_tags
8
+ @all = []
9
+ @any = []
10
+ @none = []
11
+ @args = {}
12
+ filter_args
13
+ end
14
+
15
+ def filter_args
16
+ @cli_tags.each do |expression|
17
+ case expression
18
+ when /^~@/
19
+ @none << expression.sub(/~/,'')
20
+ when /,/
21
+ @any |= expression.split(',')
22
+ else
23
+ @all << expression
24
+ end
25
+ end
26
+ end
27
+
28
+ def args
29
+ @args[:any] = @any unless @any.empty?
30
+ @args[:all] = @all unless @all.empty?
31
+ @args[:none] = @none unless @none.empty?
32
+ @args
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -3,47 +3,67 @@ require 'optparse'
3
3
  require 'fileutils'
4
4
 
5
5
  module Cuporter
6
- class Options
6
+ module CLI
7
+ class Options
7
8
 
8
- def self.[](key)
9
- self.options[key]
10
- end
9
+ def self.[](key)
10
+ self.options[key]
11
+ end
11
12
 
12
- def self.options
13
- self.parse unless @options
14
- @options
15
- end
13
+ def self.options
14
+ self.parse unless @options
15
+ @options
16
+ end
17
+
18
+ def self.parse
19
+ @options = {}
20
+ OptionParser.new(ARGV.dup) do |opts|
21
+ opts.banner = "Usage: cuporter [options]\n\n"
22
+
23
+ @options[:input_dir] = "features/**/*.feature"
24
+ opts.on("-i", "--in DIR", "directory of *.feature files\n\t\t\t\t\tDefault: features/**/*.feature\n\n") do |i|
25
+ @options[:input_dir] = "#{i}/**/*.feature"
26
+ end
27
+
28
+ opts.on("-I", "--input-file FILE", "full file name with extension: 'path/to/file.feature'\n\n") do |file|
29
+ @options[:input_file] = file
30
+ end
31
+ opts.on("-o", "--out FILE", "Output file path\n\n") do |o|
32
+ full_path = File.expand_path(o)
33
+ path = full_path.split(File::SEPARATOR)
34
+ file = path.pop
35
+ FileUtils.makedirs(path.join(File::SEPARATOR))
36
+
37
+ @options[:output] = full_path
38
+ end
39
+ @options[:format] = "text"
40
+ opts.on("-f", "--format [pretty|html|csv]", "Output format\n\t\t\t\t\tDefault: pretty text\n\n") do |f|
41
+ @options[:format] = f
42
+ end
43
+
44
+ opts.on("-n", "--numbers", "number scenarios and examples\n\n") do |n|
45
+ @options[:numbers] = n
46
+ end
47
+
48
+ @options[:tags] = []
49
+ opts.on("-t", "--tags TAG_EXPRESSION", "Filter on tags for name report.\n\t\t\t\t\tTAG_EXPRESSION rules: see '$ cucumber --help' and http://github.com/aslakhellesoy/cucumber/wiki/Tags\n\n") do |t|
50
+ @options[:tags] << t
51
+ end
52
+
53
+ @options[:report] = "tag"
54
+ opts.on("-r", "--report [tag|name]", "type of report\n\t\t\t\t\tDefault: tag\n\n") do |r|
55
+ @options[:report] = r
56
+ end
57
+
58
+ @options[:title] = "Cucumber Scenario Inventory"
59
+ opts.on("-T", "--title STRING", "title of name report\n\t\t\t\t\tDefault: 'Cucumber Scenario Inventory'\n\n") do |title|
60
+ @options[:title] = title
61
+ end
16
62
 
17
- def self.parse
18
- @options = {}
19
- OptionParser.new(ARGV.dup) do |opts|
20
- opts.banner = "Usage: cuporter [options]\n\n"
21
-
22
- opts.on("-i", "--in DIR", "directory of *.feature files\n\t\t\t\t\tDefault: features/**/*.feature\n\n") do |i|
23
- @options[:input_dir] = "#{i}/**/*.feature"
24
- end
25
- opts.on("--input-file FILE", "full file name with extension: 'path/to/file.feature'\n\n") do |file|
26
- @options[:input_file] = file
27
- end
28
- opts.on("-o", "--out FILE", "Output file path\n\n") do |o|
29
- full_path = File.expand_path(o)
30
- path = full_path.split(File::SEPARATOR)
31
- file = path.pop
32
- FileUtils.makedirs(path.join(File::SEPARATOR))
33
-
34
- @options[:output] = full_path
35
- end
36
- opts.on("-f", "--format [pretty|html|csv]", "Output format\n\t\t\t\t\tDefault: pretty text\n\n") do |f|
37
- @options[:format] = f.downcase.to_class_name
38
- end
39
- @options[:format] ||= :Text
40
-
41
- opts.on("-n", "--numbers", "number scenarios and examples") do |n|
42
- @options[:numbers] = n
43
- end
44
- end.parse!
63
+ end.parse!
45
64
 
46
65
 
66
+ end
47
67
  end
48
68
  end
49
69
  end
@@ -1,7 +1,7 @@
1
1
  # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
2
 
3
3
  module Cuporter
4
- class FeatureParser
4
+ module FeatureParser
5
5
  FEATURE_LINE = /^\s*(Feature:[^#]+)/
6
6
  TAG_LINE = /^\s*(@\w.+)/
7
7
  SCENARIO_LINE = /^\s*(Scenario:[^#]+)$/
@@ -10,65 +10,13 @@ module Cuporter
10
10
  EXAMPLE_SET_LINE = /^\s*(Examples:[^#]*)$/
11
11
  EXAMPLE_LINE = /^\s*(\|.*\|)\s*$/
12
12
 
13
- def initialize(file)
14
- @file = file
15
- @current_tags = []
13
+ def self.tag_list(file)
14
+ TagList.new(file).parse_feature
16
15
  end
17
16
 
18
- def self.parse(file)
19
- self.new(file).parse
17
+ def self.name_list(file, filter)
18
+ NameList.new(file, filter).parse_feature
20
19
  end
21
20
 
22
- def parse
23
- lines = File.read(@file).split(/\n/)
24
-
25
- lines.each_with_index do |line, i|
26
- case line
27
- when TAG_LINE
28
- # may be more than one tag line
29
- @current_tags |= $1.strip.split(/\s+/)
30
- when FEATURE_LINE
31
- @feature = TagListNode.new($1, @current_tags)
32
- @feature.file = @file.sub(/^.*features\//,"features/")
33
- @current_tags = []
34
- when SCENARIO_LINE
35
- # How do we know when we have read all the lines from a "Scenario Outline:"?
36
- # One way is when we encounter a "Scenario:"
37
- close_scenario_outline
38
-
39
- @feature.add_to_tag_nodes(TagListNode.new($1, @current_tags))
40
- @current_tags = []
41
- when SCENARIO_OUTLINE_LINE
42
- # ... another is when we hit a subsequent "Scenario Outline:"
43
- close_scenario_outline
44
-
45
- @scenario_outline = TagListNode.new($1, @current_tags)
46
- @current_tags = []
47
- when EXAMPLE_SET_LINE, SCENARIO_SET_LINE
48
- @scenario_outline.add_to_tag_nodes(@example_set) if @example_set
49
-
50
- @example_set = ExampleSetNode.new($1, @feature.tags | @current_tags)
51
- @current_tags = []
52
- when @example_set && EXAMPLE_LINE
53
- @example_set.add_child(Node.new($1))
54
- end
55
- end
56
-
57
- # EOF is the final way that we know we are finished with a "Scenario Outline"
58
- close_scenario_outline
59
- return @feature
60
- end
61
-
62
- def close_scenario_outline
63
- if @scenario_outline
64
- if @example_set
65
- @scenario_outline.add_to_tag_nodes(@example_set) if @example_set
66
- @example_set = nil
67
- end
68
- @feature.merge(@scenario_outline)
69
- @scenario_outline = nil
70
- end
71
- end
72
21
  end
73
-
74
22
  end
@@ -0,0 +1,52 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ class Filter
4
+
5
+ attr_reader :all, :any, :none
6
+
7
+ def initialize(args = {})
8
+ @all = to_tag_array(args[:all]) # logical AND
9
+ @any = to_tag_array(args[:any]) # logical OR
10
+ @none = to_tag_array(args[:none])# logical NOT
11
+ end
12
+
13
+ def pass?(other_tags)
14
+ pass = true
15
+
16
+ # Logical AND: are all of the tags in :all in the tests' tags?
17
+ unless all.empty?
18
+ pass = false unless (other_tags & all).size == all.size
19
+ end
20
+
21
+ # Logical OR: are any of the tests' tags in :any?
22
+ unless any.empty?
23
+ pass = false if (other_tags & any).empty?
24
+ end
25
+
26
+ unless none.empty?
27
+ pass = false if !(other_tags & none).empty?
28
+ end
29
+ pass
30
+ end
31
+
32
+ def empty?
33
+ all.empty? && any.empty? && none.empty?
34
+ end
35
+
36
+ private
37
+
38
+ def to_tag_array(tag_input)
39
+ case tag_input
40
+ when Array
41
+ tag_input
42
+ when String
43
+ tag_input.split(" ")
44
+ when Numeric, Symbol
45
+ [tag_input.to_s]
46
+ else
47
+ []
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -1,6 +1,6 @@
1
1
  # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
2
  module Cuporter
3
- module Formatters
3
+ module Formatter
4
4
  class Csv < Writer
5
5
  include TextMethods
6
6
 
@@ -7,14 +7,35 @@ body {
7
7
  padding-left: 2em;
8
8
  padding-bottom: 0.3em;
9
9
  padding-top: 0.05em;
10
- height: 6em;
11
10
  background-color: #24A6C1;
12
11
  color: white;
12
+ height: 7em;
13
+ }
14
+ .cuporter_header #label {
15
+ padding-right: 2em;
16
+ float: left;
17
+ }
18
+ #filter_summary {
19
+ width: 60em;
20
+ float: right;
21
+ padding-top: 1.2em;
13
22
  }
14
23
 
15
- #expand-collapse {
24
+ #summary {
16
25
  position: absolute;
17
26
  right: 1em;
27
+ text-align: right;
28
+ top: 1.2em;
29
+ }
30
+
31
+ #summary p, #filter_summary p {
32
+ margin: 0px 0px 0px 2px;
33
+ }
34
+
35
+ .tag_report #expand-collapse {
36
+ position: absolute;
37
+ right: 1em;
38
+ text-align: right;
18
39
  top: 0.5em;
19
40
  }
20
41
  ul {
@@ -78,6 +99,10 @@ ul {
78
99
  left: 4.5em;
79
100
  font-weight: bold;
80
101
  }
102
+
103
+ .name_report .number {
104
+ left: 4em;
105
+ }
81
106
  .file {
82
107
  float: right;
83
108
  padding-right: 0.5em;
@@ -3,8 +3,8 @@ require 'rubygems'
3
3
  require 'erb'
4
4
 
5
5
  module Cuporter
6
- module Formatters
7
- class Html < Writer
6
+ module Formatter
7
+ module HtmlMethods
8
8
 
9
9
  def inline_style
10
10
  File.read(File.dirname(__FILE__) + "/cuporter.css")
@@ -15,7 +15,7 @@ module Cuporter
15
15
  end
16
16
 
17
17
  def rhtml
18
- ERB.new(RHTML)
18
+ ERB.new(self.class::RHTML)
19
19
  end
20
20
 
21
21
  def write
@@ -61,45 +61,23 @@ module Cuporter
61
61
  $(FEATURES).siblings().show();
62
62
  });
63
63
 
64
+ $("#expand_features").css('cursor', 'pointer');
65
+ $("#expand_features").click(function() {
66
+ $(FEATURES).siblings().show();
67
+ });
68
+
69
+ $("#collapse_features").css('cursor', 'pointer');
70
+ $("#collapse_features").click(function() {
71
+ $(FEATURES).siblings().hide();
72
+ });
73
+
64
74
  // load page with features collapsed
65
- $("#expand_tags").click();
75
+ $("#expand_tags, #collapse_features").click();
66
76
  })
67
77
 
68
78
  EOF
69
79
  end
70
80
 
71
- RHTML = %{
72
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
73
- <html xmlns="http://www.w3.org/1999/xhtml">
74
- <head>
75
- <title>Cucumber Tags</title>
76
- <style type="text/css">
77
- <%= inline_style%>
78
- </style>
79
- <script type="text/javascript">
80
- <%= inline_jquery%>
81
- <%= inline_js_content%>
82
- </script>
83
- </head>
84
- <body>
85
- <div class="cuporter_header">
86
- <div id="label">
87
- <h1>Cucumber Tags</h1>
88
- </div>
89
- <div id="expand-collapse">
90
- <p id="expand_tags">Expand Tags</p>
91
- <p id="expand_all">Expand All</p>
92
- <p id="collapser">Collapse All</p>
93
- </div>
94
- </div>
95
- <ul class="tag_list">
96
- <%= HtmlNodeWriter.new.write_nodes(@report, @number_scenarios)%>
97
- </ul>
98
- </body>
99
- </html>
100
- }
101
-
102
-
103
81
  end
104
82
  end
105
83
  end
@@ -3,7 +3,7 @@ require 'rubygems'
3
3
  require 'builder'
4
4
 
5
5
  module Cuporter
6
- module Formatters
6
+ module Formatter
7
7
  class HtmlNodeWriter
8
8
 
9
9
  attr_reader :builder
@@ -13,8 +13,7 @@ module Cuporter
13
13
  end
14
14
 
15
15
  def write_nodes(report, number_scenarios)
16
- report.children.each do |tag_node|
17
- tag_node.number_all_descendants if number_scenarios
16
+ report.report_node.children.each do |tag_node|
18
17
  write_node(tag_node)
19
18
  end
20
19
  builder
@@ -0,0 +1,62 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ require 'rubygems'
3
+ require 'erb'
4
+
5
+ module Cuporter
6
+ module Formatter
7
+ class NameReportHtml < Writer
8
+ include HtmlMethods
9
+
10
+ def title
11
+ @report.title
12
+ end
13
+
14
+ def filter_summary
15
+ return if @report.filter.empty?
16
+ builder = Builder::XmlMarkup.new
17
+ builder.div(:id => :filter_summary) do |div|
18
+ div.p("Filtering:")
19
+ div.p("Include: #{@report.filter.all.join(' AND ')}") unless @report.filter.all.empty?
20
+ div.p("Include: #{@report.filter.any.join(' OR ')}") unless @report.filter.any.empty?
21
+ div.p("Exclude: #{@report.filter.none.join(', ')}") unless @report.filter.none.empty?
22
+ end
23
+ end
24
+
25
+ RHTML = %{
26
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
27
+ <html xmlns="http://www.w3.org/1999/xhtml">
28
+ <head>
29
+ <title><%= title %></title>
30
+ <style type="text/css">
31
+ <%= inline_style%>
32
+ </style>
33
+ <script type="text/javascript">
34
+ <%= inline_jquery%>
35
+ <%= inline_js_content%>
36
+ </script>
37
+ </head>
38
+ <body class="name_report">
39
+ <div class="cuporter_header">
40
+ <div id="label">
41
+ <h1><%= title %></h1>
42
+ </div>
43
+ <%= filter_summary %>
44
+ <div id="summary">
45
+ <p id="total"><%= @report.report_node.total%> Scenarios </p>
46
+ <div id="expand-collapse">
47
+ <p id="expand_features">Expand All</p>
48
+ <p id="collapse_features">Collapse All</p>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ <ul class="tag_list, name_report">
53
+ <%= HtmlNodeWriter.new.write_nodes(@report, @number_scenarios)%>
54
+ </ul>
55
+ </body>
56
+ </html>
57
+ }
58
+
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ require 'rubygems'
3
+ require 'erb'
4
+
5
+ module Cuporter
6
+ module Formatter
7
+ class TagReportHtml < Writer
8
+ include HtmlMethods
9
+
10
+ def title
11
+ "Cucumber Tags"
12
+ end
13
+
14
+ RHTML = %{
15
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
16
+ <html xmlns="http://www.w3.org/1999/xhtml">
17
+ <head>
18
+ <title><%= title %></title>
19
+ <style type="text/css">
20
+ <%= inline_style%>
21
+ </style>
22
+ <script type="text/javascript">
23
+ <%= inline_jquery%>
24
+ <%= inline_js_content%>
25
+ </script>
26
+ </head>
27
+ <body class="tag_report">
28
+ <div class="cuporter_header">
29
+ <div id="label">
30
+ <h1><%= title %></h1>
31
+ </div>
32
+ <div id="expand-collapse">
33
+ <p id="expand_tags">Expand Tags</p>
34
+ <p id="expand_all">Expand All</p>
35
+ <p id="collapser">Collapse All</p>
36
+ </div>
37
+ </div>
38
+ <ul class="tag_list">
39
+ <%= HtmlNodeWriter.new.write_nodes(@report, @number_scenarios)%>
40
+ </ul>
41
+ </body>
42
+ </html>
43
+ }
44
+
45
+
46
+ end
47
+ end
48
+ end
@@ -1,6 +1,6 @@
1
1
  # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
2
  module Cuporter
3
- module Formatters
3
+ module Formatter
4
4
  class Text < Writer
5
5
  include TextMethods
6
6
 
@@ -1,10 +1,10 @@
1
1
  # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
2
  module Cuporter
3
- module Formatters
3
+ module Formatter
4
4
  module TextMethods
5
5
 
6
6
  def write
7
- @report.children.each do |tag_node|
7
+ @report.report_node.children.each do |tag_node|
8
8
  tag_node.number_all_descendants if @number_scenarios
9
9
  write_node(tag_node, 0)
10
10
  end
@@ -0,0 +1,29 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+
3
+ module Cuporter
4
+ module Formatter
5
+ class Writer
6
+
7
+ def initialize(report, output)
8
+ @report = report
9
+ @output = output ? File.open(output, "w") : STDOUT
10
+ end
11
+
12
+ def self.create(format, report, output)
13
+ klass = writer_class(format, report.class.name.split(/::/).last)
14
+ klass.new(report, output)
15
+ end
16
+
17
+ def self.writer_class(format, report_class)
18
+ case format
19
+ when /text|pretty/i
20
+ Cuporter::Formatter::Text
21
+ when /csv/i
22
+ Cuporter::Formatter::Csv
23
+ when /html/i
24
+ Cuporter::Formatter.const_get("#{report_class}Html")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+
3
+ module Cuporter
4
+ class NameList
5
+
6
+ def initialize(file, filter)
7
+ @file = file
8
+ @current_tags = []
9
+ @filter = filter
10
+ end
11
+
12
+ def parse_feature
13
+ lines = File.read(@file).split(/\n/)
14
+
15
+ lines.each do |line|
16
+ case line
17
+ when FeatureParser::TAG_LINE
18
+ # may be more than one tag line
19
+ @current_tags |= $1.strip.split(/\s+/)
20
+ when FeatureParser::FEATURE_LINE
21
+ @feature = TagListNode.new($1, @current_tags, @filter)
22
+ @feature.file = @file.sub(/^.*features\//,"features/")
23
+ @current_tags = []
24
+ when FeatureParser::SCENARIO_LINE
25
+ # How do we know when we have read all the lines from a "Scenario Outline:"?
26
+ # One way is when we encounter a "Scenario:"
27
+ close_scenario_outline
28
+
29
+ @feature.filter_child(Node.new($1), @current_tags)
30
+ @current_tags = []
31
+ when FeatureParser::SCENARIO_OUTLINE_LINE
32
+ # ... another is when we hit a subsequent "Scenario Outline:"
33
+ close_scenario_outline
34
+
35
+ @scenario_outline = TagListNode.new($1, @current_tags, @filter)
36
+ @current_tags = []
37
+ when FeatureParser::EXAMPLE_SET_LINE, FeatureParser::SCENARIO_SET_LINE
38
+ @scenario_outline.filter_child(@example_set, @example_set.tags) if @example_set
39
+
40
+ @example_set = ExampleSetNode.new($1, @feature.tags | @current_tags, @filter)
41
+ @current_tags = []
42
+ when @example_set && FeatureParser::EXAMPLE_LINE
43
+ @example_set.add_child(Node.new($1))
44
+ end
45
+ end
46
+
47
+ # EOF is the final way that we know we are finished with a "Scenario Outline"
48
+ close_scenario_outline
49
+ return @feature
50
+ end
51
+
52
+ def close_scenario_outline
53
+ if @scenario_outline
54
+ if @example_set
55
+ @scenario_outline.filter_child(@example_set, @example_set.tags) if @example_set
56
+ @example_set = nil
57
+ end
58
+ @feature.add_child(@scenario_outline) if @scenario_outline.has_children?
59
+ @scenario_outline = nil
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ class NameReport < Report
4
+
5
+ def report_node
6
+ names = TagListNode.new("report", @filter)
7
+ files.each do |file|
8
+ feature = FeatureParser.name_list(file, @filter)
9
+ if feature && feature.has_children?
10
+ names.add_child(feature)
11
+ end
12
+ end
13
+ names.sort_all_descendants!
14
+ names.number_all_descendants if number_scenarios
15
+ names
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ class Report
4
+
5
+ attr_reader :number_scenarios, :filter, :title
6
+
7
+ def initialize(input_file_pattern, number_scenarios, filter_args = nil, title = nil)
8
+ @input_file_pattern = input_file_pattern
9
+ @number_scenarios = number_scenarios
10
+ @filter = Filter.new(filter_args || {})
11
+ @title = title
12
+ end
13
+
14
+ def files
15
+ Dir[@input_file_pattern].collect {|f| File.expand_path f}
16
+ end
17
+
18
+ def self.create(type, input_file_pattern, number_scenarios, filter_args = nil, title = nil)
19
+ klass = Cuporter.const_get("#{type.downcase}Report".to_class_name)
20
+ klass.new(input_file_pattern, number_scenarios, filter_args, title)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+ module Cuporter
3
+ class TagReport < Report
4
+
5
+ def report_node
6
+ tags = TagListNode.new("report",[])
7
+ files.each do |file|
8
+ feature = FeatureParser.tag_list(file)
9
+ tags.merge_tag_nodes(feature) if feature
10
+ end
11
+ tags.sort_all_descendants!
12
+ tags.children.each { |child| child.number_all_descendants } if number_scenarios
13
+ tags
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+ # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
+
3
+ module Cuporter
4
+ class TagList
5
+
6
+ def initialize(file)
7
+ @file = file
8
+ @current_tags = []
9
+ end
10
+
11
+ def parse_feature
12
+ lines = File.read(@file).split(/\n/)
13
+
14
+ lines.each do |line|
15
+ case line
16
+ when FeatureParser::TAG_LINE
17
+ # may be more than one tag line
18
+ @current_tags |= $1.strip.split(/\s+/)
19
+ when FeatureParser::FEATURE_LINE
20
+ @feature = TagListNode.new($1, @current_tags)
21
+ @feature.file = @file.sub(/^.*features\//,"features/")
22
+ @current_tags = []
23
+ when FeatureParser::SCENARIO_LINE
24
+ # How do we know when we have read all the lines from a "Scenario Outline:"?
25
+ # One way is when we encounter a "Scenario:"
26
+ close_scenario_outline
27
+
28
+ @feature.add_to_tag_nodes(TagListNode.new($1, @current_tags))
29
+ @current_tags = []
30
+ when FeatureParser::SCENARIO_OUTLINE_LINE
31
+ # ... another is when we hit a subsequent "Scenario Outline:"
32
+ close_scenario_outline
33
+
34
+ @scenario_outline = TagListNode.new($1, @current_tags)
35
+ @current_tags = []
36
+ when FeatureParser::EXAMPLE_SET_LINE, FeatureParser::SCENARIO_SET_LINE
37
+ @scenario_outline.add_to_tag_nodes(@example_set) if @example_set
38
+
39
+ @example_set = ExampleSetNode.new($1, @feature.tags | @current_tags)
40
+ @current_tags = []
41
+ when @example_set && FeatureParser::EXAMPLE_LINE
42
+ @example_set.add_child(Node.new($1))
43
+ end
44
+ end
45
+
46
+ # EOF is the final way that we know we are finished with a "Scenario Outline"
47
+ close_scenario_outline
48
+ return @feature
49
+ end
50
+
51
+ def close_scenario_outline
52
+ if @scenario_outline
53
+ if @example_set
54
+ @scenario_outline.add_to_tag_nodes(@example_set) if @example_set
55
+ @example_set = nil
56
+ end
57
+ @feature.merge_tag_nodes(@scenario_outline)
58
+ @scenario_outline = nil
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -5,15 +5,20 @@ module Cuporter
5
5
 
6
6
  attr_reader :tags
7
7
 
8
- def initialize(name, tags)
8
+ def initialize(name, tags, filter = {})
9
9
  super(name)
10
10
  @tags = tags
11
+ @filter = filter
11
12
  end
12
13
 
13
14
  def has_tags?
14
15
  @tags.size > 0
15
16
  end
16
17
 
18
+ def filter_child(node, node_tags)
19
+ add_child(node) if @filter.pass?(tags | node_tags)
20
+ end
21
+
17
22
  def add_to_tag_nodes(node)
18
23
  (tags | node.tags).each do |tag|
19
24
  tag_node = find_or_create_child(tag)
@@ -25,14 +30,14 @@ module Cuporter
25
30
  #
26
31
  # Copy children of other node's top-level, direct descendants to this
27
32
  # node's direct descendants of the same name.
28
- def merge(other)
29
- other.children.each do |other_child|
30
- direct_child = find_or_create_child(other_child.name)
31
- new_grandchild = Node.new(other.name)
32
- new_grandchild.children = other_child.children
33
- new_grandchild.file = other.file
34
-
35
- direct_child.add_child(new_grandchild)
33
+ def merge_tag_nodes(other)
34
+ other.children.each do |other_tag_node|
35
+ tag_node = find_or_create_child(other_tag_node.name)
36
+ new_grandchild = Node.new(other.name)
37
+ new_grandchild.children = other_tag_node.children
38
+ new_grandchild.file = other.file
39
+
40
+ tag_node.add_child(new_grandchild)
36
41
  end
37
42
  end
38
43
 
data/lib/cuporter.rb CHANGED
@@ -1,15 +1,23 @@
1
1
  # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
2
  require 'cuporter/node'
3
+ require 'cuporter/filter'
4
+ require 'cuporter/tag_list'
5
+ require 'cuporter/name_list'
3
6
  require 'cuporter/tag_list_node'
4
7
  require 'cuporter/example_set_node'
5
8
  require 'cuporter/node_numberer'
6
9
  require 'cuporter/feature_parser'
7
10
  require 'cuporter/extensions/string'
8
11
  require 'cuporter/cli/options'
9
- require 'cuporter/tag_report'
10
- require 'cuporter/formatters/writer'
11
- require 'cuporter/formatters/text_methods'
12
- require 'cuporter/formatters/text'
13
- require 'cuporter/formatters/csv'
14
- require 'cuporter/formatters/html_node_writer'
15
- require 'cuporter/formatters/html'
12
+ require 'cuporter/cli/filter_args_builder'
13
+ require 'cuporter/report/report'
14
+ require 'cuporter/report/tag_report'
15
+ require 'cuporter/report/name_report'
16
+ require 'cuporter/formatter/writer'
17
+ require 'cuporter/formatter/text_methods'
18
+ require 'cuporter/formatter/text'
19
+ require 'cuporter/formatter/csv'
20
+ require 'cuporter/formatter/html_node_writer'
21
+ require 'cuporter/formatter/html_methods'
22
+ require 'cuporter/formatter/tag_report_html'
23
+ require 'cuporter/formatter/name_report_html'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuporter
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 7
10
- version: 0.2.7
9
+ - 8
10
+ version: 0.2.8
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tim Camper
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-24 00:00:00 -04:00
18
+ date: 2010-09-27 00:00:00 -04:00
19
19
  default_executable: cuporter
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -34,7 +34,7 @@ dependencies:
34
34
  version: 2.1.2
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
- description: Scrapes Cucumber *.feature files to build report on tag usage
37
+ description: Scrapes Cucumber *.feature files to build reports on tag usage and test inventory
38
38
  email: twcamper@thoughtworks.com
39
39
  executables:
40
40
  - cuporter
@@ -47,23 +47,31 @@ files:
47
47
  - LICENSE
48
48
  - README.textile
49
49
  - Rakefile
50
+ - lib/cuporter/name_list.rb
50
51
  - lib/cuporter/example_set_node.rb
51
- - lib/cuporter/formatters/csv.rb
52
- - lib/cuporter/formatters/text.rb
53
- - lib/cuporter/formatters/html_node_writer.rb
54
- - lib/cuporter/formatters/html.rb
55
- - lib/cuporter/formatters/writer.rb
56
- - lib/cuporter/formatters/text_methods.rb
52
+ - lib/cuporter/formatter/csv.rb
53
+ - lib/cuporter/formatter/text.rb
54
+ - lib/cuporter/formatter/html_node_writer.rb
55
+ - lib/cuporter/formatter/html_methods.rb
56
+ - lib/cuporter/formatter/tag_report_html.rb
57
+ - lib/cuporter/formatter/name_report_html.rb
58
+ - lib/cuporter/formatter/writer.rb
59
+ - lib/cuporter/formatter/text_methods.rb
60
+ - lib/cuporter/tag_list.rb
61
+ - lib/cuporter/report/report.rb
62
+ - lib/cuporter/report/name_report.rb
63
+ - lib/cuporter/report/tag_report.rb
64
+ - lib/cuporter/cli/filter_args_builder.rb
57
65
  - lib/cuporter/cli/options.rb
58
66
  - lib/cuporter/tag_list_node.rb
59
67
  - lib/cuporter/extensions/string.rb
60
68
  - lib/cuporter/feature_parser.rb
61
69
  - lib/cuporter/node.rb
70
+ - lib/cuporter/filter.rb
62
71
  - lib/cuporter/node_numberer.rb
63
- - lib/cuporter/tag_report.rb
64
72
  - lib/cuporter.rb
65
- - lib/cuporter/formatters/cuporter.css
66
- - lib/cuporter/formatters/jquery-min.js
73
+ - lib/cuporter/formatter/cuporter.css
74
+ - lib/cuporter/formatter/jquery-min.js
67
75
  - bin/cuporter
68
76
  has_rdoc: true
69
77
  homepage: http://github.com/twcamper/cuporter
@@ -107,6 +115,6 @@ rubyforge_project: cuporter
107
115
  rubygems_version: 1.3.7
108
116
  signing_key:
109
117
  specification_version: 3
110
- summary: Scrapes Cucumber *.feature files to build report on tag usage
118
+ summary: Scrapes Cucumber *.feature files to build reports on tag usage and test inventory
111
119
  test_files: []
112
120
 
@@ -1,14 +0,0 @@
1
- # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
-
3
- module Cuporter
4
- module Formatters
5
- class Writer
6
-
7
- def initialize(report, output, number_scenarios)
8
- @report = report
9
- @output = output ? File.open(output, "w") : STDOUT
10
- @number_scenarios = number_scenarios
11
- end
12
- end
13
- end
14
- end
@@ -1,24 +0,0 @@
1
- # Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
2
- module Cuporter
3
- class TagReport
4
-
5
- def initialize(input_file_pattern)
6
- @input_file_pattern = input_file_pattern || "features/**/*.feature"
7
- end
8
-
9
- def files
10
- Dir[@input_file_pattern].collect {|f| File.expand_path f}
11
- end
12
-
13
- def scenarios_per_tag
14
- tags = TagListNode.new("report",[])
15
- files.each do |file|
16
- feature = FeatureParser.parse(file)
17
- tags.merge(feature) if feature
18
- end
19
- tags.sort_all_descendants!
20
- tags
21
- end
22
-
23
- end
24
- end