routes_coverage 0.0.2 → 0.0.3
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.
- 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
|
+

|
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
|