stepdown 0.5.0 → 0.6.0
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/Gemfile +1 -1
- data/Gemfile.lock +8 -8
- data/History.txt +4 -1
- data/README.rdoc +10 -3
- data/Rakefile +1 -0
- data/bin/stepdown +1 -0
- data/lib/stepdown.rb +4 -1
- data/lib/stepdown/analyzer.rb +10 -8
- data/lib/stepdown/graph.rb +75 -0
- data/lib/stepdown/html_reporter.rb +6 -6
- data/lib/stepdown/options.rb +6 -2
- data/lib/stepdown/reporter.rb +7 -106
- data/lib/stepdown/statistics.rb +120 -0
- data/lib/stepdown/text_reporter.rb +2 -2
- data/lib/stepdown/version.rb +3 -0
- data/lib/stepdown/yaml_writer.rb +14 -0
- data/public/bluff-min.js +1 -0
- data/public/excanvas.js +35 -0
- data/public/js-class.js +1 -0
- data/spec/fixtures/20110611.yml +5 -0
- data/spec/fixtures/20110612.yml +5 -0
- data/spec/lib/stepdown/analyzer_spec.rb +42 -6
- data/spec/lib/stepdown/graph_spec.rb +82 -0
- data/spec/lib/stepdown/{reporter_spec.rb → statistics_spec.rb} +15 -14
- data/stepdown.gemspec +10 -7
- data/templates/main.html.haml +43 -32
- metadata +20 -17
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -8,14 +8,14 @@ GEM
|
|
8
8
|
json (1.4.6)
|
9
9
|
rake (0.8.7)
|
10
10
|
rcov (0.9.9)
|
11
|
-
rspec (2.
|
12
|
-
rspec-core (~> 2.
|
13
|
-
rspec-expectations (~> 2.
|
14
|
-
rspec-mocks (~> 2.
|
15
|
-
rspec-core (2.
|
16
|
-
rspec-expectations (2.
|
11
|
+
rspec (2.5.0)
|
12
|
+
rspec-core (~> 2.5.0)
|
13
|
+
rspec-expectations (~> 2.5.0)
|
14
|
+
rspec-mocks (~> 2.5.0)
|
15
|
+
rspec-core (2.5.1)
|
16
|
+
rspec-expectations (2.5.0)
|
17
17
|
diff-lcs (~> 1.1.2)
|
18
|
-
rspec-mocks (2.
|
18
|
+
rspec-mocks (2.5.0)
|
19
19
|
|
20
20
|
PLATFORMS
|
21
21
|
ruby
|
@@ -25,4 +25,4 @@ DEPENDENCIES
|
|
25
25
|
haml (> 2.0.0)
|
26
26
|
rake
|
27
27
|
rcov (~> 0.9.9)
|
28
|
-
rspec (~> 2.5)
|
28
|
+
rspec (~> 2.5.0)
|
data/History.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
=
|
1
|
+
= Stepdown
|
2
2
|
Stepdown allows you to see where your most used Cucumber steps are, your unused steps and how they are clustered
|
3
3
|
|
4
4
|
== Statistics available
|
5
5
|
* Total number of scenarios
|
6
6
|
* Total number of steps
|
7
|
+
* Number of empty scenarios (scenarios without any steps)
|
7
8
|
* Number of steps per scenario
|
8
|
-
* Number of unique scenario
|
9
|
+
* Number of unique steps per scenario
|
9
10
|
* Per step
|
10
11
|
* Total usage
|
11
12
|
* Number of scenarios
|
@@ -13,9 +14,15 @@ Stepdown allows you to see where your most used Cucumber steps are, your unused
|
|
13
14
|
* Scenario grouping (number of times used with another step)
|
14
15
|
|
15
16
|
== Installation
|
16
|
-
Stepdown is available as a
|
17
|
+
Stepdown is available as a Ruby gem
|
17
18
|
gem install stepdown
|
18
19
|
|
20
|
+
Or, add the following to your Gemfile
|
21
|
+
gem 'stepdown'
|
22
|
+
|
19
23
|
== Usage
|
24
|
+
From the root folder of your project. This assumes step definitions are in PROJECT_ROOT/features/step_definitions and feature files are in PROJECT_ROOT/features
|
25
|
+
stepdown
|
26
|
+
Or, set the the directory parameters manually
|
20
27
|
stepdown --steps <step definition directory> --features <feature file directory>
|
21
28
|
e.g. stepdown --steps features/step_definitions --features features
|
data/Rakefile
CHANGED
data/bin/stepdown
CHANGED
data/lib/stepdown.rb
CHANGED
@@ -2,6 +2,9 @@ require 'stepdown/feature_parser'
|
|
2
2
|
require 'stepdown/step_instance'
|
3
3
|
require 'stepdown/html_reporter'
|
4
4
|
require 'stepdown/text_reporter'
|
5
|
+
require 'stepdown/statistics'
|
6
|
+
require 'stepdown/yaml_writer'
|
7
|
+
require 'stepdown/graph'
|
5
8
|
require 'stepdown/analyzer'
|
6
9
|
|
7
10
|
require 'rubygems'
|
@@ -9,6 +12,6 @@ require 'bundler/setup'
|
|
9
12
|
|
10
13
|
module Stepdown
|
11
14
|
class << self
|
12
|
-
attr_accessor :quiet
|
15
|
+
attr_accessor :quiet, :output_directory
|
13
16
|
end
|
14
17
|
end
|
data/lib/stepdown/analyzer.rb
CHANGED
@@ -14,9 +14,14 @@ module Stepdown
|
|
14
14
|
|
15
15
|
puts "Performing analysis..." unless Stepdown.quiet
|
16
16
|
|
17
|
-
|
17
|
+
stats = Statistics.new(scenarios, instance.step_collection)
|
18
|
+
|
19
|
+
reporter = reporter(@reporter, stats)
|
18
20
|
reporter.output_overview
|
19
21
|
|
22
|
+
Stepdown::YamlWriter.write(stats)
|
23
|
+
Stepdown::Graph.create_graph
|
24
|
+
|
20
25
|
end
|
21
26
|
|
22
27
|
def process_feature_files(feature_files)
|
@@ -30,20 +35,17 @@ module Stepdown
|
|
30
35
|
listener.scenarios
|
31
36
|
end
|
32
37
|
|
33
|
-
def reporter(type,
|
38
|
+
def reporter(type, stats)
|
34
39
|
case type
|
35
40
|
when "html"
|
36
|
-
Stepdown::HTMLReporter.new(
|
41
|
+
Stepdown::HTMLReporter.new(stats)
|
37
42
|
when "text"
|
38
|
-
Stepdown::TextReporter.new(
|
43
|
+
Stepdown::TextReporter.new(stats)
|
39
44
|
when "quiet"
|
40
|
-
Stepdown::Reporter.new(
|
45
|
+
Stepdown::Reporter.new(stats)
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
|
-
def step(id)
|
45
|
-
instance.steps.detect{|step| step.id == id}
|
46
|
-
end
|
47
49
|
|
48
50
|
def instance
|
49
51
|
@instance ||= begin
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'date'
|
3
|
+
module Stepdown
|
4
|
+
class Graph
|
5
|
+
BLUFF_GRAPH_SIZE = "890x400"
|
6
|
+
BLUFF_DEFAULT_OPTIONS = <<-EOS
|
7
|
+
var g = new Bluff.Line('graph', "#{BLUFF_GRAPH_SIZE}");
|
8
|
+
g.theme_37signals();
|
9
|
+
g.tooltips = true;
|
10
|
+
g.title_font_size = "24px"
|
11
|
+
g.legend_font_size = "12px"
|
12
|
+
g.marker_font_size = "10px"
|
13
|
+
EOS
|
14
|
+
|
15
|
+
def self.create_graph
|
16
|
+
stats, labels = collect_stats
|
17
|
+
label_set = {}
|
18
|
+
labels.each_with_index do |label, i|
|
19
|
+
label_set.update({i => label})
|
20
|
+
end
|
21
|
+
|
22
|
+
content = <<-EOS
|
23
|
+
#{BLUFF_DEFAULT_OPTIONS}
|
24
|
+
g.title = 'Stepdown';
|
25
|
+
g.data('Total scenarios', [#{stats[:number_scenarios].join(',')}]);
|
26
|
+
g.data('Total steps', [#{stats[:total_steps].join(',')}]);
|
27
|
+
g.data('Total steps per scenario', [#{stats[:steps_per_scenario].join(',')}]);
|
28
|
+
g.data('Total unused steps', [#{stats[:unused_steps].join(',')}]);
|
29
|
+
g.labels = #{label_set.to_json};
|
30
|
+
g.draw();
|
31
|
+
EOS
|
32
|
+
|
33
|
+
File.open(File.join(Stepdown.output_directory, 'stepdown.js'), 'w') do
|
34
|
+
|f| f << content
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.collect_stats
|
39
|
+
stats = Hash.new {|hsh, key| hsh[key] = [] }
|
40
|
+
labels = []
|
41
|
+
|
42
|
+
load_stats.each do |stat_set|
|
43
|
+
stat_set.each{|key, val| stats[key].push val }
|
44
|
+
labels.push(stat_set[:label])
|
45
|
+
end
|
46
|
+
|
47
|
+
[stats, labels]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.load_stats
|
51
|
+
stat_collection = []
|
52
|
+
Dir.glob("#{Stepdown.output_directory}/*.yml").sort.each do |file_name|
|
53
|
+
stats = Hash.new {|hsh, key| hsh[key] = [] }
|
54
|
+
file = File.open(file_name)
|
55
|
+
stat_set = YAML::load(file)
|
56
|
+
|
57
|
+
stat_set.each do |key, val|
|
58
|
+
stats[key].push(val)
|
59
|
+
end
|
60
|
+
stats[:label] = date_from_file_name(file_name)
|
61
|
+
stat_collection << stats
|
62
|
+
|
63
|
+
file.close
|
64
|
+
end
|
65
|
+
|
66
|
+
stat_collection
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.date_from_file_name(file_name)
|
70
|
+
label_date = Date.strptime(file_name.match(/(\d+)/)[1], "%Y%m%d")
|
71
|
+
"#{label_date.day} / #{label_date.month}"
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -8,33 +8,33 @@ module Stepdown
|
|
8
8
|
|
9
9
|
def output_overview()
|
10
10
|
puts "Generating report..." unless Stepdown.quiet
|
11
|
-
FileUtils.mkdir_p(
|
11
|
+
FileUtils.mkdir_p(Stepdown.output_directory)
|
12
12
|
copy_files
|
13
13
|
|
14
14
|
template = File.open(File.expand_path(File.dirname(__FILE__)) + '/../../templates/main.html.haml').read()
|
15
15
|
engine = Haml::Engine.new(template)
|
16
16
|
|
17
|
-
out = File.new(
|
17
|
+
out = File.new(Stepdown.output_directory + '/analysis.html','w+')
|
18
18
|
out.puts engine.render(self)
|
19
19
|
out.close
|
20
20
|
|
21
21
|
template = File.open(File.expand_path(File.dirname(__FILE__)) + '/../../templates/style.sass').read
|
22
22
|
sass_engine = Sass::Engine.new(template)
|
23
23
|
|
24
|
-
out = File.new(
|
24
|
+
out = File.new(Stepdown.output_directory + '/style.css', 'w+')
|
25
25
|
out.puts sass_engine.render
|
26
26
|
|
27
27
|
out.close
|
28
28
|
|
29
|
-
puts "\nReport output to #{
|
29
|
+
puts "\nReport output to #{Stepdown.output_directory}/analysis.html" unless Stepdown.quiet
|
30
30
|
end
|
31
31
|
|
32
32
|
protected
|
33
33
|
|
34
34
|
def copy_files
|
35
|
-
['step_down.js', 'jquery-1.4.3.min.js'].each do |file|
|
35
|
+
['step_down.js', 'jquery-1.4.3.min.js', 'bluff-min.js', 'excanvas.js', 'js-class.js'].each do |file|
|
36
36
|
src = File.expand_path("#{File.dirname(__FILE__)}/../../public/#{file}")
|
37
|
-
FileUtils.cp(src, File.join(
|
37
|
+
FileUtils.cp(src, File.join(Stepdown.output_directory, "#{file}"))
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/stepdown/options.rb
CHANGED
@@ -2,7 +2,7 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Stepdown
|
4
4
|
class Options
|
5
|
-
attr_reader :steps_dir, :features_dir, :reporter, :quiet
|
5
|
+
attr_reader :steps_dir, :features_dir, :reporter, :quiet, :output_directory
|
6
6
|
|
7
7
|
OUTPUT_FORMATS = ["html", "text", "quiet"]
|
8
8
|
|
@@ -11,8 +11,12 @@ module Stepdown
|
|
11
11
|
@features_dir = "features"
|
12
12
|
@reporter = "html"
|
13
13
|
@quiet = false
|
14
|
+
@output_directory = "./stepdown"
|
14
15
|
parser = OptionParser.new do |opts|
|
15
|
-
opts.banner =
|
16
|
+
opts.banner = <<-USE
|
17
|
+
Usage: stepdown
|
18
|
+
stepdown --steps <STEP_DEFINITION_DIRECTORY> --features <FEATURE_FILE_DIRECTORY>
|
19
|
+
USE
|
16
20
|
|
17
21
|
opts.separator("")
|
18
22
|
|
data/lib/stepdown/reporter.rb
CHANGED
@@ -2,114 +2,15 @@ require 'stepdown/step_group'
|
|
2
2
|
require 'stepdown/step_usage'
|
3
3
|
|
4
4
|
module Stepdown
|
5
|
-
class Reporter
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(scenarios, step_collection)
|
11
|
-
@scenarios = scenarios
|
12
|
-
@step_collection = step_collection
|
13
|
-
end
|
14
|
-
|
15
|
-
def groupings
|
16
|
-
@groupings ||= grouping(@scenarios)
|
17
|
-
end
|
18
|
-
|
19
|
-
def total_scenarios
|
20
|
-
@scenarios.length
|
21
|
-
end
|
22
|
-
|
23
|
-
def total_steps
|
24
|
-
@step_collection.length
|
25
|
-
end
|
26
|
-
|
27
|
-
def steps_per_scenario
|
28
|
-
steps_scenario(@scenarios)
|
29
|
-
end
|
30
|
-
|
31
|
-
def unique_steps
|
32
|
-
uniq_steps_per_scenario(@scenarios)
|
33
|
-
end
|
34
|
-
|
35
|
-
def grouping(scenarios)
|
36
|
-
step_groups = @step_collection.collect{|step| Stepdown::StepGroup.new(step) }
|
37
|
-
|
38
|
-
step_groups.each do |step_group|
|
39
|
-
scenarios.each do |scenario|
|
40
|
-
|
41
|
-
if scenario.steps.any?{|step| step.id == step_group.id}
|
42
|
-
step_group.add_steps(scenario.steps)
|
43
|
-
step_group.update_use_count(scenario.step_count)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
step_groups.sort{|a,b| b.use_count <=> a.use_count}
|
49
|
-
end
|
50
|
-
|
51
|
-
def step_usage(scenarios)
|
52
|
-
usages = @step_collection.collect{|step| Stepdown::StepUsage.new(step) }
|
53
|
-
scenarios.each do |scenario|
|
54
|
-
|
55
|
-
scenario.steps.each do |step|
|
56
|
-
usage = usages.detect{|use| use.step.id == step.id}
|
57
|
-
if usage
|
58
|
-
usage.total_usage += step.count
|
59
|
-
usage.number_scenarios += 1
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
usages.each do |usage|
|
65
|
-
if usage.number_scenarios > 0
|
66
|
-
use = sprintf "%.2f", (usage.total_usage / Float(usage.number_scenarios))
|
67
|
-
usage.use_scenario = use
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
usages.sort{|a,b| b.total_usage <=> a.total_usage}
|
72
|
-
end
|
73
|
-
|
74
|
-
def step_usages
|
75
|
-
@step_usages ||= step_usage(@scenarios)
|
76
|
-
end
|
77
|
-
|
78
|
-
def usages
|
79
|
-
step_usages.select{|use| use.total_usage > 0 }
|
80
|
-
end
|
81
|
-
|
82
|
-
def unused_steps
|
83
|
-
step_usages.select{|use| use.total_usage == 0}
|
84
|
-
end
|
85
|
-
|
86
|
-
def unused_step_count
|
87
|
-
unused_steps.length
|
88
|
-
end
|
89
|
-
|
90
|
-
def uniq_steps_per_scenario(scenarios)
|
91
|
-
total_steps = 0.0
|
92
|
-
uniq_steps = 0.0
|
93
|
-
scenarios.each do |scen|
|
94
|
-
total_steps += scen.step_count
|
95
|
-
uniq_steps += scen.unique_step_count
|
96
|
-
end
|
97
|
-
sprintf "%.2f", (total_steps / uniq_steps)
|
98
|
-
end
|
99
|
-
|
100
|
-
def steps_scenario(scenarios)
|
101
|
-
scen_count = scenarios.length
|
102
|
-
step_count = 0.0
|
103
|
-
scenarios.each do |scenario|
|
104
|
-
step_count += scenario.step_count
|
105
|
-
end
|
106
|
-
sprintf "%.2f", (step_count / scen_count)
|
5
|
+
class Reporter < Delegator
|
6
|
+
|
7
|
+
def initialize(statistics)
|
8
|
+
@statistics = statistics
|
9
|
+
super @statistics
|
107
10
|
end
|
108
11
|
|
109
|
-
def
|
110
|
-
@
|
111
|
-
scen.steps.empty?
|
112
|
-
end
|
12
|
+
def __getobj__
|
13
|
+
@statistics
|
113
14
|
end
|
114
15
|
|
115
16
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
|
2
|
+
module Stepdown
|
3
|
+
class Statistics
|
4
|
+
attr_reader :scenarios, :usages, :step_collection, :grouping
|
5
|
+
|
6
|
+
def initialize(scenarios, step_collection)
|
7
|
+
@scenarios = scenarios
|
8
|
+
@step_collection = step_collection
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
stats = {}
|
13
|
+
stats[:number_scenarios] = total_scenarios
|
14
|
+
stats[:total_steps] = total_steps
|
15
|
+
stats[:steps_per_scenario] = steps_per_scenario
|
16
|
+
stats[:unused_steps] = unused_step_count
|
17
|
+
stats
|
18
|
+
end
|
19
|
+
|
20
|
+
def groupings
|
21
|
+
@groupings ||= grouping(@scenarios)
|
22
|
+
end
|
23
|
+
|
24
|
+
def total_scenarios
|
25
|
+
@scenarios.length
|
26
|
+
end
|
27
|
+
|
28
|
+
def total_steps
|
29
|
+
@step_collection.length
|
30
|
+
end
|
31
|
+
|
32
|
+
def steps_per_scenario
|
33
|
+
steps_scenario(@scenarios)
|
34
|
+
end
|
35
|
+
|
36
|
+
def unique_steps
|
37
|
+
uniq_steps_per_scenario(@scenarios)
|
38
|
+
end
|
39
|
+
|
40
|
+
def grouping(scenarios)
|
41
|
+
step_groups = @step_collection.collect{|step| Stepdown::StepGroup.new(step) }
|
42
|
+
|
43
|
+
step_groups.each do |step_group|
|
44
|
+
scenarios.each do |scenario|
|
45
|
+
|
46
|
+
if scenario.steps.any?{|step| step.id == step_group.id}
|
47
|
+
step_group.add_steps(scenario.steps)
|
48
|
+
step_group.update_use_count(scenario.step_count)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
step_groups.sort{|a,b| b.use_count <=> a.use_count}
|
54
|
+
end
|
55
|
+
|
56
|
+
def step_usage(scenarios)
|
57
|
+
usages = @step_collection.collect{|step| Stepdown::StepUsage.new(step) }
|
58
|
+
scenarios.each do |scenario|
|
59
|
+
|
60
|
+
scenario.steps.each do |step|
|
61
|
+
usage = usages.detect{|use| use.step.id == step.id}
|
62
|
+
if usage
|
63
|
+
usage.total_usage += step.count
|
64
|
+
usage.number_scenarios += 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
usages.each do |usage|
|
70
|
+
if usage.number_scenarios > 0
|
71
|
+
use = sprintf "%.2f", (usage.total_usage / Float(usage.number_scenarios))
|
72
|
+
usage.use_scenario = use
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
usages.sort{|a,b| b.total_usage <=> a.total_usage}
|
77
|
+
end
|
78
|
+
|
79
|
+
def step_usages
|
80
|
+
@step_usages ||= step_usage(@scenarios)
|
81
|
+
end
|
82
|
+
|
83
|
+
def usages
|
84
|
+
step_usages.select{|use| use.total_usage > 0 }
|
85
|
+
end
|
86
|
+
|
87
|
+
def unused_steps
|
88
|
+
step_usages.select{|use| use.total_usage == 0}
|
89
|
+
end
|
90
|
+
|
91
|
+
def unused_step_count
|
92
|
+
unused_steps.length
|
93
|
+
end
|
94
|
+
|
95
|
+
def uniq_steps_per_scenario(scenarios)
|
96
|
+
total_steps = 0.0
|
97
|
+
uniq_steps = 0.0
|
98
|
+
scenarios.each do |scen|
|
99
|
+
total_steps += scen.step_count
|
100
|
+
uniq_steps += scen.unique_step_count
|
101
|
+
end
|
102
|
+
sprintf "%.2f", (total_steps / uniq_steps)
|
103
|
+
end
|
104
|
+
|
105
|
+
def steps_scenario(scenarios)
|
106
|
+
scen_count = scenarios.length
|
107
|
+
step_count = 0.0
|
108
|
+
scenarios.each do |scenario|
|
109
|
+
step_count += scenario.step_count
|
110
|
+
end
|
111
|
+
sprintf "%.2f", (step_count / scen_count)
|
112
|
+
end
|
113
|
+
|
114
|
+
def empty_scenarios
|
115
|
+
@scenarios.select do |scen|
|
116
|
+
scen.steps.empty?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|