stepdown 0.3.3 → 0.4.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.
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
@@ -0,0 +1,22 @@
1
+ module Stepdown
2
+ class Step
3
+
4
+ attr_accessor :count
5
+ attr_reader :id, :regex
6
+
7
+ def initialize(id, regex)
8
+ @id = id
9
+ @regex = regex
10
+ @count = 0
11
+ end
12
+
13
+ def <=>(other)
14
+ other.count <=> self.count
15
+ end
16
+
17
+ def ==(other)
18
+ self.id == other.id
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ require 'stepdown/step'
2
+
3
+ module Stepdown
4
+ class StepCollection
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @steps = {}
9
+ end
10
+
11
+ def add_step(id, regex)
12
+ if @steps[id]
13
+ @steps[id].count += 1
14
+ else
15
+ @steps[id] = Stepdown::Step.new(id, regex)
16
+ @steps[id].count = 1
17
+ end
18
+ end
19
+
20
+ def steps
21
+ @steps.collect{|id,step| step }
22
+ end
23
+
24
+ def each
25
+ @steps.each{|id, step| yield step }
26
+ end
27
+
28
+ def [](id)
29
+ @steps[id]
30
+ end
31
+
32
+ def length
33
+ @steps.length
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,45 @@
1
+ require 'cgi'
2
+ require 'stepdown/step_collection'
3
+
4
+ module Stepdown
5
+ class StepGroup
6
+ attr_reader :id, :regex, :use_count
7
+
8
+ def initialize(step)
9
+ @id = step.id
10
+ @regex = step.regex
11
+ @use_count = 0
12
+ @step_collection = Stepdown::StepCollection.new
13
+ end
14
+
15
+ def step_collection
16
+ @step_collection.sort
17
+ end
18
+
19
+ def add_step(step)
20
+ @step_collection.add_step(step.id, step.regex)
21
+ end
22
+
23
+ def add_steps(step_set)
24
+ step_set.each{|step| add_step(step)}
25
+ end
26
+
27
+ def update_use_count(num_steps)
28
+ @use_count += num_steps
29
+ end
30
+
31
+ def group_graph
32
+ base = "https://chart.googleapis.com/chart?cht=gv:dot&chl=graph{"
33
+ base += "a [label=\"#{CGI.escape(CGI.escapeHTML(@regex.inspect.to_s))}\"];"
34
+
35
+ step_collection[0..10].each do |step|
36
+
37
+ next if step.regex.nil?
38
+ base += "a--\"#{CGI.escape(CGI.escapeHTML(step.regex.inspect.to_s))}\" [weight=#{step.count}];"
39
+ #a [label=\"#{grouping.in_steps[0][:step].regex.inspect}\"]; a--b [penwidth=3,weight=2];b--d}"
40
+ end
41
+ base += "}"
42
+ base
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ require 'stepdown/step_collection'
2
+
3
+ module Stepdown
4
+ class StepInstance
5
+ def initialize
6
+ @steps = []
7
+ end
8
+
9
+ def Given(regex,&block)
10
+ define_step(regex,&block)
11
+ end
12
+
13
+ def When(regex,&block)
14
+ define_step(regex,&block)
15
+ end
16
+
17
+ def Then(regex,&block)
18
+ define_step(regex,&block)
19
+ end
20
+
21
+ def define_step(regex,&block)
22
+ @steps << regex
23
+ end
24
+
25
+ def self.method_missing(*args)
26
+ #nothing
27
+ end
28
+
29
+ def method_missing(*args)
30
+ #nothing
31
+ end
32
+
33
+ def self.const_missing(*args)
34
+ self
35
+ end
36
+
37
+ def require(*args)
38
+ # do nothing
39
+ end
40
+
41
+ def line_matches(line)
42
+ stripped_line = line.strip.gsub(/^(And|Given|When|Then) (.*)$/,'\2')
43
+
44
+ @steps.each_with_index do |regex,i|
45
+ match = regex.match(stripped_line)
46
+ if match
47
+ return step_collection.detect{|step| i == step.id}
48
+ end
49
+ end
50
+
51
+ return nil
52
+ end
53
+
54
+ def step_collection
55
+ return @step_collection if @step_collection
56
+ @step_collection = StepCollection.new
57
+ @steps.each_with_index do |regex, i|
58
+ @step_collection.add_step(i, regex)
59
+ end
60
+ @step_collection
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+
2
+ module Stepdown
3
+ class StepUsage
4
+
5
+ attr_accessor :total_usage, :number_scenarios, :use_scenario
6
+ attr_reader :step
7
+
8
+ def initialize(step)
9
+ @step = step
10
+ @total_usage = 0
11
+ @number_scenarios = 0
12
+ @use_scenario = 0.0
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require 'stepdown/reporter'
2
+
3
+ module Stepdown
4
+ class TextReporter < Reporter
5
+
6
+ def output_overview
7
+ puts "Generating report..."
8
+ output = File.new(Reporter::OUTPUT_DIR + '/analysis.txt', "w+")
9
+
10
+ output.puts "Total number of scenarios: #{total_scenarios}"
11
+ output.puts "Total numer of steps: #{total_steps}"
12
+ output.puts "Steps per scenario: #{steps_per_scenario}"
13
+ output.puts "Unique steps per scenario: #{unique_steps}"
14
+
15
+ output.puts "Step usages"
16
+ output.puts "Step|Total usage|Scenarios|Use per scenario"
17
+ usages.each{|use| output.puts used_step_line(use) }
18
+
19
+ output.puts "Unused steps"
20
+ unused_steps.each{|use| output.puts unused_step_line(use) }
21
+
22
+ output.close
23
+
24
+ puts "Report output to #{Reporter::OUTPUT_DIR}/analysis.txt"
25
+
26
+ end
27
+
28
+ def used_step_line(use)
29
+ line = [use.step.regex.inspect, use.total_usage, use.number_scenarios, use.use_scenario]
30
+ line.join("|")
31
+ end
32
+
33
+ def unused_step_line(use)
34
+ use.step.regex.inspect
35
+ end
36
+
37
+ end
38
+ end
@@ -1,7 +1,8 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../lib/scenario')
2
- require File.expand_path(File.dirname(__FILE__) + '/../../lib/feature_parser')
1
+ require 'spec_helper'
2
+ require 'scenario'
3
+ require 'feature_parser'
3
4
 
