routes_coverage 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Appraisals +5 -0
- data/Gemfile +6 -3
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_40.gemfile +1 -1
- data/gemfiles/rails_40.gemfile.lock +1 -1
- data/gemfiles/rails_40_rspec.gemfile +1 -1
- data/gemfiles/rails_40_rspec.gemfile.lock +1 -1
- data/gemfiles/rails_40_simplecov.gemfile +8 -0
- data/gemfiles/rails_40_simplecov.gemfile.lock +97 -0
- data/gemfiles/rails_42.gemfile +1 -1
- data/gemfiles/rails_42.gemfile.lock +1 -1
- data/gemfiles/rails_5.gemfile +1 -1
- data/gemfiles/rails_5.gemfile.lock +1 -1
- data/gemfiles/rails_51.gemfile +1 -1
- data/gemfiles/rails_51.gemfile.lock +1 -1
- data/lib/routes_coverage/adapters/simplecov.rb +12 -0
- data/lib/routes_coverage/formatters/base.rb +15 -0
- data/lib/routes_coverage/formatters/full_text.rb +5 -26
- data/lib/routes_coverage/formatters/html_views/layout.erb +53 -0
- data/lib/routes_coverage/formatters/html_views/route_group.erb +48 -0
- data/lib/routes_coverage/formatters/simplecov_html.rb +97 -0
- data/lib/routes_coverage/formatters/summary_text.rb +17 -15
- data/lib/routes_coverage/middleware.rb +1 -5
- data/lib/routes_coverage/result.rb +64 -35
- data/lib/routes_coverage/version.rb +1 -1
- data/lib/routes_coverage.rb +61 -14
- data/routes_coverage.gemspec +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcc558504676f10b8f11d5c803288084c92b7c84
|
4
|
+
data.tar.gz: 063d639635c7c5b3fbd17396f129882559541cfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6dfb367c8a7f0487959ad3ef831cf057ef3a1ea3849583a89b0c52df259ffe58b27ad59a6ec03420d17bbd192f71fed14af2fdfe385de4298960f07db347060
|
7
|
+
data.tar.gz: a5308eb14d95cb0afb8a95221fad4c5ac7f6ef3897d159680ede52d1b6db6e51b77268e52992fa3c0e27986746c1adb823d0bfe687b20fc4bd85e9a6ca769047
|
data/.gitignore
CHANGED
data/Appraisals
CHANGED
data/Gemfile
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
#
|
4
|
-
|
3
|
+
#NB: gem's dependencies are in routes_coverage.gemspec
|
4
|
+
#NB: all other non-listed gems should go into Appraisals,
|
5
|
+
# this file is only for quick tests
|
5
6
|
|
6
|
-
#
|
7
|
+
# rails should be included before us
|
7
8
|
gem 'rails', '4.0.13'
|
9
|
+
|
10
|
+
gemspec
|
data/README.md
CHANGED
@@ -39,6 +39,14 @@ To get more detailed information somewhere in your test helper add
|
|
39
39
|
|
40
40
|
```ruby
|
41
41
|
RoutesCoverage.settings.format = :full_text
|
42
|
+
```
|
43
|
+
|
44
|
+
or into RSpec config:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
RSpec.configure do |config|
|
48
|
+
config.routes_coverage.format = :full_text
|
49
|
+
end
|
42
50
|
```
|
43
51
|
|
44
52
|
Routes coverage is 11.1% (1 of 9 routes hit at 1.0 hits average)
|
@@ -59,6 +67,41 @@ RoutesCoverage.settings.format = :full_text
|
|
59
67
|
PUT /reqs/:id(.:format) dummy#update
|
60
68
|
DELETE /reqs/:id(.:format) dummy#destroy
|
61
69
|
|
70
|
+
### Usage with SimpleCov
|
71
|
+
|
72
|
+
Use `RoutesCoverage.settings.format = :simplecov_html` along with simplecov to generate a html report like this:
|
73
|
+
|
74
|
+
![Html output example](/assets/html_output_screenshot.png?raw=true "Html Output example")
|
75
|
+
|
76
|
+
at the moment it shares styles with simplecov's one,
|
77
|
+
code coverage report does not need to be generated each time as long as you have `/coverage` directory with all the resources.
|
78
|
+
|
79
|
+
### Configuration
|
80
|
+
|
81
|
+
Set options in `RoutesCoverage.settings` or rspec's `config.routes_coverage`:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
RSpec.configure do |config|
|
85
|
+
config.routes_coverage.format = :full_text
|
86
|
+
config.routes_coverage.exclude_patterns << %r{PATCH /reqs} # excludes all requests matching regex
|
87
|
+
config.routes_coverage.exclude_namespaces << 'somenamespace' # excludes /somenamespace/*
|
88
|
+
config.routes_coverage.minimum_coverage = 80 # %, your coverage goal
|
89
|
+
config.routes_coverage.round_precision = 0 # just round to whole percents
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
or
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
RoutesCoverage.configure do |config|
|
97
|
+
config.format = :full_text
|
98
|
+
# ...
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
|
103
|
+
Excluded routes do not show in pending, but are shown if they're hit.
|
104
|
+
|
62
105
|
|
63
106
|
## Development
|
64
107
|
|
data/Rakefile
CHANGED
@@ -9,6 +9,12 @@ Rake::TestTask.new(:spec) do |t|
|
|
9
9
|
t.libs.push 'spec'
|
10
10
|
end
|
11
11
|
|
12
|
+
Rake::TestTask.new(:dummytest) do |t| t.pattern = 'spec/fixtures/dummy_test.rb' end
|
13
|
+
Rake::TestTask.new(:dummytest_full) do |t| t.pattern = 'spec/fixtures/dummy_test_full.rb' end
|
14
|
+
Rake::TestTask.new(:dummytest_filter) do |t| t.pattern = 'spec/fixtures/dummy_test_nsfilters.rb' end
|
15
|
+
Rake::TestTask.new(:dummytest_groups) do |t| t.pattern = 'spec/fixtures/dummy_test_groups.rb' end
|
16
|
+
|
17
|
+
|
12
18
|
task :default => :spec
|
13
19
|
|
14
20
|
$:.push File.expand_path("../lib", __FILE__)
|
data/gemfiles/rails_40.gemfile
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
routes_coverage (0.0.3)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actionmailer (4.0.13)
|
10
|
+
actionpack (= 4.0.13)
|
11
|
+
mail (~> 2.5, >= 2.5.4)
|
12
|
+
actionpack (4.0.13)
|
13
|
+
activesupport (= 4.0.13)
|
14
|
+
builder (~> 3.1.0)
|
15
|
+
erubis (~> 2.7.0)
|
16
|
+
rack (~> 1.5.2)
|
17
|
+
rack-test (~> 0.6.2)
|
18
|
+
activemodel (4.0.13)
|
19
|
+
activesupport (= 4.0.13)
|
20
|
+
builder (~> 3.1.0)
|
21
|
+
activerecord (4.0.13)
|
22
|
+
activemodel (= 4.0.13)
|
23
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
24
|
+
activesupport (= 4.0.13)
|
25
|
+
arel (~> 4.0.0)
|
26
|
+
activerecord-deprecated_finders (1.0.4)
|
27
|
+
activesupport (4.0.13)
|
28
|
+
i18n (~> 0.6, >= 0.6.9)
|
29
|
+
minitest (~> 4.2)
|
30
|
+
multi_json (~> 1.3)
|
31
|
+
thread_safe (~> 0.1)
|
32
|
+
tzinfo (~> 0.3.37)
|
33
|
+
appraisal (2.2.0)
|
34
|
+
bundler
|
35
|
+
rake
|
36
|
+
thor (>= 0.14.0)
|
37
|
+
arel (4.0.2)
|
38
|
+
builder (3.1.4)
|
39
|
+
concurrent-ruby (1.0.5)
|
40
|
+
docile (1.1.5)
|
41
|
+
erubis (2.7.0)
|
42
|
+
i18n (0.8.1)
|
43
|
+
json (1.8.6)
|
44
|
+
mail (2.6.5)
|
45
|
+
mime-types (>= 1.16, < 4)
|
46
|
+
mime-types (3.1)
|
47
|
+
mime-types-data (~> 3.2015)
|
48
|
+
mime-types-data (3.2016.0521)
|
49
|
+
minitest (4.7.5)
|
50
|
+
multi_json (1.12.1)
|
51
|
+
rack (1.5.5)
|
52
|
+
rack-test (0.6.3)
|
53
|
+
rack (>= 1.0)
|
54
|
+
rails (4.0.13)
|
55
|
+
actionmailer (= 4.0.13)
|
56
|
+
actionpack (= 4.0.13)
|
57
|
+
activerecord (= 4.0.13)
|
58
|
+
activesupport (= 4.0.13)
|
59
|
+
bundler (>= 1.3.0, < 2.0)
|
60
|
+
railties (= 4.0.13)
|
61
|
+
sprockets-rails (~> 2.0)
|
62
|
+
railties (4.0.13)
|
63
|
+
actionpack (= 4.0.13)
|
64
|
+
activesupport (= 4.0.13)
|
65
|
+
rake (>= 0.8.7)
|
66
|
+
thor (>= 0.18.1, < 2.0)
|
67
|
+
rake (10.5.0)
|
68
|
+
simplecov (0.14.1)
|
69
|
+
docile (~> 1.1.0)
|
70
|
+
json (>= 1.8, < 3)
|
71
|
+
simplecov-html (~> 0.10.0)
|
72
|
+
simplecov-html (0.10.0)
|
73
|
+
sprockets (3.7.1)
|
74
|
+
concurrent-ruby (~> 1.0)
|
75
|
+
rack (> 1, < 3)
|
76
|
+
sprockets-rails (2.3.3)
|
77
|
+
actionpack (>= 3.0)
|
78
|
+
activesupport (>= 3.0)
|
79
|
+
sprockets (>= 2.8, < 4.0)
|
80
|
+
thor (0.19.4)
|
81
|
+
thread_safe (0.3.6)
|
82
|
+
tzinfo (0.3.53)
|
83
|
+
|
84
|
+
PLATFORMS
|
85
|
+
ruby
|
86
|
+
|
87
|
+
DEPENDENCIES
|
88
|
+
appraisal
|
89
|
+
bundler (~> 1.14)
|
90
|
+
minitest
|
91
|
+
rails (~> 4.0.0)
|
92
|
+
rake (~> 10.0)
|
93
|
+
routes_coverage!
|
94
|
+
simplecov
|
95
|
+
|
96
|
+
BUNDLED WITH
|
97
|
+
1.14.6
|
data/gemfiles/rails_42.gemfile
CHANGED
data/gemfiles/rails_5.gemfile
CHANGED
data/gemfiles/rails_51.gemfile
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
module RoutesCoverage
|
2
|
+
module Formatters
|
3
|
+
class Base
|
4
|
+
def initialize result, groups, settings
|
5
|
+
@result = result
|
6
|
+
@groups = groups
|
7
|
+
@settings = settings
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :result
|
11
|
+
attr_reader :groups
|
12
|
+
attr_reader :settings
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,29 +1,7 @@
|
|
1
|
-
require 'active_support/core_ext/string' # needed for rails5 version of inspector
|
2
|
-
require 'action_dispatch/routing/inspector'
|
3
|
-
|
4
1
|
module RoutesCoverage
|
5
2
|
module Formatters
|
6
3
|
class FullText < SummaryText
|
7
4
|
|
8
|
-
class Inspector < ActionDispatch::Routing::RoutesInspector
|
9
|
-
def collect_routes(routes)
|
10
|
-
routes.collect do |route|
|
11
|
-
ActionDispatch::Routing::RouteWrapper.new(route)
|
12
|
-
end.reject do |route|
|
13
|
-
route.internal?
|
14
|
-
end.collect do |route|
|
15
|
-
collect_engine_routes(route)
|
16
|
-
|
17
|
-
{ name: route.name,
|
18
|
-
verb: route.verb,
|
19
|
-
path: route.path,
|
20
|
-
reqs: route.reqs,
|
21
|
-
# regexp: route.json_regexp, # removed, this is not present in rails5
|
22
|
-
original: route, # added
|
23
|
-
}
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
5
|
|
28
6
|
class RouteFormatter < ActionDispatch::Routing::ConsoleFormatter
|
29
7
|
|
@@ -50,7 +28,7 @@ module RoutesCoverage
|
|
50
28
|
if @output_hits
|
51
29
|
# hits = " ?"
|
52
30
|
original_route = r[:original].__getobj__ # SimpleDelegator
|
53
|
-
hits = " #{@result.
|
31
|
+
hits = " #{@result.route_hit_counts[original_route]}"
|
54
32
|
end
|
55
33
|
"#{r[:name].rjust(name_width) if @output_prefix} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs].ljust(reqs_width)}#{hits}"
|
56
34
|
end
|
@@ -83,15 +61,16 @@ module RoutesCoverage
|
|
83
61
|
# routes.map do |r|
|
84
62
|
# "#{r.verb.ljust(verb_width)} #{r.path.ljust(path_width)} #{r.reqs}"
|
85
63
|
# end
|
86
|
-
|
64
|
+
#TODO: replace formatter
|
65
|
+
Result::Inspector.new(routes).format(RouteFormatter.new(result, settings, true)),
|
87
66
|
nil,
|
88
67
|
"Pending routes:",
|
89
|
-
Inspector.new(result.pending_routes).format(RouteFormatter.new),
|
68
|
+
Result::Inspector.new(result.pending_routes).format(RouteFormatter.new),
|
90
69
|
].flatten.join("\n")
|
91
70
|
end
|
92
71
|
|
93
72
|
def format
|
94
|
-
"
|
73
|
+
"#{super}\n\n#{hit_routes}"
|
95
74
|
end
|
96
75
|
end
|
97
76
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html xmlns='http://www.w3.org/1999/xhtml'>
|
3
|
+
<head>
|
4
|
+
<title>Code coverage for <%= SimpleCov.project_name %></title>
|
5
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
6
|
+
<script src='<%= assets_path('application.js') %>' type='text/javascript'></script>
|
7
|
+
<link href='<%= assets_path('application.css') %>' media='screen, projection, print' rel='stylesheet' type='text/css'>
|
8
|
+
<link rel="shortcut icon" type="image/png" href="<%= assets_path("favicon_#{coverage_css_class(result.coverage)}.png") %>" />
|
9
|
+
<link rel="icon" type="image/png" href="<%= assets_path('favicon.png') %>" />
|
10
|
+
|
11
|
+
<script>
|
12
|
+
$(document).ready(function() {
|
13
|
+
$('.route_list').dataTable({
|
14
|
+
"aaSorting": [], //[[ 1, "asc" ]],
|
15
|
+
"bPaginate": false,
|
16
|
+
"bJQueryUI": true,
|
17
|
+
"aoColumns": [
|
18
|
+
null, //name
|
19
|
+
null, //verb
|
20
|
+
null, //path
|
21
|
+
null, //action
|
22
|
+
null, //covered yes/no
|
23
|
+
null, //hits count
|
24
|
+
]
|
25
|
+
});
|
26
|
+
});
|
27
|
+
</script>
|
28
|
+
</head>
|
29
|
+
|
30
|
+
<body>
|
31
|
+
<div id="loading">
|
32
|
+
Loading...
|
33
|
+
</div>
|
34
|
+
<div id="wrapper" style="display:none;">
|
35
|
+
<div class="timestamp">Generated <%= timeago(Time.now) %></div>
|
36
|
+
<ul class="group_tabs"></ul>
|
37
|
+
|
38
|
+
<div id="content">
|
39
|
+
<%= route_group_result("All Routes", result) %>
|
40
|
+
|
41
|
+
<% groups.each do |name, group_result| %>
|
42
|
+
<%= route_group_result(name, group_result) %>
|
43
|
+
<% end %>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div id="footer">
|
47
|
+
Generated by <a href="http://github.com/Vasfed/routes_coverage">routes_coverage</a> v<%= RoutesCoverage::VERSION %>
|
48
|
+
and simplecov-html v<%= SimpleCov::Formatter::HTMLFormatter::VERSION %><br/>
|
49
|
+
</div>
|
50
|
+
|
51
|
+
</div>
|
52
|
+
</body>
|
53
|
+
</html>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<div class="file_list_container" id="<%= title_id %>">
|
2
|
+
<h2>
|
3
|
+
<span class="group_name"><%= title %></span>
|
4
|
+
(<span class="covered_percent"><span class="<%= coverage_css_class(result.coverage.to_f) %>"><%= result.coverage %>%</span></span>
|
5
|
+
covered at
|
6
|
+
<span class="covered_strength">
|
7
|
+
<span class="<%#= strength_css_class(result.avg_hits) %>">
|
8
|
+
<%= result.avg_hits.round(2) %>
|
9
|
+
</span>
|
10
|
+
</span> hits/route)
|
11
|
+
</h2>
|
12
|
+
<a name="<%= title_id %>"></a>
|
13
|
+
<div>
|
14
|
+
<b><%= result.total_count %></b> routes in total.
|
15
|
+
<b><%= result.expected_routes_count %></b> relevant routes.
|
16
|
+
<span class="green"><b><%= result.hit_routes_count %></b> covered</span> and
|
17
|
+
<span class="red"><b><%= result.pending_routes.size %></b> missed </span>
|
18
|
+
</div>
|
19
|
+
<table class="route_list">
|
20
|
+
<thead>
|
21
|
+
<tr>
|
22
|
+
<th>Name</th>
|
23
|
+
<th>Verb</th>
|
24
|
+
<th>Path</th>
|
25
|
+
<th>Controller</th>
|
26
|
+
<th>Covered</th>
|
27
|
+
<th>Hits</th>
|
28
|
+
</tr>
|
29
|
+
</thead>
|
30
|
+
<tbody>
|
31
|
+
<% result.all_routes_with_hits.each do |route| %>
|
32
|
+
<tr>
|
33
|
+
<td class="strong"><%= route[:name] %></td>
|
34
|
+
<td class="strong"><%= route[:verb] %></td>
|
35
|
+
<td class="strong"><%= route[:path] %></td>
|
36
|
+
<td class="strong">
|
37
|
+
<% if route[:engine_name] %>
|
38
|
+
engine <%= route[:engine_name] %>
|
39
|
+
<% end %>
|
40
|
+
<%= route[:reqs] %>
|
41
|
+
</td>
|
42
|
+
<td class="strong <%= hits_css_class route[:hits] %>"><%= route[:hits] > 0 ? 'Yes' : 'No' %></td>
|
43
|
+
<td class="<%= hits_css_class route[:hits] %> strong"><%= route[:hits] %></td>
|
44
|
+
</tr>
|
45
|
+
<% end %>
|
46
|
+
</tbody>
|
47
|
+
</table>
|
48
|
+
</div>
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "cgi"
|
3
|
+
require "fileutils"
|
4
|
+
require "digest/sha1"
|
5
|
+
require "time"
|
6
|
+
|
7
|
+
module RoutesCoverage
|
8
|
+
module Formatters
|
9
|
+
class SimpleCovHtml < Base
|
10
|
+
def format
|
11
|
+
#TODO: copy assets, if simplecov does not generate a report along us
|
12
|
+
|
13
|
+
# Dir[File.join(File.dirname(__FILE__), "../public/*")].each do |path|
|
14
|
+
# FileUtils.cp_r(path, asset_output_path)
|
15
|
+
# end
|
16
|
+
|
17
|
+
routes_filename = "routes.html"
|
18
|
+
|
19
|
+
File.open(File.join(output_path, routes_filename), "wb") do |file|
|
20
|
+
file.puts template("layout").result(binding)
|
21
|
+
end
|
22
|
+
|
23
|
+
"Routes coverage is #{result.coverage}% Report generated to #{output_path}/#{routes_filename}"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def template(name)
|
29
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), "html_views", "#{name}.erb")))
|
30
|
+
end
|
31
|
+
|
32
|
+
def output_path
|
33
|
+
SimpleCov.coverage_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def asset_output_path
|
37
|
+
return @asset_output_path if defined?(@asset_output_path) && @asset_output_path
|
38
|
+
@asset_output_path = File.join(output_path, "assets", SimpleCov::Formatter::HTMLFormatter::VERSION)
|
39
|
+
FileUtils.mkdir_p(@asset_output_path)
|
40
|
+
@asset_output_path
|
41
|
+
end
|
42
|
+
|
43
|
+
def assets_path(name)
|
44
|
+
File.join("./assets", SimpleCov::Formatter::HTMLFormatter::VERSION, name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the html for the given source_file
|
48
|
+
def formatted_source_file(source_file)
|
49
|
+
template("source_file").result(binding)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a table containing the given source files
|
53
|
+
def route_group_result(title, result)
|
54
|
+
title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-\_]/, "")
|
55
|
+
# Silence a warning by using the following variable to assign to itself:
|
56
|
+
# "warning: possibly useless use of a variable in void context"
|
57
|
+
# The variable is used by ERB via binding.
|
58
|
+
title_id = title_id
|
59
|
+
template("route_group").result(binding)
|
60
|
+
end
|
61
|
+
|
62
|
+
def coverage_css_class(covered_percent)
|
63
|
+
if covered_percent > 90
|
64
|
+
"green"
|
65
|
+
elsif covered_percent > 80
|
66
|
+
"yellow"
|
67
|
+
else
|
68
|
+
"red"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def strength_css_class(covered_strength)
|
73
|
+
if covered_strength > 1
|
74
|
+
"green"
|
75
|
+
elsif covered_strength == 1
|
76
|
+
"yellow"
|
77
|
+
else
|
78
|
+
"red"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def hits_css_class hits
|
83
|
+
hits > 0 ? 'green' : 'red'
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return a (kind of) unique id for the source file given. Uses SHA1 on path for the id
|
87
|
+
def id(source_file)
|
88
|
+
Digest::SHA1.hexdigest(source_file.filename)
|
89
|
+
end
|
90
|
+
|
91
|
+
def timeago(time)
|
92
|
+
"<abbr class=\"timeago\" title=\"#{time.iso8601}\">#{time.iso8601}</abbr>"
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -1,29 +1,31 @@
|
|
1
1
|
module RoutesCoverage
|
2
2
|
module Formatters
|
3
|
-
class SummaryText
|
4
|
-
def
|
5
|
-
@result = result
|
6
|
-
@settings = settings
|
7
|
-
end
|
8
|
-
|
9
|
-
attr_reader :result
|
10
|
-
attr_reader :settings
|
11
|
-
|
12
|
-
def hits_count
|
3
|
+
class SummaryText < Base
|
4
|
+
def hits_count result
|
13
5
|
"#{result.hit_routes_count} of #{result.expected_routes_count}#{"(#{result.total_count} total)" if result.expected_routes_count != result.total_count} routes hit#{ " at #{result.avg_hits} hits average" if result.hit_routes_count > 0}"
|
14
6
|
end
|
15
7
|
|
16
8
|
def status
|
17
9
|
return unless settings.minimum_coverage
|
18
|
-
|
19
|
-
""
|
20
|
-
else
|
21
|
-
"\nCoverage failed. Need at least #{(settings.minimum_coverage / 100.0 * result.total_count).ceil}"
|
10
|
+
unless result.coverage_pass?
|
11
|
+
"Coverage is too low"
|
22
12
|
end
|
23
13
|
end
|
24
14
|
|
25
15
|
def format
|
26
|
-
|
16
|
+
buffer = [
|
17
|
+
"Routes coverage is #{result.coverage}% (#{hits_count result})"
|
18
|
+
]
|
19
|
+
|
20
|
+
if groups.any?
|
21
|
+
buffer += groups.map{|group_name, group_result|
|
22
|
+
" #{group_name}: #{group_result.coverage}% (#{hits_count group_result})"
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
buffer << status
|
27
|
+
|
28
|
+
buffer.compact.join("\n")
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
@@ -5,13 +5,9 @@ module RoutesCoverage
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def call env
|
8
|
-
# method = env["REQUEST_METHOD"]
|
9
|
-
# path = env["REQUEST_PATH"] || env["PATH_INFO"] || env["ORIGINAL_FULLPATH"]
|
10
|
-
# puts "req #{method} #{env["REQUEST_PATH"]} || #{env["ORIGINAL_FULLPATH"]} || #{env["PATH_INFO"]}"
|
11
|
-
# puts "env is #{env.inspect}"
|
12
8
|
req = ::Rails.application.routes.request_class.new env
|
13
9
|
::Rails.application.routes.router.recognize(req) do |route|
|
14
|
-
RoutesCoverage.
|
10
|
+
RoutesCoverage._touch_route(route)
|
15
11
|
end
|
16
12
|
#TODO: detect 404s? and maybe other route errors?
|
17
13
|
@app.call(env)
|
@@ -1,45 +1,33 @@
|
|
1
|
+
require 'active_support/core_ext/string' # needed for rails5 version of inspector
|
2
|
+
require 'action_dispatch/routing/inspector'
|
3
|
+
|
1
4
|
module RoutesCoverage
|
2
5
|
class Result
|
3
|
-
def initialize
|
4
|
-
@
|
6
|
+
def initialize all_routes, hit_routes, settings
|
7
|
+
@all_routes = all_routes
|
8
|
+
@route_hit_counts = hit_routes
|
9
|
+
@settings = settings
|
5
10
|
end
|
6
11
|
|
7
|
-
attr_reader :
|
8
|
-
|
9
|
-
def touch_route route
|
10
|
-
@route_hits[route] += 1
|
11
|
-
end
|
12
|
+
attr_reader :all_routes
|
12
13
|
|
13
|
-
|
14
|
-
::Rails.application.routes.routes.routes
|
15
|
-
end
|
14
|
+
attr_reader :route_hit_counts
|
16
15
|
|
17
16
|
def expected_routes
|
18
17
|
return @expected_routes if @expected_routes
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
if defined?(::Sprockets) && defined?(::Sprockets::Environment)
|
23
|
-
routes.reject!{|r| r.app.is_a?(::Sprockets::Environment) }
|
24
|
-
end
|
19
|
+
filter_regex = Regexp.union(@settings.exclude_patterns)
|
20
|
+
namespaces_regex = Regexp.union(@settings.exclude_namespaces.map{|n| /^\/#{n}/})
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
22
|
+
routes_groups = all_routes.group_by{|r|
|
23
|
+
!!(
|
24
|
+
("#{r.verb.to_s[8..-3]} #{r.path.spec}".strip =~ filter_regex) ||
|
25
|
+
(r.path.spec.to_s =~ namespaces_regex)
|
26
|
+
)
|
32
27
|
}
|
33
28
|
|
34
|
-
|
35
|
-
|
36
|
-
if r.path.spec.to_s =~ namespaces_regex
|
37
|
-
excluded_routes << r
|
38
|
-
end
|
39
|
-
}
|
40
|
-
|
41
|
-
@excluded_routes = excluded_routes
|
42
|
-
@expected_routes = routes
|
29
|
+
@excluded_routes = routes_groups[true] || []
|
30
|
+
@expected_routes = routes_groups[false] || []
|
43
31
|
end
|
44
32
|
|
45
33
|
def pending_routes
|
@@ -53,12 +41,12 @@ module RoutesCoverage
|
|
53
41
|
|
54
42
|
def hit_routes
|
55
43
|
#TODO: sort?
|
56
|
-
|
44
|
+
@route_hit_counts.keys
|
57
45
|
end
|
58
46
|
|
59
47
|
|
60
48
|
def hit_routes_count
|
61
|
-
|
49
|
+
@route_hit_counts.size
|
62
50
|
end
|
63
51
|
|
64
52
|
def expected_routes_count
|
@@ -75,15 +63,56 @@ module RoutesCoverage
|
|
75
63
|
|
76
64
|
def coverage
|
77
65
|
return 'n/a' unless expected_routes.any?
|
78
|
-
(hit_routes_count * 100.0 / expected_routes_count).round(
|
66
|
+
(hit_routes_count * 100.0 / expected_routes_count).round(@settings.round_precision)
|
79
67
|
end
|
80
68
|
|
81
69
|
def avg_hits
|
82
|
-
(
|
70
|
+
(@route_hit_counts.values.sum.to_f / hit_routes_count).round(@settings.round_precision)
|
83
71
|
end
|
84
72
|
|
85
73
|
def coverage_pass?
|
86
|
-
|
74
|
+
!@settings.minimum_coverage || (coverage.to_f >= @settings.minimum_coverage)
|
87
75
|
end
|
76
|
+
|
77
|
+
def all_routes_with_hits
|
78
|
+
res = Inspector.new(all_routes).collect_all_routes
|
79
|
+
res.each{|val|
|
80
|
+
val[:hits] = @route_hit_counts[val[:original]] || 0
|
81
|
+
}
|
82
|
+
res
|
83
|
+
end
|
84
|
+
|
85
|
+
class Inspector < ActionDispatch::Routing::RoutesInspector
|
86
|
+
def collect_all_routes
|
87
|
+
res = collect_routes(@routes)
|
88
|
+
#TODO: test with engines
|
89
|
+
@engines.each do |engine_name, engine_routes|
|
90
|
+
res += engine_routes.map{|er|
|
91
|
+
er.merge({ engine_name: engine_name })
|
92
|
+
}
|
93
|
+
end
|
94
|
+
res
|
95
|
+
end
|
96
|
+
|
97
|
+
def collect_routes(routes)
|
98
|
+
routes.collect do |route|
|
99
|
+
ActionDispatch::Routing::RouteWrapper.new(route)
|
100
|
+
end.reject do |route|
|
101
|
+
route.internal?
|
102
|
+
end.collect do |route|
|
103
|
+
collect_engine_routes(route)
|
104
|
+
|
105
|
+
{ name: route.name,
|
106
|
+
verb: route.verb,
|
107
|
+
path: route.path,
|
108
|
+
reqs: route.reqs,
|
109
|
+
# regexp: route.json_regexp, # removed, this is not present in rails5
|
110
|
+
# added:
|
111
|
+
original: route,
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
88
117
|
end
|
89
118
|
end
|
data/lib/routes_coverage.rb
CHANGED
@@ -2,8 +2,10 @@ require "routes_coverage/version"
|
|
2
2
|
require "routes_coverage/result"
|
3
3
|
require "routes_coverage/middleware"
|
4
4
|
|
5
|
+
require "routes_coverage/formatters/base"
|
5
6
|
require "routes_coverage/formatters/summary_text"
|
6
7
|
require "routes_coverage/formatters/full_text"
|
8
|
+
require "routes_coverage/formatters/simplecov_html"
|
7
9
|
|
8
10
|
module RoutesCoverage
|
9
11
|
class Railtie < ::Rails::Railtie
|
@@ -24,12 +26,30 @@ module RoutesCoverage
|
|
24
26
|
|
25
27
|
attr_accessor :format
|
26
28
|
|
29
|
+
attr_reader :groups
|
30
|
+
|
27
31
|
def initialize
|
28
32
|
@exclude_patterns = []
|
29
33
|
@exclude_namespaces = []
|
30
|
-
@minimum_coverage =
|
34
|
+
@minimum_coverage = 1
|
31
35
|
@round_precision = 1
|
32
36
|
@format = :summary_text
|
37
|
+
@groups = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def formatter_class
|
41
|
+
case format
|
42
|
+
when :full_text
|
43
|
+
Formatters::FullText
|
44
|
+
when :summary_text
|
45
|
+
Formatters::SummaryText
|
46
|
+
when :simplecov_html
|
47
|
+
Formatters::SimpleCovHtml
|
48
|
+
when Formatters::Base
|
49
|
+
format
|
50
|
+
else
|
51
|
+
raise "Unknown formatter #{settings.format.inspect}"
|
52
|
+
end
|
33
53
|
end
|
34
54
|
end
|
35
55
|
|
@@ -41,33 +61,60 @@ module RoutesCoverage
|
|
41
61
|
@@settings ||= Settings.new
|
42
62
|
end
|
43
63
|
|
44
|
-
|
64
|
+
def self.configure
|
65
|
+
yield self.settings
|
66
|
+
end
|
67
|
+
|
45
68
|
mattr_reader :pid
|
46
69
|
|
47
70
|
def self.reset!
|
48
|
-
@@
|
71
|
+
@@route_hit_count = Hash.new(0)
|
49
72
|
@@pid = Process.pid
|
50
73
|
end
|
51
74
|
|
52
75
|
def self.perform_report
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
when :summary_text
|
59
|
-
Formatters::SummaryText
|
60
|
-
else
|
61
|
-
raise "Unknown formatter #{settings.format.inspect}"
|
76
|
+
|
77
|
+
all_routes = ::Rails.application.routes.routes.routes.dup
|
78
|
+
|
79
|
+
if defined?(::Sprockets) && defined?(::Sprockets::Environment)
|
80
|
+
all_routes.reject!{|r| r.app.is_a?(::Sprockets::Environment) }
|
62
81
|
end
|
63
82
|
|
64
|
-
|
65
|
-
|
83
|
+
all_result = Result.new(
|
84
|
+
all_routes,
|
85
|
+
@@route_hit_count,
|
86
|
+
settings
|
87
|
+
)
|
88
|
+
|
89
|
+
|
90
|
+
groups = Hash[settings.groups.map{|group_name, regex|
|
91
|
+
[group_name,
|
92
|
+
Result.new(
|
93
|
+
all_routes.select{|r| r.path.spec.to_s =~ regex},
|
94
|
+
Hash[@@route_hit_count.select{|r,_hits| r.path.spec.to_s =~ regex}],
|
95
|
+
settings
|
96
|
+
)
|
97
|
+
]
|
98
|
+
}]
|
99
|
+
|
100
|
+
#TODO: group 'ungroupped'
|
101
|
+
|
102
|
+
puts
|
103
|
+
puts settings.formatter_class.new(all_result, groups, settings).format
|
66
104
|
end
|
67
105
|
|
106
|
+
|
107
|
+
def self._touch_route route
|
108
|
+
reset! unless @@route_hit_count
|
109
|
+
@@route_hit_count[route] += 1
|
110
|
+
end
|
68
111
|
end
|
69
112
|
|
70
113
|
if RoutesCoverage.enabled?
|
114
|
+
if defined? SimpleCov
|
115
|
+
#TODO: use SimpleCov.at_exit
|
116
|
+
end
|
117
|
+
|
71
118
|
if defined? RSpec
|
72
119
|
require "routes_coverage/adapters/rspec"
|
73
120
|
else
|
data/routes_coverage.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: routes_coverage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vasily Fedoseyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-05-
|
11
|
+
date: 2017-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -83,10 +83,13 @@ files:
|
|
83
83
|
- Rakefile
|
84
84
|
- bin/console
|
85
85
|
- bin/setup
|
86
|
+
- gemfiles/.bundle/config
|
86
87
|
- gemfiles/rails_40.gemfile
|
87
88
|
- gemfiles/rails_40.gemfile.lock
|
88
89
|
- gemfiles/rails_40_rspec.gemfile
|
89
90
|
- gemfiles/rails_40_rspec.gemfile.lock
|
91
|
+
- gemfiles/rails_40_simplecov.gemfile
|
92
|
+
- gemfiles/rails_40_simplecov.gemfile.lock
|
90
93
|
- gemfiles/rails_42.gemfile
|
91
94
|
- gemfiles/rails_42.gemfile.lock
|
92
95
|
- gemfiles/rails_5.gemfile
|
@@ -96,7 +99,12 @@ files:
|
|
96
99
|
- lib/routes_coverage.rb
|
97
100
|
- lib/routes_coverage/adapters/atexit.rb
|
98
101
|
- lib/routes_coverage/adapters/rspec.rb
|
102
|
+
- lib/routes_coverage/adapters/simplecov.rb
|
103
|
+
- lib/routes_coverage/formatters/base.rb
|
99
104
|
- lib/routes_coverage/formatters/full_text.rb
|
105
|
+
- lib/routes_coverage/formatters/html_views/layout.erb
|
106
|
+
- lib/routes_coverage/formatters/html_views/route_group.erb
|
107
|
+
- lib/routes_coverage/formatters/simplecov_html.rb
|
100
108
|
- lib/routes_coverage/formatters/summary_text.rb
|
101
109
|
- lib/routes_coverage/middleware.rb
|
102
110
|
- lib/routes_coverage/result.rb
|