stepdown 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +4 -2
  2. data/Gemfile.lock +14 -10
  3. data/History.txt +30 -0
  4. data/README.rdoc +2 -2
  5. data/Rakefile +13 -0
  6. data/bin/stepdown +4 -4
  7. data/lib/stepdown.rb +6 -0
  8. data/lib/stepdown/analyzer.rb +67 -0
  9. data/lib/stepdown/feature_parser.rb +30 -0
  10. data/lib/stepdown/html_reporter.rb +41 -0
  11. data/lib/stepdown/options.rb +59 -0
  12. data/lib/stepdown/reporter.rb +106 -0
  13. data/lib/stepdown/scenario.rb +25 -0
  14. data/lib/stepdown/step.rb +22 -0
  15. data/lib/stepdown/step_collection.rb +36 -0
  16. data/lib/stepdown/step_group.rb +45 -0
  17. data/lib/stepdown/step_instance.rb +63 -0
  18. data/lib/stepdown/step_usage.rb +15 -0
  19. data/lib/stepdown/text_reporter.rb +38 -0
  20. data/spec/lib/{feature_parser_spec.rb → stepdown/feature_parser_spec.rb} +13 -12
  21. data/spec/lib/{options_spec.rb → stepdown/options_spec.rb} +24 -6
  22. data/spec/lib/stepdown/reporter_spec.rb +184 -0
  23. data/spec/lib/stepdown/scenario_spec.rb +40 -0
  24. data/spec/lib/stepdown/step_collection_spec.rb +78 -0
  25. data/spec/lib/stepdown/step_group_spec.rb +43 -0
  26. data/spec/lib/{step_instance_spec.rb → stepdown/step_instance_spec.rb} +13 -12
  27. data/spec/spec_helper.rb +4 -0
  28. data/stepdown.gemspec +7 -4
  29. data/templates/main.html.haml +3 -3
  30. data/templates/style.sass +5 -4
  31. metadata +64 -34
  32. data/lib/counting_step.rb +0 -14
  33. data/lib/feature_parser.rb +0 -32
  34. data/lib/html_reporter.rb +0 -37
  35. data/lib/options.rb +0 -69
  36. data/lib/reporter.rb +0 -62
  37. data/lib/scenario.rb +0 -19
  38. data/lib/step.rb +0 -10
  39. data/lib/step_down.rb +0 -112
  40. data/lib/step_group.rb +0 -52
  41. data/lib/step_instance.rb +0 -60
  42. data/lib/step_usage.rb +0 -13
  43. data/lib/text_reporter.rb +0 -36
  44. data/spec/lib/scenario_spec.rb +0 -42
  45. data/spec/lib/step_group_spec.rb +0 -119