4
- describe FeatureParser do
5
+ describe Stepdown::FeatureParser do
5
6
  def stub_line_match_with(instance, line, value)
6
7
  instance.stub!(:line_matches).with(line).and_return(value)
7
8
  end
@@ -15,10 +16,10 @@ describe FeatureParser do
15
16
  instance = mock("instance")
16
17
  file_lines = ["Scenario: My testing scenario"]
17
18
 
18
- @parser = FeatureParser.new
19
+ @parser = Stepdown::FeatureParser.new
19
20
  @parser.should_receive(:read_feature_file).with(file).and_return(file_lines)
20
21
  scenario = mock("scenario")
21
- Scenario.should_receive(:new).and_return(scenario)
22
+ Stepdown::Scenario.should_receive(:new).and_return(scenario)
22
23
 
23
24
  @parser.process_feature(file, instance).should =~ [scenario]
24
25
  end
@@ -28,10 +29,10 @@ describe FeatureParser do
28
29
  instance = mock("instance")
29
30
  file_lines = ["Background: My testing scenario"]
30
31
 
31
- @parser = FeatureParser.new
32
+ @parser = Stepdown::FeatureParser.new
32
33
  @parser.should_receive(:read_feature_file).with(file).and_return(file_lines)
33
34
  scenario = mock("scenario")
34
- Scenario.should_receive(:new).and_return(scenario)
35
+ Stepdown::Scenario.should_receive(:new).and_return(scenario)
35
36
 
36
37
  @parser.process_feature(file, instance).should =~ [scenario]
37
38
  end
