stepdown 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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