cucumber_statistics 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.document +5 -0
- data/.gitignore +19 -0
- data/.rvmrc +3 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +46 -0
- data/README.md +44 -0
- data/Rakefile +7 -0
- data/cucumber_statistics.gemspec +38 -0
- data/lib/cucumber_statistics.rb +6 -0
- data/lib/cucumber_statistics/autoload.rb +3 -0
- data/lib/cucumber_statistics/configuration.rb +57 -0
- data/lib/cucumber_statistics/formatter.rb +68 -0
- data/lib/cucumber_statistics/overall_statistics.rb +29 -0
- data/lib/cucumber_statistics/renderer.rb +25 -0
- data/lib/cucumber_statistics/renderer_helper.rb +73 -0
- data/lib/cucumber_statistics/step_statistics.rb +91 -0
- data/lib/cucumber_statistics/unused_steps.rb +18 -0
- data/lib/cucumber_statistics/version.rb +3 -0
- data/lib/cucumber_statistics/view/step_statistics.html +162 -0
- data/lib/cucumber_statistics/view/step_statistics.html.haml +145 -0
- data/notes.txt +9 -0
- data/spec/cucumber_statistics/configuration_spec.rb +20 -0
- data/spec/cucumber_statistics/renderer_helper_spec.rb +27 -0
- data/spec/cucumber_statistics/renderer_spec.rb +50 -0
- data/spec/cucumber_statistics/step_statistics_spec.rb +199 -0
- data/spec/cucumber_statistics/unused_steps_spec.rb +39 -0
- data/spec/spec_helper.rb +11 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NWFjYzc0M2VmMzkzOTI2MGZkMzAyNWFjMDM5ODVmOTc2NDRkMWI5Yw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YjhjZTczMmMwYjEyYTc0Y2M4ZTI4YTZhNDQxMjc5NWJkOGI2Njc0MQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDFkNGUyNjNmZGI2ZjdkZGIyYTI4MTUwOTNhNjBjNmNkY2I0NWY5NjhjODgx
|
10
|
+
NjIyODQ1NzRjNzI3MzE1MTdmNWMzMGU2NWZjMWMwMGUyYjQyY2I0OTBmYTkz
|
11
|
+
MzZjMTQ0YWE2OTRmOGVkNzA1NTIwNzg2MjBkM2Q0ZmNhYzk0YTU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjViMTU4NTdjYWVjY2I1MjY1MmNkMzgyODljN2Q0MGQ1ZmJmM2RhY2Y2NWJi
|
14
|
+
Yjc2OTlmZGUwYTViZmMxYjZlN2Y5NGQxNjY1YzYyOWRiNDk5NDhmOWM4Yzc2
|
15
|
+
ZDU1MDFhNjcwODVkNDg3YWRmMGIyZjU5YWE1Y2U0NTk1YTA5Nzk=
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
Copyright (c) 2014 AlienFast, LLC
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
-----------------------------------------------------------------------------------------------------------------------
|
25
|
+
Some code originally authored by Ryan Boucher https://github.com/distributedlife/cucumber_timing_presenter
|
26
|
+
|
27
|
+
Copyright (c) 2012 Ryan Boucher
|
28
|
+
|
29
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
30
|
+
a copy of this software and associated documentation files (the
|
31
|
+
"Software"), to deal in the Software without restriction, including
|
32
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
33
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
34
|
+
permit persons to whom the Software is furnished to do so, subject to
|
35
|
+
the following conditions:
|
36
|
+
|
37
|
+
The above copyright notice and this permission notice shall be
|
38
|
+
included in all copies or substantial portions of the Software.
|
39
|
+
|
40
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
41
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
42
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
43
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
44
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
45
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
46
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Cucumber Statistics
|
2
|
+
|
3
|
+
A cucumber formatter that will gather statistics and generate a single page showing step time metrics.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
1. Add `gem 'cucumber_statistics'` to your `Gemfile`
|
8
|
+
|
9
|
+
2. Or `gem install 'cucumber_statistics`
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
|
13
|
+
1. For always-on automatic loading (recommended), add `require 'cucumber_statistics/autoload'` to `features/support/env.rb` or other support file.
|
14
|
+
|
15
|
+
2. Or, add it to your `cucumber.yml` by adding `--format CucumberStatistics::Formatter` i.e.
|
16
|
+
|
17
|
+
`std_opts = "-r features/support/ -r features/step_definitions --quiet --format CucumberStatistics::Formatter --format progress --format junit -o test-reports --strict --tags ~@wip --tags ~@todo"`
|
18
|
+
|
19
|
+
3. Or, use it via command line with the `--format CucumberStatistics::Formatter` option.
|
20
|
+
|
21
|
+
## Results
|
22
|
+
|
23
|
+
Look in the `./tmp/cucumber_statistics` for the generated html document.
|
24
|
+
|
25
|
+
## Why?
|
26
|
+
|
27
|
+
It should be fast and easy to find long running steps. This generates a bootstrap styled page with a sortable table, where the outliers are clearly identified. It should be fast and easy to diagnose problems.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Please contribute!
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
38
|
+
|
39
|
+
## Credits
|
40
|
+
Credit to Ryan Boucher [cucumber_timing_presenter](https://github.com/distributedlife/cucumber_timing_presenter) for the original code used to gather statistics.
|
41
|
+
|
42
|
+
## Copyright
|
43
|
+
|
44
|
+
Copyright (c) 2014 AlienFast. See [LICENSE.txt](https://github.com/alienfast/cucumber_statistics/blob/master/LICENSE.txt) for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cucumber_statistics/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cucumber_statistics"
|
8
|
+
spec.version = CucumberStatistics::VERSION
|
9
|
+
spec.authors = ["Kevin Ross"]
|
10
|
+
spec.email = ["kevin.ross@alienfast.com.com"]
|
11
|
+
spec.summary = <<-TEXT
|
12
|
+
An cucumber formatter that will gather statistics and generate a single page showing step time metrics.
|
13
|
+
TEXT
|
14
|
+
spec.description = <<-TEXT
|
15
|
+
Want to know what is slowing down your build?
|
16
|
+
TEXT
|
17
|
+
|
18
|
+
|
19
|
+
spec.homepage = "http://github.com/alienfast/cucumber_statistics"
|
20
|
+
spec.license = 'MIT'
|
21
|
+
|
22
|
+
spec.files = `git ls-files`.split($/).reject { |f| f =~ /^samples\// }
|
23
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
|
27
|
+
# development
|
28
|
+
#spec.add_development_dependency 'cucumber'
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
30
|
+
spec.add_development_dependency 'rake'
|
31
|
+
spec.add_development_dependency 'rspec', '>= 2.14.1'
|
32
|
+
|
33
|
+
# runtime
|
34
|
+
spec.add_runtime_dependency 'haml'
|
35
|
+
spec.add_runtime_dependency 'tilt'
|
36
|
+
spec.add_runtime_dependency 'cucumber'
|
37
|
+
spec.add_runtime_dependency 'virtus'
|
38
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'cucumber_statistics/configuration'
|
2
|
+
require 'cucumber_statistics/step_statistics'
|
3
|
+
require 'cucumber_statistics/overall_statistics'
|
4
|
+
require 'cucumber_statistics/unused_steps'
|
5
|
+
require 'cucumber_statistics/formatter'
|
6
|
+
require 'cucumber_statistics/renderer'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module CucumberStatistics
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
$tmp_path = 'tmp/cucumber_statistics'
|
9
|
+
|
10
|
+
def clean_tmp_dir
|
11
|
+
FileUtils.rm_r tmp_dir
|
12
|
+
end
|
13
|
+
|
14
|
+
def tmp_dir
|
15
|
+
dir = resolve_path_from_root $tmp_path
|
16
|
+
FileUtils.mkdir_p dir unless File.exists? dir
|
17
|
+
|
18
|
+
dir
|
19
|
+
end
|
20
|
+
|
21
|
+
def tmp_file(filename)
|
22
|
+
"#{tmp_dir}/#{filename}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def result_step_statistics
|
26
|
+
tmp_file('step_statistics.html')
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def resolve_path_from_root(relative_path)
|
31
|
+
if defined?(Rails)
|
32
|
+
Rails.root.join(relative_path)
|
33
|
+
elsif defined?(Rake.original_dir)
|
34
|
+
File.expand_path(relative_path, Rake.original_dir)
|
35
|
+
else
|
36
|
+
File.expand_path(relative_path, Dir.pwd)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
#
|
40
|
+
#def all_usage_results
|
41
|
+
# File.expand_path('templates/all_usage_results.html', File.dirname(__FILE__))
|
42
|
+
#end
|
43
|
+
#
|
44
|
+
#def unused_steps
|
45
|
+
# File.expand_path('templates/unused_steps.html', File.dirname(__FILE__))
|
46
|
+
#end
|
47
|
+
#
|
48
|
+
#def step_times_of_whole
|
49
|
+
# File.expand_path('templates/step_times_of_whole.html', File.dirname(__FILE__))
|
50
|
+
#end
|
51
|
+
#
|
52
|
+
#def step_average_and_total
|
53
|
+
# File.expand_path('templates/step_average_and_total.html', File.dirname(__FILE__))
|
54
|
+
#end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module CucumberStatistics
|
2
|
+
class Formatter
|
3
|
+
def initialize(step_mother, io, options)
|
4
|
+
@step_mother = step_mother
|
5
|
+
@io = io
|
6
|
+
@options = options
|
7
|
+
|
8
|
+
@overall_statistics = OverallStatistics.new
|
9
|
+
@step_statistics = StepStatistics.new
|
10
|
+
@unused_steps = UnusedSteps.new
|
11
|
+
end
|
12
|
+
|
13
|
+
#----------------------------------------------------
|
14
|
+
# Step callbacks
|
15
|
+
#----------------------------------------------------
|
16
|
+
def before_step(step)
|
17
|
+
@step_start_time = Time.now
|
18
|
+
end
|
19
|
+
|
20
|
+
def before_step_result(*args)
|
21
|
+
@step_duration = Time.now - @step_start_time
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
|
25
|
+
|
26
|
+
step_definition = step_match.step_definition
|
27
|
+
unless step_definition.nil? # nil if it's from a scenario outline
|
28
|
+
@step_statistics.record step_definition.regexp_source, @step_duration, file_colon_line
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
#----------------------------------------------------
|
34
|
+
# Overall callbacks
|
35
|
+
#----------------------------------------------------
|
36
|
+
#def before_feature(feature)
|
37
|
+
#end
|
38
|
+
def scenario_name(keyword, name, file_colon_line, source_indent)
|
39
|
+
@overall_statistics.scenario_count_inc
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_step(step)
|
43
|
+
@overall_statistics.step_count_inc
|
44
|
+
end
|
45
|
+
|
46
|
+
def after_feature(feature)
|
47
|
+
@overall_statistics.feature_count_inc
|
48
|
+
end
|
49
|
+
|
50
|
+
def before_features(features)
|
51
|
+
@overall_statistics.start_time = Time.now
|
52
|
+
end
|
53
|
+
|
54
|
+
def after_features(features)
|
55
|
+
|
56
|
+
@overall_statistics.end_time = Time.now
|
57
|
+
|
58
|
+
# gather unused steps
|
59
|
+
@step_mother.unmatched_step_definitions.each do |step_definition|
|
60
|
+
@unused_steps.record step_definition.regexp_source, step_definition.file_colon_line
|
61
|
+
end
|
62
|
+
|
63
|
+
@step_statistics.calculate
|
64
|
+
|
65
|
+
Renderer.render_step_statistics @step_statistics, @overall_statistics
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
|
3
|
+
module CucumberStatistics
|
4
|
+
class OverallStatistics
|
5
|
+
include Virtus.model
|
6
|
+
|
7
|
+
attribute :start_time, Time
|
8
|
+
attribute :end_time, Time
|
9
|
+
attribute :feature_count, Integer, default: 0
|
10
|
+
attribute :scenario_count, Integer, default: 0
|
11
|
+
attribute :step_count, Integer, default: 0
|
12
|
+
|
13
|
+
def duration
|
14
|
+
end_time - start_time
|
15
|
+
end
|
16
|
+
|
17
|
+
def feature_count_inc
|
18
|
+
self.feature_count += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def scenario_count_inc
|
22
|
+
self.scenario_count += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def step_count_inc
|
26
|
+
self.step_count += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'haml'
|
2
|
+
require 'tilt/haml'
|
3
|
+
require 'cucumber_statistics/renderer_helper'
|
4
|
+
|
5
|
+
module CucumberStatistics
|
6
|
+
class Renderer
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
$renderer_helper ||=
|
11
|
+
|
12
|
+
def render_step_statistics(step_statistics, overall_statistics)
|
13
|
+
template = Tilt::HamlTemplate.new(File.expand_path('../view/step_statistics.html.haml', __FILE__))
|
14
|
+
rendered_content = template.render(RendererHelper.new, step_statistics: step_statistics, overall_statistics: overall_statistics)
|
15
|
+
|
16
|
+
absolute_file_name = Configuration.result_step_statistics
|
17
|
+
File.open(absolute_file_name, 'w') do |f|
|
18
|
+
f.write rendered_content
|
19
|
+
end
|
20
|
+
|
21
|
+
absolute_file_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module CucumberStatistics
|
2
|
+
|
3
|
+
class RendererHelper
|
4
|
+
|
5
|
+
def name_td(step_results)
|
6
|
+
%{<td title="#{step_results[1][:file]}">#{step_results[0]}</td>}
|
7
|
+
end
|
8
|
+
|
9
|
+
def time_td(step_results, metric, *warning_step_results)
|
10
|
+
duration = step_results[1][metric]
|
11
|
+
|
12
|
+
%{<td #{warning_class(step_results, warning_step_results)} data-value="#{duration}">#{format(duration)}</td>}
|
13
|
+
end
|
14
|
+
|
15
|
+
def warning_class(step_results, warning_step_results)
|
16
|
+
|
17
|
+
if warning_step_results.nil? || warning_step_results.empty?
|
18
|
+
should_warn = false
|
19
|
+
else
|
20
|
+
should_warn = (step_results[0].eql? warning_step_results[0][0])
|
21
|
+
end
|
22
|
+
if should_warn
|
23
|
+
%{class="danger"}
|
24
|
+
else
|
25
|
+
''
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def count_td(step_results, metric)
|
30
|
+
value = step_results[1][metric]
|
31
|
+
%{<td data-value="#{value}">#{value}</td>}
|
32
|
+
end
|
33
|
+
|
34
|
+
def format (ts)
|
35
|
+
|
36
|
+
return '-' if ts.nil? || ts == 0
|
37
|
+
|
38
|
+
#find the seconds
|
39
|
+
seconds = ts % 60
|
40
|
+
|
41
|
+
#find the minutes
|
42
|
+
minutes = (ts / 60) % 60
|
43
|
+
|
44
|
+
#find the hours
|
45
|
+
hours = (ts/3600)
|
46
|
+
|
47
|
+
formatted_h = hours.to_i
|
48
|
+
formatted_m = minutes.to_i
|
49
|
+
formatted_s = seconds.to_i
|
50
|
+
formatted_ms = Time.at(seconds).utc.strftime("%3N")
|
51
|
+
|
52
|
+
# http://apidock.com/ruby/DateTime/strftime
|
53
|
+
if hours >= 1
|
54
|
+
#result = Time.at(ts).utc.strftime("%Hh %Mm %S.%3Ns")
|
55
|
+
result = "#{formatted_h}h #{formatted_m}m #{formatted_s}.#{formatted_ms}s"
|
56
|
+
elsif minutes >= 1
|
57
|
+
#result = Time.at(ts).utc.strftime("%Mm %S.%3Ns")
|
58
|
+
result = "#{formatted_m}m #{formatted_s}.#{formatted_ms}s"
|
59
|
+
elsif formatted_ms.to_i == 0 && formatted_s == 0 && formatted_h == 0
|
60
|
+
result = "< #{formatted_s}.#{formatted_ms}s"
|
61
|
+
else
|
62
|
+
#result = Time.at(ts).utc.strftime("%S.%3Ns")
|
63
|
+
result = "#{formatted_s}.#{formatted_ms}s"
|
64
|
+
end
|
65
|
+
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def format_date_time (time)
|
70
|
+
time.strftime("%m/%d/%Y at %I:%M%p")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|