@@ -40,7 +41,7 @@ describe FeatureParser do
40
41
 
41
42
  describe "parsing step lines" do
42
43
  before :each do
43
- @parser = FeatureParser.new
44
+ @parser = Stepdown::FeatureParser.new
44
45
  @step_instance = mock("step_instance")
45
46
 
46
47
  end
@@ -50,7 +51,7 @@ describe FeatureParser do
50
51
  unmatched_lines = ["not matched", "not matched 2"]
51
52
  steps = []
52
53
  lines.each_with_index do |line, i|
53
- step = Step.new(i, line)
54
+ step = Stepdown::Step.new(i, line)
54
55
  stub_line_match_with(@step_instance, line, step)
55
56
  steps << step
56
57
  end
@@ -63,21 +64,21 @@ describe FeatureParser do
63
64
  @parser.should_receive(:read_feature_file).and_return(all_lines)
64
65
 
65
66
  scenarios = @parser.process_feature(mock('file'), @step_instance)
66
- scenarios.first.steps.should =~ steps[1..2]
67
+ scenarios.first.steps.collect{|s| s.regex }.should =~ ["matched", "match 2"]
67
68
  end
68
69
 
69
70
  it "should add matched steps" do
70
71
  lines = ["Scenario", "matched", "match 2"]
71
72
  steps = []
72
73
  lines.each_with_index do |line, i|
73
- step = Step.new(i, line)
74
+ step = Stepdown::Step.new(i, line)
74
75
  stub_line_match_with(@step_instance, line, step)
75
76
  steps << step
76
77
  end
77
78
  @parser.should_receive(:read_feature_file).and_return(lines)
78
79
 
79
80
  scenarios = @parser.process_feature(mock('file'), @step_instance)
80
- scenarios.first.steps.should =~ steps[1..2]
81
+ scenarios.first.steps.collect{|s| s.regex }.should =~ ["matched", "match 2"]
81
82
 
82
83
  end
83
84
 
@@ -1,10 +1,10 @@
1
- require 'rspec'
2
- require File.expand_path(File.dirname(__FILE__) + '/../../lib/options')
1
+ require 'spec_helper'
2
+ require 'options'
3
3
 
4
- describe @options do
4
+ describe Stepdown::Options do
5
5
 
6
6
  before :each do
7
- @options = Options.new
7
+ @options = Stepdown::Options.new
8
8
  end
9
9
 
10
10
  describe "setting input directories" do
@@ -66,9 +66,27 @@ describe @options do
66
66
  end
67
67
 
68
68
  describe "validating options" do
69
- it "should report an invalid steps directory"
70
- it "should report an invalid features directory"
69
+ require 'stringio'
70
+ before :each do
71
+ Dir.stub!(:pwd).and_return("")
72
+ @io = StringIO.new
73
+ end
74
+
75
+ it "should report an invalid steps directory" do
76
+ @options.parse(["--steps=steps_dir"])
77
+ lambda do
78
+ @options.validate(@io)
79
+ @io.string.should == "Directory steps_dir does not exist"
80
+ end.should raise_error(SystemExit)
81
+ end
71
82
 
83
+ it "should report an invalid features directory" do
84
+ @options.parse(["--features=features_dir"])
85
+ lambda do
86
+ @options.validate(@io)
87
+ @io.string.should == "Directory features_dir does not exist"
88
+ end.should raise_error(SystemExit)
89
+ end
72
90
  end
73
91
 
74
92
  end