@@ -1,14 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/step')
2
- class CountingStep < Step
3
- attr_accessor :count
4
-
5
- def initialize(id, regex)
6
- @count = 0
7
- super(id, regex)
8
- end
9
-
10
- def <=>(step)
11
- self.count <=> step.count
12
- end
13
-
14
- end
@@ -1,32 +0,0 @@
1
- #!/usr/bin/ruby
2
- require 'rubygems'
3
-
4
- require File.expand_path(File.dirname(__FILE__) + '/step')
5
- require File.expand_path(File.dirname(__FILE__) + '/scenario')
6
-
7
- class FeatureParser
8
-
9
- def process_feature(file, instance)
10
- @scenarios = []
11
- file_lines = read_feature_file(file)
12
-
13
- file_lines.each do |line|
14
-
15
- if line =~ /Scenario|Background/
16
- @scenario = Scenario.new
17
- @scenarios << @scenario
18
- else
19
- step = instance.line_matches(line)
20
- @scenario.add_step(step) if step
21
- end
22
- end
23
-
24
- return @scenarios
25
- end
26
-
27
- protected
28
- def read_feature_file(file_name)
29
- File.read(file_name).split("\n")
30
- end
31
- end
32
-
@@ -1,37 +0,0 @@
1
- require 'fileutils'
2
- require 'reporter'
3
- class HTMLReporter < Reporter
4
-
5
- def output_overview()
6
- puts "Generating report..."
7
- FileUtils.mkdir_p(Reporter::OUTPUT_DIR)
8
- copy_files
9
-
10
- template = File.open(File.expand_path(File.dirname(__FILE__)) + '/../templates/main.html.haml').read()
11
- engine = Haml::Engine.new(template)
12
-
13
- out = File.new(Reporter::OUTPUT_DIR + '/analysis.html','w+')
14
- out.puts engine.render(self)
15
- out.close
16
-
17
- template = File.open(File.expand_path(File.dirname(__FILE__)) + '/../templates/style.sass').read
18
- sass_engine = Sass::Engine.new(template)
19
-
20
- out = File.new(Reporter::OUTPUT_DIR + '/style.css', 'w+')
21
- out.puts sass_engine.render
22
-
23
- out.close
24
-
25
- $stdout.puts "\nReport output to #{Reporter::OUTPUT_DIR}/analysis.html"
26
- end
27
-
28
- protected
29
-
30
- def copy_files
31
- ['step_down.js', 'jquery-1.4.3.min.js'].each do |file|
32
- src = File.expand_path("#{File.dirname(__FILE__)}/../public/#{file}")
33
- FileUtils.cp(src, File.join(OUTPUT_DIR, "#{file}"))
34
- end
35
- end
36
-
37
- end
@@ -1,69 +0,0 @@
1
- require 'optparse'
2
-
3
- class Options
4
-
5
- OUTPUT_FORMATS = ["html", "text"]
6
-
7
- def parse(params)
8
- @@steps_dir = "features/step_definitions"
9
- @@features_dir = "features"
10
- @@reporter = "html"
11
- parser = OptionParser.new do |opts|
12
- opts.banner = "Usage: stepdown step_definition_dir feature_file_directory"
13
-
14
- opts.separator("")
15
-
16
- opts.on("--output=TYPE", OUTPUT_FORMATS, "Select ouput format (#{OUTPUT_FORMATS.join(',')}). Default: html") do |o|
17
- @@reporter = o
18
- end
19
-
20
- opts.on("--steps=STEPS_DIR", "Step definition directory. Default: ./features/step_definitions") do |o|
21
- @@steps_dir = o
22
- end
23
-
24
- opts.on("--features=FEATURE_DIR", "Feature file directory. Default: ./features") do |o|
25
- @@features_dir = o
26
- end
27
-
28
- opts.on_tail("-h", "--help", "You're looking at it") do |o|
29
- puts opts
30
- exit
31
- end
32
-
33
- end
34
-
35
- begin
36
- parser.parse(params)
37
-
38
- rescue OptionParser::ParseError => e
39
- puts e
40
- exit 1
41
- end
42
- end
43
-
44
- def validate
45
- @@steps_dir = File.join(Dir.pwd, @@steps_dir)
46
- @@features_dir = File.join(Dir.pwd, @@features_dir)
47
-
48
- [@@steps_dir, @@features_dir].each do |dir|
49
- unless File.exists?(dir)
50
- puts "Directory #{dir} does not exist"
51
- exit 1
52
- end
53
- end
54
-
55
- end
56
-
57
- def steps_dir
58
- @@steps_dir
59
- end
60
-
61
- def features_dir
62
- @@features_dir
63
- end
64
-
65
- def reporter
66
- @@reporter
67
- end
68
-
69
- end
@@ -1,62 +0,0 @@
1
-
2
- class Reporter
3
- OUTPUT_DIR = "./stepdown"
4
-
5
- attr_reader :scenarios, :usages, :steps, :grouping
6
-
7
- def initialize(scenarios, usages, grouping, steps)
8
- @scenarios = scenarios
9
- @usages = usages
10
- @steps = steps
11
- @grouping = grouping
12
- end
13
-
14
- def groupings
15
- @grouping
16
- end
17
-
18
- def total_scenarios
19
- @scenarios.length
20
- end
21
-
22
- def total_steps
23
- @steps.length
24
- end
25
-
26
- def steps_per_scenario
27
- steps_scenario(@scenarios)
28
- end
29
-
30
- def unique_steps
31
- uniq_steps_per_scenario(@scenarios)
32
- end
33
-
34
- def usages
35
- @usages.select{|use| use.total_usage > 0 }
36
- end
37
-
38
- def unused_steps
39
- @usages.select{|use| use.total_usage == 0}
40
- end
41
-
42
- def uniq_steps_per_scenario(scenarios)
43
- total_steps = 0.0
44
- uniq_steps = 0.0
45
- scenarios.each do |scen|
46
- total_steps += scen.steps.length
47
- uniq_steps += scen.uniq_steps.length
48
- end
49
- sprintf "%.2f", (total_steps / uniq_steps)
50
- end
51
-
52
- def steps_scenario(scenarios)
53
- scen_count = scenarios.length
54
- step_count = 0.0
55
- scenarios.each do |scenario|
56
- step_count += scenario.steps.length
57
- end
58
- sprintf "%.2f", (step_count / scen_count)
59
- end
60
-
61
- end
62
-
@@ -1,19 +0,0 @@
1
- class Scenario
2
-
3
- def add_step(step)
4
- steps << step
5
- end
6
-
7
- def steps
8
- @steps ||= []
9
- end
10
-
11
- def uniq_steps
12
- uniq_steps = []
13
- steps.each do |step|
14
- uniq_steps << step unless uniq_steps.any?{|uniq| uniq.id == step.id}
15
- end
16
- uniq_steps
17
- end
18
-
19
- end
@@ -1,10 +0,0 @@
1
- class Step
2
-
3
- attr_reader :id, :regex
4
-
5
- def initialize(id, regex)
6
- @id = id
7
- @regex = regex
8
- end
9
-
10
- end
@@ -1,112 +0,0 @@
1
- require 'feature_parser'
2
- require 'pp'
3
- require 'haml'
4
- require 'sass'
5
-
6
- require 'step_instance'
7
- require 'step_group'
8
- require 'step_usage'
9
- require 'html_reporter'
10
- require 'text_reporter'
11
- class StepDown
12
-
13
- def initialize(steps_dir, feature_dir, reporter)
14
- @feature_files = Dir.glob(feature_dir + '/**/*.feature')
15
- @step_files = Dir.glob(steps_dir + '/**/*.rb')
16
- @reporter = reporter
17
- end
18
-
19
- def analyse
20
- puts "Parsing feature files..."
21
- parser = FeatureParser.new
22
-
23
- scenarios = []
24
- @feature_files.each do |feature|
25
- scenarios << parser.process_feature(feature, instance)
26
- end
27
- scenarios.flatten!
28
-
29
- puts "Performing analysis..."
30
- usages = step_usage(scenarios)
31
- usages = usages.sort{|a,b| b.total_usage <=> a.total_usage }
32
- grouping = grouping(scenarios).sort{|a,b| b.use_count <=> a.use_count}
33
-
34
- reporter = reporter(@reporter, scenarios, usages, grouping, instance.steps)
35
- reporter.output_overview
36
-
37
- # #pp grouping(@scenarios)
38
- # puts YAML::dump(scenario) if scenario.use_count > 100 && scenario.use_count < 500
39
- # end
40
- end
41
-
42
- # don't want a factory just yet
43
- def reporter(type, scenarios, usages, grouping, steps)
44
- case type
45
- when "html"
46
- HTMLReporter.new(scenarios, usages, grouping, steps)
47
- when "text"
48
- TextReporter.new(scenarios, usages, grouping, steps)
49
- end
50
- end
51
-
52
- def step_usage(scenarios)
53
- usages = instance.steps.collect{|step| StepUsage.new(step) }
54
- scenarios.each do |scenario|
55
-
56
- scenario.steps.each do |step|
57
- usage = usages.detect{|use| use.step.id == step.id}
58
- usage.total_usage += 1 if usage
59
- end
60
-
61
- scenario.uniq_steps.each do |step|
62
- usage = usages.detect{|use| use.step.id == step.id}
63
- usage.number_scenarios += 1 if usage
64
- end
65
-
66
- end
67
-
68
- usages.each do |usage|
69
- if usage.number_scenarios > 0
70
- use = sprintf "%.2f", (usage.total_usage / Float(usage.number_scenarios))
71
- usage.use_scenario = use
72
- end
73
- end
74
-
75
- usages
76
- end
77
-
78
- def grouping(scenarios)
79
- step_groups = instance.steps.collect{|step| StepGroup.new(step) }
80
-
81
- step_groups.each do |step_group|
82
- scenarios.each do |scenario|
83
- used = scenario.steps.any?{|s| s.id == step_group.id}
84
-
85
- if used
86
- scenario.steps.each do |scen_step|
87
- step_group.add_step(scen_step)
88
- end
89
- step_group.update_use_count
90
- end
91
-
92
- end
93
-
94
- end
95
- step_groups
96
- end
97
-
98
- def step(id)
99
- instance.steps.detect{|step| step.id == id}
100
- end
101
-
102
- def instance
103
- @instance ||= begin
104
- new_inst = StepInstance.new
105
-
106
- Dir.glob(@step_files).each do |file_name|
107
- new_inst.instance_eval File.read(file_name)
108
- end
109
- new_inst
110
- end
111
- end
112
- end
@@ -1,52 +0,0 @@
1
- require 'cgi'
2
- require 'counting_step'
3
- class StepGroup
4
- attr_reader :id, :regex, :total_usage
5
-
6
- def initialize(step)
7
- @id = step.id
8
- @regex = step.regex
9
- @total_usage = 0
10
- @in_steps = {}
11
- end
12
-
13
- def in_steps
14
- @in_steps.sort{|a,b| b[1] <=> a[1]}
15
- end
16
-
17
- def add_step(step)
18
- if @in_steps[step.id]
19
- @in_steps[step.id].count += 1
20
- else
21
- @in_steps[step.id] = CountingStep.new(step.id, step.regex)
22
- @in_steps[step.id].count = 1
23
- end
24
- end
25
-
26
- def update_use_count
27
- @total_usage = 0
28
- @in_steps.each do |key,val|
29
- @total_usage += val.count
30
- end
31
- @total_usage
32
- end
33
-
34
- def use_count
35
- @total_usage || 0
36
- end
37
-
38
- def group_graph
39
- base = "https://chart.googleapis.com/chart?cht=gv:circo&chl=graph{"
40
- base += "a [label=\"#{CGI.escape(CGI.escapeHTML(@regex.inspect.to_s))}\"];"
41
-
42
- @in_steps.each do |id,in_step|
43
-
44
- next if in_step[:step].regex.nil?
45
- base += "a--\"#{CGI.escape(CGI.escapeHTML(in_step[:step].regex.inspect.to_s))}\" [weight=#{in_step[:count]}];"
46
- #a [label=\"#{grouping.in_steps[0][:step].regex.inspect}\"]; a--b [penwidth=3,weight=2];b--d}"
47
- end
48
- base += "}"
49
- base
50
- end
51
-
52
- end
@@ -1,60 +0,0 @@
1
- class StepInstance
2
- def initialize
3
- @steps = []
4
- end
5
-
6
- def Given(regex,&block)
7
- define_step(regex,&block)
8
- end
9
-
10
- def When(regex,&block)
11
- define_step(regex,&block)
12
- end
13
-
14
- def Then(regex,&block)
15
- define_step(regex,&block)
16
- end
17
-
18
- def define_step(regex,&block)
19
- @steps << regex
20
- end
21
-
22
- def self.method_missing(*args)
23
- #nothing
24
- end
25
-
26
- def method_missing(*args)
27
- #nothing
28
- end
29
-
30
- def self.const_missing(*args)
31
- self
32
- end
33
-
34
- def require(*args)
35
- # do nothing
36
- end
37
-
38
- def line_matches(line)
39
- stripped_line = line.strip.gsub(/^(And|Given|When|Then) (.*)$/,'\2')
40
-
41
- @steps.each_with_index do |regex,i|
42
- match = regex.match(stripped_line)
43
- if match
44
- return steps[i]
45
- end
46
- end
47
-
48
- return nil
49
- end
50
-
51
- def steps
52
- return @step_definitions if @step_definitions
53
- @step_definitions = []
54
- @steps.each_with_index do |regex, i|
55
- @step_definitions << Step.new(i, regex)
56
- end
57
- @step_definitions
58
- end
59
-
60
- end