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 CHANGED
@@ -5,6 +5,6 @@ gem 'gherkin', '~> 2.3'
5
5
 
6
6
  group :development,:test do
7
7
  gem 'rake'
8
- gem 'rspec', '~> 2.5'
8
+ gem 'rspec', '~> 2.5.0'
9
9
  gem 'rcov', '~> 0.9.9'
10
10
  end
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.6.0)
12
- rspec-core (~> 2.6.0)
13
- rspec-expectations (~> 2.6.0)
14
- rspec-mocks (~> 2.6.0)
15
- rspec-core (2.6.0)
16
- rspec-expectations (2.6.0)
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.6.0)
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
@@ -1,4 +1,7 @@
1
- == 0.50 2011-5-17
1
+ == 0.6.0 2011-6
2
+ * Graph for overall statistics
3
+
4
+ == 0.5.0 2011-5-17
2
5
  * Use gherkin gem to parse features
3
6
  * Added empty step detection
4
7
  * Added unused step count to summary
data/README.rdoc CHANGED
@@ -1,11 +1,12 @@
1
- = Step down
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 ruby gem
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
@@ -1,4 +1,5 @@
1
1
  require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
2
3
 
3
4
  RSpec::Core::RakeTask.new(:spec) do |t|
4
5
  t.fail_on_error = false
data/bin/stepdown CHANGED
@@ -12,6 +12,7 @@ begin
12
12
  options.validate
13
13
 
14
14
  Stepdown.quiet = options.quiet
15
+ Stepdown.output_directory = options.output_directory
15
16
 
16
17
  Stepdown::Analyzer.new(options.steps_dir, options.features_dir, options.reporter).analyse
17
18
 
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
@@ -14,9 +14,14 @@ module Stepdown
14
14
 
15
15
  puts "Performing analysis..." unless Stepdown.quiet
16
16
 
17
- reporter = reporter(@reporter, scenarios, instance.step_collection)
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, scenarios, step_collection)
38
+ def reporter(type, stats)
34
39
  case type
35
40
  when "html"
36
- Stepdown::HTMLReporter.new(scenarios, step_collection)
41
+ Stepdown::HTMLReporter.new(stats)
37
42
  when "text"
38
- Stepdown::TextReporter.new(scenarios, step_collection)
43
+ Stepdown::TextReporter.new(stats)
39
44
  when "quiet"
40
- Stepdown::Reporter.new(scenarios, step_collection)
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(Reporter::OUTPUT_DIR)
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(Reporter::OUTPUT_DIR + '/analysis.html','w+')
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(Reporter::OUTPUT_DIR + '/style.css', 'w+')
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 #{Reporter::OUTPUT_DIR}/analysis.html" unless Stepdown.quiet
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(OUTPUT_DIR, "#{file}"))
37
+ FileUtils.cp(src, File.join(Stepdown.output_directory, "#{file}"))
38
38
  end
39
39
  end
40
40
  end
@@ -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 = "Usage: stepdown step_definition_dir feature_file_directory"
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
 
@@ -2,114 +2,15 @@ require 'stepdown/step_group'
2
2
  require 'stepdown/step_usage'
3
3
 
4
4
  module Stepdown
5
- class Reporter
6
- OUTPUT_DIR = "./stepdown"
7
-
8
- attr_reader :scenarios, :usages, :step_collection, :grouping
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 empty_scenarios
110
- @scenarios.select do |scen|
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