@@ -0,0 +1,184 @@
1
+ require 'stepdown/step_group'
2
+ require 'stepdown/step_usage'
3
+ require 'stepdown/reporter'
4
+ require 'stepdown/scenario'
5
+
6
+ describe Stepdown::Reporter do
7
+
8
+ describe "returning overall statistics" do
9
+ it "should return the total number of steps" do
10
+ steps = [mock('step_1'), mock('step_2')]
11
+ reporter = Stepdown::Reporter.new([], steps)
12
+ reporter.total_steps.should == 2
13
+ end
14
+
15
+ it "should return the total number of scenarios" do
16
+ scenarios = [mock('scenario_1'), mock('scenario_2')]
17
+ reporter = Stepdown::Reporter.new(scenarios, [])
18
+ reporter.total_scenarios.should == 2
19
+ end
20
+
21
+ it "should return the number of steps per scenario" do
22
+ steps = [mock('step_1'), mock('step_2'), mock('step_3')]
23
+ scenario1 = mock("scenario1", :steps => steps, :step_count => steps.length)
24
+ scenario2 = mock("scenario2", :steps => [], :step_count => 0)
25
+
26
+ reporter = Stepdown::Reporter.new([scenario1, scenario2], [])
27
+ reporter.steps_per_scenario.should == "1.50"
28
+ end
29
+
30
+ it "should return the number of unique steps per scenario" do
31
+ steps = [mock('step_1'), mock('step_2'), mock('step_3')]
32
+ scenario1 = mock("scenario1", :steps => steps, :unique_step_count => 2, :step_count => 3)
33
+ scenario2 = mock("scenario2", :steps => steps[0...1], :unique_step_count => 1, :step_count => 1)
34
+
35
+ reporter = Stepdown::Reporter.new([scenario1, scenario2], [])
36
+ reporter.unique_steps.should == "1.33"
37
+ end
38
+
39
+ end
40
+
41
+ #this whole grouping thing needs to be refactored. Nasty.
42
+ describe "creating step groupings" do
43
+ before :each do
44
+ @scen_1 = Stepdown::Scenario.new
45
+ @scen_2 = Stepdown::Scenario.new
46
+ @scenarios = [@scen_1, @scen_2]
47
+
48
+ @collection = Stepdown::StepCollection.new
49
+ @s1 = Stepdown::Step.new(1, /step 1/)
50
+ @s2 = Stepdown::Step.new(2, /step 2/)
51
+ @s3 = Stepdown::Step.new(3, /step 3/)
52
+ @s4 = Stepdown::Step.new(4, /step 4/)
53
+ @s5 = Stepdown::Step.new(5, /step 5/)
54
+
55
+ @collection.add_step(@s1.id, @s1.regex)
56
+ @collection.add_step(@s2.id, @s2.regex)
57
+ @collection.add_step(@s3.id, @s3.regex)
58
+ @collection.add_step(@s4.id, @s4.regex)
59
+ @collection.add_step(@s5.id, @s5.regex)
60
+
61
+ @scen_1.add_step(@s1)
62
+ @scen_1.add_step(@s2)
63
+ @scen_1.add_step(@s3)
64
+ @scen_1.add_step(@s4)
65
+
66
+ @scen_2.add_step(@s1)
67
+ @scen_2.add_step(@s2)
68
+ @scen_2.add_step(@s1)
69
+ @scen_2.add_step(@s5)
70
+
71
+ end
72
+
73
+ it "should return the correct step grouping" do
74
+ reporter = Stepdown::Reporter.new([@scen_1, @scen_2], @collection)
75
+
76
+ reporter.groupings[0].step_collection.should =~ [@s1,@s2,@s3,@s4,@s5]
77
+ reporter.groupings[1].step_collection.should =~ [@s1,@s2,@s3,@s4,@s5]
78
+ reporter.groupings[2].step_collection.should =~ [@s1,@s2,@s5]
79
+ reporter.groupings[3].step_collection.should =~ [@s1,@s2,@s3,@s4]
80
+ reporter.groupings[4].step_collection.should =~ [@s1,@s2,@s3,@s4]
81
+
82
+ end
83
+
84
+ it "should return usage for steps across scenarios" do
85
+ reporter = Stepdown::Reporter.new([@scen_1, @scen_2], @collection)
86
+
87
+ group_1 = reporter.groupings.detect{|g| g.id == 1}
88
+ group_1.use_count.should == 8
89
+ end
90
+
91
+ it "should return usage for steps in scenarios with duplicated steps" do
92
+ reporter = Stepdown::Reporter.new([@scen_1, @scen_2], @collection)
93
+
94
+ group_5 = reporter.groupings.detect{|g| g.id == 5}
95
+ group_5.use_count.should == 4
96
+ end
97
+
98
+ end
99
+
100
+ #this usage thing needs to be refactored as well
101
+ describe "creating step usages" do
102
+ before :each do
103
+ @scen_1 = Stepdown::Scenario.new
104
+ @scen_2 = Stepdown::Scenario.new
105
+ @scenarios = [@scen_1, @scen_2]
106
+
107
+ @collection = Stepdown::StepCollection.new
108
+ @s1 = Stepdown::Step.new(1, /step 1/)
109
+ @s2 = Stepdown::Step.new(2, /step 2/)
110
+ @s3 = Stepdown::Step.new(3, /step 3/)
111
+ @s4 = Stepdown::Step.new(4, /step 4/)
112
+ @s5 = Stepdown::Step.new(5, /step 5/)
113
+
114
+ @collection.add_step(@s1.id, @s1.regex)
115
+ @collection.add_step(@s2.id, @s2.regex)
116
+ @collection.add_step(@s3.id, @s3.regex)
117
+ @collection.add_step(@s4.id, @s4.regex)
118
+ @collection.add_step(@s5.id, @s5.regex)
119
+
120
+ @scen_1.add_step(@s1)
121
+ @scen_1.add_step(@s2)
122
+ @scen_1.add_step(@s3)
123
+ @scen_1.add_step(@s4)
124
+
125
+ @scen_2.add_step(@s1)
126
+ @scen_2.add_step(@s2)
127
+ @scen_2.add_step(@s1)
128
+ @scen_2.add_step(@s5)
129
+ @scen_2.add_step(@s5)
130
+ end
131
+
132
+ it "should return the usage of across scenarios" do
133
+ reporter = Stepdown::Reporter.new([@scen_2, @scen_1], @collection)
134
+
135
+ usage = reporter.usages.detect{|use| use.step.id == 1}
136
+ usage.total_usage.should == 3
137
+ usage.number_scenarios.should == 2
138
+ usage.use_scenario.should == "1.50"
139
+ end
140
+
141
+ it "should return duplicate usage of a step in a scenario" do
142
+ reporter = Stepdown::Reporter.new([@scen_2, @scen_1], @collection)
143
+
144
+ usage = reporter.usages.detect{|use| use.step.id == 5}
145
+ usage.total_usage.should == 2
146
+ usage.number_scenarios.should == 1
147
+ usage.use_scenario.should == "2.00"
148
+ end
149
+
150
+ it "should return usage of a step in a scenario" do
151
+ reporter = Stepdown::Reporter.new([@scen_2, @scen_1], @collection)
152
+
153
+ usage = reporter.usages.detect{|use| use.step.id == 3}
154
+ usage.total_usage.should == 1
155
+ usage.number_scenarios.should == 1
156
+ usage.use_scenario.should == "1.00"
157
+ end
158
+
159
+ end
160
+
161
+ describe "returing step usage" do
162
+ before :each do
163
+ @reporter = Stepdown::Reporter.new([], mock('step_colllection'))
164
+
165
+ @use_1 = Stepdown::StepUsage.new(Stepdown::Step.new(1,/regex/))
166
+ @use_2 = Stepdown::StepUsage.new(Stepdown::Step.new(2,/regex/))
167
+ @use_3 = Stepdown::StepUsage.new(Stepdown::Step.new(3,/regex/))
168
+
169
+ @use_1.total_usage += 1
170
+ @use_2.total_usage += 1
171
+
172
+ @reporter.stub!(:step_usages).and_return([@use_1, @use_2, @use_3])
173
+ end
174
+
175
+ it "should return unused steps" do
176
+ @reporter.usages.should =~ [@use_1, @use_2]
177
+ end
178
+
179
+ it "should return used steps" do
180
+ @reporter.unused_steps.should =~ [@use_3]
181
+ end
182
+ end
183
+
184
+ end