minitest-reporters 1.0.18 → 1.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +2 -1
- data/lib/minitest/reporters.rb +1 -0
- data/lib/minitest/reporters/html_reporter.rb +212 -0
- data/lib/minitest/reporters/version.rb +1 -1
- data/lib/minitest/templates/index.html.erb +83 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c79f88a67d61dda24ebe17a89046fe86046e101a
|
4
|
+
data.tar.gz: 26cec6b5fab44023ba5e4ceb71e9554ebc9a53ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d089e802175a8ba1d677f7e3710ca6f08771a94a4bbeb5d2bb83b6349b782ebde0c8ffe3546826f313a677e86aa87177e0e119ad7caeb5101c543a81c345296
|
7
|
+
data.tar.gz: ccf2cd2b9dc9e473c60d4b4be0ddb2b78b9118354eac249ba855e4045f222853a1ec6082952055926932b8cdf2afaee30effb468a9c9edee2ceaf7b170e69e0c
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/minitest-reporters
|
2
|
-
[travis]: https://travis-ci.org/
|
2
|
+
[travis]: https://travis-ci.org/kern/minitest-reporters
|
3
3
|
|
4
4
|
# minitest-reporters - create customizable Minitest output formats
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/minitest-reporters.svg)][gem]
|
data/Rakefile
CHANGED
data/lib/minitest/reporters.rb
CHANGED
@@ -15,6 +15,7 @@ module Minitest
|
|
15
15
|
autoload :RubyMateReporter, "minitest/reporters/ruby_mate_reporter"
|
16
16
|
autoload :RubyMineReporter, "minitest/reporters/rubymine_reporter"
|
17
17
|
autoload :JUnitReporter, "minitest/reporters/junit_reporter"
|
18
|
+
autoload :HtmlReporter, "minitest/reporters/html_reporter"
|
18
19
|
|
19
20
|
class << self
|
20
21
|
attr_accessor :reporters
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Minitest
|
6
|
+
module Reporters
|
7
|
+
# A reporter for generating HTML test reports
|
8
|
+
# This is recommended to be used with a CI server, where the report is kept as an artifact and is accessible via a shared link
|
9
|
+
#
|
10
|
+
# The reporter sorts the results alphabetically and then by results so that failing and skipped tests are at the top.
|
11
|
+
#
|
12
|
+
# When using Minitest Specs, the number prefix is dropped from the name of the test so that it reads well
|
13
|
+
#
|
14
|
+
# On each test run all files in the reports directory are deleted, this prevents a build up of old reports
|
15
|
+
#
|
16
|
+
# The report is generated using ERB. A custom ERB template can be provided but it is not required
|
17
|
+
# The default ERB template uses JQuery and Bootstrap, both of these are included by referencing the CDN sites
|
18
|
+
class HtmlReporter < BaseReporter
|
19
|
+
|
20
|
+
# The title of the report
|
21
|
+
attr_reader :title
|
22
|
+
|
23
|
+
# The number of tests that passed
|
24
|
+
def passes
|
25
|
+
count - failures - errors - skips
|
26
|
+
end
|
27
|
+
|
28
|
+
# The percentage of tests that passed, calculated in a way that avoids rounding errors
|
29
|
+
def percent_passes
|
30
|
+
100 - percent_skipps - percent_errors_failures
|
31
|
+
end
|
32
|
+
|
33
|
+
# The percentage of tests that were skipped
|
34
|
+
def percent_skipps
|
35
|
+
(skips/count.to_f * 100).to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
# The percentage of tests that failed
|
39
|
+
def percent_errors_failures
|
40
|
+
((errors+failures)/count.to_f * 100).to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
# Trims off the number prefix on test names when using Minitest Specs
|
44
|
+
def friendly_name(test)
|
45
|
+
groups = test.name.scan(/(test_\d+_)(.*)/i)
|
46
|
+
return test.name if groups.empty?
|
47
|
+
"it #{groups[0][1]}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# The constructor takes a hash, and uses the following keys:
|
51
|
+
# :title - the title that will be used in the report, defaults to 'Test Results'
|
52
|
+
# :reports_dir - the directory the reports should be written to, defaults to 'test/html_reports'
|
53
|
+
# :erb_template - the path to a custom ERB template, defaults to the supplied ERB template
|
54
|
+
# :mode - Useful for debugging, :terse suppresses errors and is the default, :verbose lets errors bubble up
|
55
|
+
def initialize(args = {})
|
56
|
+
super({})
|
57
|
+
|
58
|
+
defaults = {
|
59
|
+
:title => 'Test Results',
|
60
|
+
:erb_template => "#{File.dirname(__FILE__)}/../templates/index.html.erb",
|
61
|
+
:reports_dir => 'test/html_reports',
|
62
|
+
:mode => :safe
|
63
|
+
}
|
64
|
+
|
65
|
+
settings = defaults.merge(args)
|
66
|
+
|
67
|
+
@mode = settings[:mode]
|
68
|
+
@title = settings[:title]
|
69
|
+
@erb_template = settings[:erb_template]
|
70
|
+
reports_dir = settings[:reports_dir]
|
71
|
+
|
72
|
+
@reports_path = File.absolute_path(reports_dir)
|
73
|
+
|
74
|
+
puts "Emptying #{@reports_path}"
|
75
|
+
FileUtils.remove_dir(@reports_path) if File.exists?(@reports_path)
|
76
|
+
FileUtils.mkdir_p(@reports_path)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Called by the framework to generate the report
|
80
|
+
def report
|
81
|
+
super
|
82
|
+
|
83
|
+
begin
|
84
|
+
puts "Writing HTML reports to #{@reports_path}"
|
85
|
+
html_file = @reports_path + "/index.html"
|
86
|
+
erb_str = File.read(@erb_template)
|
87
|
+
renderer = ERB.new(erb_str)
|
88
|
+
|
89
|
+
tests_by_suites = tests.group_by(&:class) # taken from the JUnit reporter
|
90
|
+
|
91
|
+
suites = tests_by_suites.map do |suite, tests|
|
92
|
+
suite_summary = summarize_suite(suite, tests)
|
93
|
+
suite_summary[:tests] = tests.sort { |a, b| compare_tests(a, b) }
|
94
|
+
suite_summary
|
95
|
+
end
|
96
|
+
|
97
|
+
suites.sort! { |a, b| compare_suites(a, b) }
|
98
|
+
|
99
|
+
result = renderer.result(binding)
|
100
|
+
File.open(html_file, 'w') do |f|
|
101
|
+
f.write(result)
|
102
|
+
end
|
103
|
+
|
104
|
+
rescue Exception => e
|
105
|
+
puts 'There was an error writing the HTML report'
|
106
|
+
puts 'This may have been caused by cancelling the test run'
|
107
|
+
puts 'Use mode => :verbose in the HTML reporters constructor to see more detail' if @mode == :terse
|
108
|
+
puts 'Use mode => :terse in the HTML reporters constructor to see less detail' if @mode != :terse
|
109
|
+
raise e if @mode != :terse
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def compare_suites_by_name(suite_a, suite_b)
|
117
|
+
suite_a[:name] <=> suite_b[:name]
|
118
|
+
end
|
119
|
+
|
120
|
+
def compare_tests_by_name(test_a, test_b)
|
121
|
+
friendly_name(test_a) <=> friendly_name(test_b)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Test suites are first ordered by evaluating the results of the tests, then by test suite name
|
125
|
+
# Test suites which have failing tests are given highest order
|
126
|
+
# Tests suites which have skipped tests are given second highest priority
|
127
|
+
def compare_suites(suite_a, suite_b)
|
128
|
+
return compare_suites_by_name(suite_a, suite_b) if suite_a[:has_errors_or_failures] && suite_b[:has_errors_or_failures]
|
129
|
+
return -1 if suite_a[:has_errors_or_failures] && !suite_b[:has_errors_or_failures]
|
130
|
+
return 1 if !suite_a[:has_errors_or_failures] && suite_b[:has_errors_or_failures]
|
131
|
+
|
132
|
+
return compare_suites_by_name(suite_a, suite_b) if suite_a[:has_skipps] && suite_b[:has_skipps]
|
133
|
+
return -1 if suite_a[:has_skipps] && !suite_b[:has_skipps]
|
134
|
+
return 1 if !suite_a[:has_skipps] && suite_b[:has_skipps]
|
135
|
+
|
136
|
+
compare_suites_by_name(suite_a, suite_b)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Tests are first ordered by evaluating the results of the tests, then by tests names
|
140
|
+
# Tess which fail are given highest order
|
141
|
+
# Tests which are skipped are given second highest priority
|
142
|
+
def compare_tests(test_a, test_b)
|
143
|
+
return compare_tests_by_name(test_a, test_b) if test_fail_or_error?(test_a) && test_fail_or_error?(test_b)
|
144
|
+
|
145
|
+
return -1 if test_fail_or_error?(test_a) && !test_fail_or_error?(test_b)
|
146
|
+
return 1 if !test_fail_or_error?(test_a) && test_fail_or_error?(test_b)
|
147
|
+
|
148
|
+
return compare_tests_by_name(test_a, test_b) if test_a.skipped? && test_b.skipped?
|
149
|
+
return -1 if test_a.skipped? && !test_b.skipped?
|
150
|
+
return 1 if !test_a.skipped? && test_b.skipped?
|
151
|
+
|
152
|
+
compare_tests_by_name(test_a, test_b)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_fail_or_error?(test)
|
156
|
+
test.error? || test.failure
|
157
|
+
end
|
158
|
+
|
159
|
+
# based on analyze_suite from the JUnit reporter
|
160
|
+
def summarize_suite(suite, tests)
|
161
|
+
summary = Hash.new(0)
|
162
|
+
summary[:name] = suite.to_s
|
163
|
+
tests.each do |test|
|
164
|
+
summary[:"#{result(test)}_count"] += 1
|
165
|
+
summary[:assertion_count] += test.assertions
|
166
|
+
summary[:test_count] += 1
|
167
|
+
summary[:time] += test.time
|
168
|
+
end
|
169
|
+
summary[:has_errors_or_failures] = (summary[:fail_count] + summary[:error_count]) > 0
|
170
|
+
summary[:has_skipps] = summary[:skip_count] > 0
|
171
|
+
summary
|
172
|
+
end
|
173
|
+
|
174
|
+
# based on message_for(test) from the JUnit reporter
|
175
|
+
def message_for(test)
|
176
|
+
suite = test.class
|
177
|
+
name = test.name
|
178
|
+
e = test.failure
|
179
|
+
|
180
|
+
if test.passed?
|
181
|
+
nil
|
182
|
+
elsif test.skipped?
|
183
|
+
"Skipped:\n#{name}(#{suite}) [#{location(e)}]:\n#{e.message}\n"
|
184
|
+
elsif test.failure
|
185
|
+
"Failure:\n#{name}(#{suite}) [#{location(e)}]:\n#{e.message}\n"
|
186
|
+
elsif test.error?
|
187
|
+
"Error:\n#{name}(#{suite}):\n#{e.message}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# taken from the JUnit reporter
|
192
|
+
def location(exception)
|
193
|
+
last_before_assertion = ''
|
194
|
+
exception.backtrace.reverse_each do |s|
|
195
|
+
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
|
196
|
+
last_before_assertion = s
|
197
|
+
end
|
198
|
+
last_before_assertion.sub(/:in .*$/, '')
|
199
|
+
end
|
200
|
+
|
201
|
+
def total_time_to_hms
|
202
|
+
return ('%.2fs' % total_time) if total_time < 1
|
203
|
+
|
204
|
+
hours = total_time / (60 * 60)
|
205
|
+
minutes = ((total_time / 60) % 60).to_s.rjust(2,'0')
|
206
|
+
seconds = (total_time % 60).to_s.rjust(2,'0')
|
207
|
+
|
208
|
+
"#{ hours }h#{ minutes }m#{ seconds }s"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<title><%= title %></title>
|
5
|
+
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
|
6
|
+
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
7
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
8
|
+
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
|
9
|
+
</head>
|
10
|
+
|
11
|
+
<body>
|
12
|
+
<div class="container">
|
13
|
+
<div class="jumbotron">
|
14
|
+
<h1><%= title %></h1>
|
15
|
+
<p>
|
16
|
+
Finished in <%= total_time_to_hms %>, <%= '%.2f tests/s' % (count / total_time) %>, <%= '%.2f assertions/s' % (assertions / total_time) %>
|
17
|
+
</p>
|
18
|
+
<p>
|
19
|
+
<strong>
|
20
|
+
<span class="<%= 'text-info' if (failures == 0 && errors == 0) %>"><%= '%d' % count %> tests</span>,
|
21
|
+
<span class="<%= 'text-info' if (failures == 0 && errors == 0) %>"> <%= '%d' % assertions %> assertions</span>,
|
22
|
+
<span class="<%= 'text-danger' if failures > 0 %>"> <%= '%d' % failures %> failures</span>,
|
23
|
+
<span class="<%= 'text-danger' if errors > 0 %>"> <%= '%d' % errors %> errors</span>,
|
24
|
+
<span class="<%= 'text-warning' if skips > 0 %>"> <%= '%d' % skips %> skips</span>
|
25
|
+
</strong>
|
26
|
+
</p>
|
27
|
+
|
28
|
+
<div class="progress">
|
29
|
+
<div class="progress-bar progress-bar-info" style="width: <%= percent_passes %>%">
|
30
|
+
<%= '%d' % percent_passes %>% passed
|
31
|
+
</div>
|
32
|
+
<div class="progress-bar progress-bar-danger" style="width: <%= percent_errors_failures %>%">
|
33
|
+
<%= '%d' % percent_errors_failures %>% failed
|
34
|
+
</div>
|
35
|
+
<div class="progress-bar progress-bar-warning" style="width: <%= percent_skipps %>%">
|
36
|
+
<%= '%d' % percent_skipps %>% skipped
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<% suites.each do |suite| %>
|
44
|
+
<div class="panel panel-default">
|
45
|
+
<div class="panel-heading"><strong><%= suite[:name] %></strong>
|
46
|
+
<span class="pull-right">
|
47
|
+
<span class="<%= 'text-info' if (suite[:fail_count] == 0 && suite[:error_count] == 0) %>"><%= '%d' % suite[:test_count] %> tests</span>,
|
48
|
+
<span class="<%= 'text-info' if (failures == 0 && errors == 0) %>"> <%= '%d' % suite[:assertion_count] %> assertions</span>,
|
49
|
+
<span class="<%= 'text-danger' if suite[:fail_count] > 0 %>"> <%= '%d' % suite[:fail_count] %> failures</span>,
|
50
|
+
<span class="<%= 'text-danger' if suite[:error_count] > 0 %>"> <%= '%d' % suite[:error_count] %> errors</span>,
|
51
|
+
<span class="<%= 'text-warning' if suite[:skip_count] > 0 %>"> <%= '%d' % suite[:skip_count] %> skips</span>,
|
52
|
+
<span> finished in <%= '%.4fs' % suite[:time] %></span>
|
53
|
+
</span>
|
54
|
+
</div>
|
55
|
+
<div class="panel-body">
|
56
|
+
<div class="list-group">
|
57
|
+
<% suite[:tests].each do |test| %>
|
58
|
+
<div class="list-group-item">
|
59
|
+
<h5 class="list-group-item-heading">
|
60
|
+
<% if result(test) == :pass %>
|
61
|
+
<span class="glyphicon glyphicon-ok text-info" aria-hidden="true"></span>
|
62
|
+
<% elsif result(test) == :skip %>
|
63
|
+
<span class="glyphicon glyphicon-ban-circle text-warning" aria-hidden="true"></span>
|
64
|
+
<% else %>
|
65
|
+
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
|
66
|
+
<% end %>
|
67
|
+
<%= friendly_name(test) %>
|
68
|
+
<span class="pull-right">
|
69
|
+
Assertions <%= test.assertions %>, time <%= ('%.6fs' % test.time) %>
|
70
|
+
</span>
|
71
|
+
</h5>
|
72
|
+
<% if !test.passed? %>
|
73
|
+
<pre class="list-group-item-text"><%= "#{location(test.failure)}\n\n#{test.failure.message}" %></pre>
|
74
|
+
<% end %>
|
75
|
+
</div>
|
76
|
+
<% end %>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
<% end %>
|
81
|
+
</div>
|
82
|
+
</body>
|
83
|
+
</html>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minitest-reporters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Kern
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -120,12 +120,14 @@ files:
|
|
120
120
|
- lib/minitest/reporters/ansi.rb
|
121
121
|
- lib/minitest/reporters/base_reporter.rb
|
122
122
|
- lib/minitest/reporters/default_reporter.rb
|
123
|
+
- lib/minitest/reporters/html_reporter.rb
|
123
124
|
- lib/minitest/reporters/junit_reporter.rb
|
124
125
|
- lib/minitest/reporters/progress_reporter.rb
|
125
126
|
- lib/minitest/reporters/ruby_mate_reporter.rb
|
126
127
|
- lib/minitest/reporters/rubymine_reporter.rb
|
127
128
|
- lib/minitest/reporters/spec_reporter.rb
|
128
129
|
- lib/minitest/reporters/version.rb
|
130
|
+
- lib/minitest/templates/index.html.erb
|
129
131
|
- minitest-reporters.gemspec
|
130
132
|
- test/fixtures/junit_filename_bug_example_test.rb
|
131
133
|
- test/fixtures/progress_detailed_skip_test.rb
|