stepdown 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -2
- data/Gemfile.lock +14 -10
- data/History.txt +30 -0
- data/README.rdoc +2 -2
- data/Rakefile +13 -0
- data/bin/stepdown +4 -4
- data/lib/stepdown.rb +6 -0
- data/lib/stepdown/analyzer.rb +67 -0
- data/lib/stepdown/feature_parser.rb +30 -0
- data/lib/stepdown/html_reporter.rb +41 -0
- data/lib/stepdown/options.rb +59 -0
- data/lib/stepdown/reporter.rb +106 -0
- data/lib/stepdown/scenario.rb +25 -0
- data/lib/stepdown/step.rb +22 -0
- data/lib/stepdown/step_collection.rb +36 -0
- data/lib/stepdown/step_group.rb +45 -0
- data/lib/stepdown/step_instance.rb +63 -0
- data/lib/stepdown/step_usage.rb +15 -0
- data/lib/stepdown/text_reporter.rb +38 -0
- data/spec/lib/{feature_parser_spec.rb → stepdown/feature_parser_spec.rb} +13 -12
- data/spec/lib/{options_spec.rb → stepdown/options_spec.rb} +24 -6
- data/spec/lib/stepdown/reporter_spec.rb +184 -0
- data/spec/lib/stepdown/scenario_spec.rb +40 -0
- data/spec/lib/stepdown/step_collection_spec.rb +78 -0
- data/spec/lib/stepdown/step_group_spec.rb +43 -0
- data/spec/lib/{step_instance_spec.rb → stepdown/step_instance_spec.rb} +13 -12
- data/spec/spec_helper.rb +4 -0
- data/stepdown.gemspec +7 -4
- data/templates/main.html.haml +3 -3
- data/templates/style.sass +5 -4
- metadata +64 -34
- data/lib/counting_step.rb +0 -14
- data/lib/feature_parser.rb +0 -32
- data/lib/html_reporter.rb +0 -37
- data/lib/options.rb +0 -69
- data/lib/reporter.rb +0 -62
- data/lib/scenario.rb +0 -19
- data/lib/step.rb +0 -10
- data/lib/step_down.rb +0 -112
- data/lib/step_group.rb +0 -52
- data/lib/step_instance.rb +0 -60
- data/lib/step_usage.rb +0 -13
- data/lib/text_reporter.rb +0 -36
- data/spec/lib/scenario_spec.rb +0 -42
- data/spec/lib/step_group_spec.rb +0 -119
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -2,20 +2,24 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
diff-lcs (1.1.2)
|
5
|
-
haml (
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
rspec-
|
10
|
-
|
11
|
-
|
5
|
+
haml (2.0.10)
|
6
|
+
rake (0.8.7)
|
7
|
+
rcov (0.9.9)
|
8
|
+
rspec (2.5.0)
|
9
|
+
rspec-core (~> 2.5.0)
|
10
|
+
rspec-expectations (~> 2.5.0)
|
11
|
+
rspec-mocks (~> 2.5.0)
|
12
|
+
rspec-core (2.5.1)
|
13
|
+
rspec-expectations (2.5.0)
|
12
14
|
diff-lcs (~> 1.1.2)
|
13
|
-
rspec-mocks (2.
|
15
|
+
rspec-mocks (2.5.0)
|
14
16
|
|
15
17
|
PLATFORMS
|
16
18
|
java
|
17
19
|
ruby
|
18
20
|
|
19
21
|
DEPENDENCIES
|
20
|
-
haml
|
21
|
-
|
22
|
+
haml (> 2.0.0)
|
23
|
+
rake
|
24
|
+
rcov (~> 0.9.9)
|
25
|
+
rspec (~> 2.5.0)
|
data/History.txt
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
== 0.4.0 2011-3-29
|
2
|
+
* Better HTML output
|
3
|
+
* Structured like other gems
|
4
|
+
* Maintainability improvements
|
5
|
+
* Better test coverage
|
6
|
+
|
7
|
+
== 0.3.3 2011-03-05
|
8
|
+
* Better HTML output
|
9
|
+
|
10
|
+
== 0.3.2 - 2011-02-27
|
11
|
+
* Bugfix in option parsing
|
12
|
+
* Bugfix for missing constants
|
13
|
+
|
14
|
+
== 0.3.1 - 2011-02-26
|
15
|
+
* Fix broken include [Robert Postill]
|
16
|
+
|
17
|
+
== 0.3.0 - 2011-02-26
|
18
|
+
* Added text report
|
19
|
+
* Bug fix for missing constants
|
20
|
+
|
21
|
+
== 0.2.1 - 2011-02-19
|
22
|
+
* Proper option parsing
|
23
|
+
|
24
|
+
== 0.2 - 2011-02-14
|
25
|
+
* Added some specs
|
26
|
+
* Improved output
|
27
|
+
* Updated usage message
|
28
|
+
|
29
|
+
== 0.1 - 2011-01-16
|
30
|
+
* Initial release
|
data/README.rdoc
CHANGED
@@ -17,5 +17,5 @@ Stepdown is available as a ruby gem
|
|
17
17
|
gem install stepdown
|
18
18
|
|
19
19
|
== Usage
|
20
|
-
stepdown <step definition directory> <feature file directory>
|
21
|
-
e.g. stepdown features/step_definitions features
|
20
|
+
stepdown --steps <step definition directory> --features <feature file directory>
|
21
|
+
e.g. stepdown --steps features/step_definitions --features features
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
|
3
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
4
|
+
t.fail_on_error = false
|
5
|
+
end
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
8
|
+
t.fail_on_error = false
|
9
|
+
t.rcov_opts = %w{ --exclude gems\/,spec\/,features\/}
|
10
|
+
t.rcov = true
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :spec
|
data/bin/stepdown
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
lib = File.expand_path('../lib/', __FILE__)
|
3
3
|
$:.unshift lib unless $:.include?(lib)
|
4
4
|
|
5
|
-
require '
|
6
|
-
require 'options'
|
5
|
+
require 'stepdown'
|
6
|
+
require 'stepdown/options'
|
7
7
|
|
8
8
|
begin
|
9
9
|
|
10
|
-
options = Options.new
|
10
|
+
options = Stepdown::Options.new
|
11
11
|
options.parse(ARGV)
|
12
12
|
options.validate
|
13
13
|
|
14
|
-
|
14
|
+
Stepdown::Analyzer.new(options.steps_dir, options.features_dir, options.reporter).analyse
|
15
15
|
|
16
16
|
rescue Interrupt => e
|
17
17
|
puts "Quiting..."
|
data/lib/stepdown.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stepdown
|
4
|
+
class Analyzer
|
5
|
+
def initialize(steps_dir, feature_dir, reporter)
|
6
|
+
@feature_dir = feature_dir
|
7
|
+
@steps_dir = steps_dir
|
8
|
+
@reporter = reporter
|
9
|
+
end
|
10
|
+
|
11
|
+
def analyse
|
12
|
+
puts "Parsing feature files..."
|
13
|
+
scenarios = process_feature_files(feature_files)
|
14
|
+
|
15
|
+
puts "Performing analysis..."
|
16
|
+
|
17
|
+
reporter = reporter(@reporter, scenarios, instance.step_collection)
|
18
|
+
reporter.output_overview
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def process_feature_files(feature_files)
|
23
|
+
parser = Stepdown::FeatureParser.new
|
24
|
+
|
25
|
+
scenarios = []
|
26
|
+
feature_files.each do |feature_file|
|
27
|
+
scenarios << parser.process_feature(feature_file, instance)
|
28
|
+
end
|
29
|
+
scenarios.flatten
|
30
|
+
end
|
31
|
+
|
32
|
+
def reporter(type, scenarios, step_collection)
|
33
|
+
case type
|
34
|
+
when "html"
|
35
|
+
Stepdown::HTMLReporter.new(scenarios, step_collection)
|
36
|
+
when "text"
|
37
|
+
Stepdown::TextReporter.new(scenarios, step_collection)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def step(id)
|
42
|
+
instance.steps.detect{|step| step.id == id}
|
43
|
+
end
|
44
|
+
|
45
|
+
def instance
|
46
|
+
@instance ||= begin
|
47
|
+
new_inst = Stepdown::StepInstance.new
|
48
|
+
|
49
|
+
Dir.glob(step_files).each do |file_name|
|
50
|
+
new_inst.instance_eval File.read(file_name)
|
51
|
+
end
|
52
|
+
new_inst
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def feature_files
|
58
|
+
return @feature_files if @feature_files
|
59
|
+
@feature_files = Dir.glob(@feature_dir + '/**/*.feature')
|
60
|
+
end
|
61
|
+
|
62
|
+
def step_files
|
63
|
+
return @step_files if @step_files
|
64
|
+
@step_files = Dir.glob(@steps_dir + '/**/*.rb')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'stepdown/step'
|
2
|
+
require 'stepdown/scenario'
|
3
|
+
module Stepdown
|
4
|
+
class FeatureParser
|
5
|
+
|
6
|
+
def process_feature(file, instance)
|
7
|
+
scenarios = []
|
8
|
+
file_lines = read_feature_file(file)
|
9
|
+
|
10
|
+
file_lines.each do |line|
|
11
|
+
|
12
|
+
if line =~ /Scenario|Background/
|
13
|
+
@scenario = Scenario.new
|
14
|
+
scenarios << @scenario
|
15
|
+
else
|
16
|
+
step = instance.line_matches(line)
|
17
|
+
@scenario.add_step(step) if step
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
scenarios
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def read_feature_file(file_name)
|
26
|
+
File.read(file_name).split("\n")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'stepdown/reporter'
|
3
|
+
require 'haml'
|
4
|
+
require 'sass'
|
5
|
+
|
6
|
+
module Stepdown
|
7
|
+
class HTMLReporter < Reporter
|
8
|
+
|
9
|
+
def output_overview()
|
10
|
+
puts "Generating report..."
|
11
|
+
FileUtils.mkdir_p(Reporter::OUTPUT_DIR)
|
12
|
+
copy_files
|
13
|
+
|
14
|
+
template = File.open(File.expand_path(File.dirname(__FILE__)) + '/../../templates/main.html.haml').read()
|
15
|
+
engine = Haml::Engine.new(template)
|
16
|
+
|
17
|
+
out = File.new(Reporter::OUTPUT_DIR + '/analysis.html','w+')
|
18
|
+
out.puts engine.render(self)
|
19
|
+
out.close
|
20
|
+
|
21
|
+
template = File.open(File.expand_path(File.dirname(__FILE__)) + '/../../templates/style.sass').read
|
22
|
+
sass_engine = Sass::Engine.new(template)
|
23
|
+
|
24
|
+
out = File.new(Reporter::OUTPUT_DIR + '/style.css', 'w+')
|
25
|
+
out.puts sass_engine.render
|
26
|
+
|
27
|
+
out.close
|
28
|
+
|
29
|
+
$stdout.puts "\nReport output to #{Reporter::OUTPUT_DIR}/analysis.html"
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def copy_files
|
35
|
+
['step_down.js', 'jquery-1.4.3.min.js'].each do |file|
|
36
|
+
src = File.expand_path("#{File.dirname(__FILE__)}/../../public/#{file}")
|
37
|
+
FileUtils.cp(src, File.join(OUTPUT_DIR, "#{file}"))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Stepdown
|
4
|
+
class Options
|
5
|
+
attr_reader :steps_dir, :features_dir, :reporter
|
6
|
+
|
7
|
+
OUTPUT_FORMATS = ["html", "text"]
|
8
|
+
|
9
|
+
def parse(params)
|
10
|
+
@steps_dir = "features/step_definitions"
|
11
|
+
@features_dir = "features"
|
12
|
+
@reporter = "html"
|
13
|
+
parser = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: stepdown step_definition_dir feature_file_directory"
|
15
|
+
|
16
|
+
opts.separator("")
|
17
|
+
|
18
|
+
opts.on("--output=TYPE", OUTPUT_FORMATS, "Select ouput format (#{OUTPUT_FORMATS.join(',')}). Default: html") do |o|
|
19
|
+
@reporter = o
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("--steps=STEPS_DIR", "Step definition directory. Default: ./features/step_definitions") do |o|
|
23
|
+
@steps_dir = o
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("--features=FEATURE_DIR", "Feature file directory. Default: ./features") do |o|
|
27
|
+
@features_dir = o
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on_tail("-h", "--help", "You're looking at it") do |o|
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
parser.parse(params)
|
39
|
+
|
40
|
+
rescue OptionParser::ParseError => e
|
41
|
+
puts e
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate(io = STDOUT)
|
47
|
+
@steps_dir = File.join(Dir.pwd, @steps_dir)
|
48
|
+
@features_dir = File.join(Dir.pwd, @features_dir)
|
49
|
+
|
50
|
+
[@steps_dir, @features_dir].each do |dir|
|
51
|
+
unless File.exists?(dir)
|
52
|
+
io.puts "Directory #{dir} does not exist"
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'stepdown/step_group'
|
2
|
+
require 'stepdown/step_usage'
|
3
|
+
|
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 uniq_steps_per_scenario(scenarios)
|
87
|
+
total_steps = 0.0
|
88
|
+
uniq_steps = 0.0
|
89
|
+
scenarios.each do |scen|
|
90
|
+
total_steps += scen.step_count
|
91
|
+
uniq_steps += scen.unique_step_count
|
92
|
+
end
|
93
|
+
sprintf "%.2f", (total_steps / uniq_steps)
|
94
|
+
end
|
95
|
+
|
96
|
+
def steps_scenario(scenarios)
|
97
|
+
scen_count = scenarios.length
|
98
|
+
step_count = 0.0
|
99
|
+
scenarios.each do |scenario|
|
100
|
+
step_count += scenario.step_count
|
101
|
+
end
|
102
|
+
sprintf "%.2f", (step_count / scen_count)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'stepdown/step_collection'
|
2
|
+
|
3
|
+
module Stepdown
|
4
|
+
class Scenario
|
5
|
+
attr_reader :step_count
|
6
|
+
def initialize
|
7
|
+
@step_collection = Stepdown::StepCollection.new
|
8
|
+
@step_count = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_step(step)
|
12
|
+
@step_count += 1
|
13
|
+
@step_collection.add_step(step.id, step.regex)
|
14
|
+
end
|
15
|
+
|
16
|
+
def steps
|
17
|
+
@step_collection.steps
|
18
|
+
end
|
19
|
+
|
20
|
+
def unique_step_count
|
21
|
+
@step_collection.length
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|