cuporter 0.2.7 → 0.2.8

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 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