rspec-tracer 0.1.0
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 +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE +21 -0
- data/README.md +248 -0
- data/lib/rspec_tracer/cache.rb +109 -0
- data/lib/rspec_tracer/configuration.rb +134 -0
- data/lib/rspec_tracer/coverage_reporter.rb +179 -0
- data/lib/rspec_tracer/defaults.rb +10 -0
- data/lib/rspec_tracer/example.rb +58 -0
- data/lib/rspec_tracer/filter.rb +68 -0
- data/lib/rspec_tracer/html_reporter/assets/javascripts/application.js +56 -0
- data/lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js +10881 -0
- data/lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js +15381 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/application.css +196 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/datatables.css +459 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/jquery-ui.css +436 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/print.css +92 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/reset.css +265 -0
- data/lib/rspec_tracer/html_reporter/public/application.css +5 -0
- data/lib/rspec_tracer/html_reporter/public/application.js +6 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc_disabled.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_both.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc_disabled.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/favicon.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/loading.gif +0 -0
- data/lib/rspec_tracer/html_reporter/reporter.rb +180 -0
- data/lib/rspec_tracer/html_reporter/views/examples.erb +53 -0
- data/lib/rspec_tracer/html_reporter/views/examples_dependency.erb +36 -0
- data/lib/rspec_tracer/html_reporter/views/files_dependency.erb +36 -0
- data/lib/rspec_tracer/html_reporter/views/layout.erb +32 -0
- data/lib/rspec_tracer/reporter.rb +255 -0
- data/lib/rspec_tracer/rspec_reporter.rb +43 -0
- data/lib/rspec_tracer/rspec_runner.rb +25 -0
- data/lib/rspec_tracer/ruby_coverage.rb +9 -0
- data/lib/rspec_tracer/runner.rb +299 -0
- data/lib/rspec_tracer/source_file.rb +31 -0
- data/lib/rspec_tracer/version.rb +5 -0
- data/lib/rspec_tracer.rb +243 -0
- metadata +122 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
<div class="report_container" id="<%= title_id %>">
|
2
|
+
<h2>
|
3
|
+
<span class="group_name"><%= title %></span>
|
4
|
+
(
|
5
|
+
<span class="blue">
|
6
|
+
<strong><%= last_run[:actual_count] %></strong>
|
7
|
+
</span> examples,
|
8
|
+
<% if last_run[:failed_examples].positive? %>
|
9
|
+
<span class="red">
|
10
|
+
<strong><%= last_run[:failed_examples] %></strong>
|
11
|
+
</span> failures,
|
12
|
+
<% end %>
|
13
|
+
<% if last_run[:pending_examples].positive? %>
|
14
|
+
<span class="yellow">
|
15
|
+
<strong><%= last_run[:pending_examples] %></strong>
|
16
|
+
</span> pending,
|
17
|
+
<% end %>
|
18
|
+
<span class="blue">
|
19
|
+
<strong><%= last_run[:skipped_examples] %></strong>
|
20
|
+
</span> skipped by rspec-tracer
|
21
|
+
)
|
22
|
+
</h2>
|
23
|
+
|
24
|
+
<a name="<%= title_id %>"></a>
|
25
|
+
|
26
|
+
<div class="report-table--responsive">
|
27
|
+
<table class="report-table">
|
28
|
+
<thead>
|
29
|
+
<tr>
|
30
|
+
<th>ID</th>
|
31
|
+
<th>Description</th>
|
32
|
+
<th>Location</th>
|
33
|
+
<th>Run Reason</th>
|
34
|
+
<th>Result</th>
|
35
|
+
<th>Run At</th>
|
36
|
+
</tr>
|
37
|
+
</thead>
|
38
|
+
|
39
|
+
<tbody>
|
40
|
+
<% examples.each do |example| %>
|
41
|
+
<tr>
|
42
|
+
<td><%= example[:id] %></td>
|
43
|
+
<td><%= example[:description] %></td>
|
44
|
+
<td><%= example[:location] %></td>
|
45
|
+
<td><strong class="<%= example_status_css_class(example[:status]) %>"><%= example[:status] %></strong></td>
|
46
|
+
<td><strong class="<%= example_result_css_class(example[:result]) %>"><%= example[:result] %></strong></td>
|
47
|
+
<td width="8%"><%= example[:last_run] %></td>
|
48
|
+
</tr>
|
49
|
+
<% end %>
|
50
|
+
</tbody>
|
51
|
+
</table>
|
52
|
+
</div>
|
53
|
+
</div>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<div class="report_container" id="<%= title_id %>">
|
2
|
+
<h2>
|
3
|
+
<span class="group_name"><%= title %></span>
|
4
|
+
(
|
5
|
+
<span class="blue">
|
6
|
+
<strong><%= examples_dependency.count %></strong>
|
7
|
+
</span> examples
|
8
|
+
)
|
9
|
+
</h2>
|
10
|
+
|
11
|
+
<a name="<%= title_id %>"></a>
|
12
|
+
|
13
|
+
<div class="report-table--responsive">
|
14
|
+
<table class="report-table">
|
15
|
+
<thead>
|
16
|
+
<tr>
|
17
|
+
<th>Example ID</th>
|
18
|
+
<th>Example</th>
|
19
|
+
<th>Files Count</th>
|
20
|
+
<th data-orderable="false">Files Name</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
|
24
|
+
<tbody>
|
25
|
+
<% examples_dependency.each do |example_dependency| %>
|
26
|
+
<tr>
|
27
|
+
<td><%= example_dependency[:example_id] %></td>
|
28
|
+
<td><%= example_dependency[:example] %></td>
|
29
|
+
<td class="number" width="8%"><strong><%= example_dependency[:files_count] %></strong></td>
|
30
|
+
<td><%= example_dependency[:files] %></td>
|
31
|
+
</tr>
|
32
|
+
<% end %>
|
33
|
+
</tbody>
|
34
|
+
</table>
|
35
|
+
</div>
|
36
|
+
</div>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<div class="report_container" id="<%= title_id %>">
|
2
|
+
<h2>
|
3
|
+
<span class="group_name"><%= title %></span>
|
4
|
+
(
|
5
|
+
<span class="blue">
|
6
|
+
<strong><%= files_dependency.count %></strong>
|
7
|
+
</span> files
|
8
|
+
)
|
9
|
+
</h2>
|
10
|
+
|
11
|
+
<a name="<%= title_id %>"></a>
|
12
|
+
|
13
|
+
<div class="report-table--responsive">
|
14
|
+
<table class="report-table">
|
15
|
+
<thead>
|
16
|
+
<tr>
|
17
|
+
<th searchable="true">File</th>
|
18
|
+
<th>Examples Count</th>
|
19
|
+
<th>Files Count</th>
|
20
|
+
<th data-orderable="false">Dependent Files</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
|
24
|
+
<tbody>
|
25
|
+
<% files_dependency.each do |file_dependency| %>
|
26
|
+
<tr class="t-example">
|
27
|
+
<td><%= file_dependency[:name] %></td>
|
28
|
+
<td class="number" width="8%"><strong><%= file_dependency[:example_count] %></strong></td>
|
29
|
+
<td class="number" width="8%"><strong><%= file_dependency[:file_count] %></strong></td>
|
30
|
+
<td><%= file_dependency[:files] %></td>
|
31
|
+
</tr>
|
32
|
+
<% end %>
|
33
|
+
</tbody>
|
34
|
+
</table>
|
35
|
+
</div>
|
36
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
|
3
|
+
<html xmlns='http://www.w3.org/1999/xhtml'>
|
4
|
+
<head>
|
5
|
+
<title>RSpec Tracer Report for <%= RSpecTracer.project_name %></title>
|
6
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
7
|
+
<script src='<%= assets_path('application.js') %>' type='text/javascript'></script>
|
8
|
+
<link href='<%= assets_path('application.css') %>' media='screen, projection, print' rel='stylesheet' type='text/css' />
|
9
|
+
<link rel="icon" type="image/png" href="<%= assets_path('favicon.png') %>" />
|
10
|
+
</head>
|
11
|
+
|
12
|
+
<body>
|
13
|
+
<div id="loading">
|
14
|
+
<img src="<%= assets_path('loading.gif') %>" width="60" height="60" alt="loading" />
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div id="wrapper" class="hide">
|
18
|
+
<ul class="group_tabs"></ul>
|
19
|
+
|
20
|
+
<div id="content">
|
21
|
+
<%= formatted_examples('Examples', examples.values) %>
|
22
|
+
<%= formatted_examples_dependency('Examples Dependency', examples_dependency) %>
|
23
|
+
<%= formatted_files_dependency("Files Dependency", files_dependency) %>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
<div id="footer">
|
27
|
+
Generated by <a href="https://github.com/avmnu-sng/rspec-tracer">rspec-tracer</a>
|
28
|
+
v<%= RSpecTracer::VERSION %>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</body>
|
32
|
+
</html>
|
@@ -0,0 +1,255 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
class Reporter
|
5
|
+
attr_reader :all_examples, :pending_examples, :all_files, :dependency,
|
6
|
+
:reverse_dependency, :examples_coverage, :last_run
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
initialize_examples
|
10
|
+
initialize_files
|
11
|
+
initialize_dependency
|
12
|
+
initialize_coverage
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_example(example)
|
16
|
+
@all_examples[example[:example_id]] = example
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_example_skipped(example_id)
|
20
|
+
@skipped_examples << example_id
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_example_passed(example_id, result)
|
24
|
+
@all_examples[example_id][:execution_result] = formatted_execution_result(result)
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_example_failed(example_id, result)
|
28
|
+
@failed_examples << example_id
|
29
|
+
@all_examples[example_id][:execution_result] = formatted_execution_result(result)
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_example_pending(example_id, result)
|
33
|
+
@pending_examples << example_id
|
34
|
+
@all_examples[example_id][:execution_result] = formatted_execution_result(result)
|
35
|
+
end
|
36
|
+
|
37
|
+
def register_deleted_examples(seen_examples)
|
38
|
+
@deleted_examples = seen_examples.keys.to_set - (@skipped_examples | @all_examples.keys)
|
39
|
+
|
40
|
+
@deleted_examples.select! do |example_id|
|
41
|
+
example = seen_examples[example_id]
|
42
|
+
|
43
|
+
file_changed?(example[:file_name]) || file_changed?(example[:rerun_file_name])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def register_failed_example(example_id)
|
48
|
+
@failed_examples << example_id
|
49
|
+
end
|
50
|
+
|
51
|
+
def register_pending_example(example_id)
|
52
|
+
@pending_examples << example_id
|
53
|
+
end
|
54
|
+
|
55
|
+
def example_skipped?(example_id)
|
56
|
+
@skipped_examples.include?(example_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def example_failed?(example_id)
|
60
|
+
@failed_examples.include?(example_id)
|
61
|
+
end
|
62
|
+
|
63
|
+
def example_pending?(example_id)
|
64
|
+
@pending_examples.include?(example_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def example_deleted?(example_id)
|
68
|
+
@deleted_examples.include?(example_id)
|
69
|
+
end
|
70
|
+
|
71
|
+
def register_source_file(source_file)
|
72
|
+
@all_files[source_file[:file_name]] = source_file
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_file_deleted(file_name)
|
76
|
+
@deleted_files << file_name
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_file_modified(file_name)
|
80
|
+
@modified_files << file_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def file_deleted?(file_name)
|
84
|
+
@deleted_files.include?(file_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
def file_modified?(file_name)
|
88
|
+
@modified_files.include?(file_name)
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_changed?(file_name)
|
92
|
+
file_deleted?(file_name) || file_modified?(file_name)
|
93
|
+
end
|
94
|
+
|
95
|
+
def register_dependency(example_id, file_name)
|
96
|
+
@dependency[example_id] << file_name
|
97
|
+
end
|
98
|
+
|
99
|
+
def register_examples_coverage(examples_coverage)
|
100
|
+
@examples_coverage = examples_coverage
|
101
|
+
end
|
102
|
+
|
103
|
+
def generate_reverse_dependency_report
|
104
|
+
@dependency.each_pair do |example_id, files|
|
105
|
+
example_file = @all_examples[example_id][:rerun_file_name]
|
106
|
+
|
107
|
+
files.each do |file_name|
|
108
|
+
@reverse_dependency[file_name][:example_count] += 1
|
109
|
+
@reverse_dependency[file_name][:examples][example_file] += 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
format_reverse_dependency_report
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_last_run_report
|
117
|
+
@last_run = {
|
118
|
+
run_id: @run_id,
|
119
|
+
pid: RSpecTracer.pid,
|
120
|
+
actual_count: RSpec.world.example_count + @skipped_examples.count,
|
121
|
+
example_count: RSpec.world.example_count,
|
122
|
+
failed_examples: @failed_examples.count,
|
123
|
+
skipped_examples: @skipped_examples.count,
|
124
|
+
pending_examples: @pending_examples.count
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def write_reports
|
129
|
+
@run_id = Digest::MD5.hexdigest(@all_examples.keys.sort.to_json)
|
130
|
+
@cache_dir = File.join(RSpecTracer.cache_path, @run_id)
|
131
|
+
|
132
|
+
FileUtils.mkdir_p(@cache_dir)
|
133
|
+
|
134
|
+
%i[
|
135
|
+
all_examples
|
136
|
+
failed_examples
|
137
|
+
pending_examples
|
138
|
+
all_files
|
139
|
+
dependency
|
140
|
+
reverse_dependency
|
141
|
+
examples_coverage
|
142
|
+
last_run
|
143
|
+
].each { |report_type| send("write_#{report_type}_report") }
|
144
|
+
|
145
|
+
puts "RSpec tracer reports generated to #{@cache_dir}"
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def initialize_examples
|
151
|
+
@all_examples = {}
|
152
|
+
@failed_examples = Set.new
|
153
|
+
@skipped_examples = Set.new
|
154
|
+
@pending_examples = Set.new
|
155
|
+
@deleted_examples = Set.new
|
156
|
+
end
|
157
|
+
|
158
|
+
def initialize_files
|
159
|
+
@all_files = {}
|
160
|
+
@modified_files = Set.new
|
161
|
+
@deleted_files = Set.new
|
162
|
+
end
|
163
|
+
|
164
|
+
def initialize_dependency
|
165
|
+
@dependency = Hash.new { |hash, key| hash[key] = Set.new }
|
166
|
+
@reverse_dependency = Hash.new do |examples, file_name|
|
167
|
+
examples[file_name] = {
|
168
|
+
example_count: 0,
|
169
|
+
examples: Hash.new(0)
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def initialize_coverage
|
175
|
+
@examples_coverage = Hash.new do |examples, example_id|
|
176
|
+
examples[example_id] = Hash.new do |files, file_name|
|
177
|
+
files[file_name] = {}
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def formatted_execution_result(result)
|
183
|
+
{
|
184
|
+
started_at: result.started_at.utc,
|
185
|
+
finished_at: result.finished_at.utc,
|
186
|
+
run_time: result.run_time,
|
187
|
+
status: result.status.to_s
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
def format_reverse_dependency_report
|
192
|
+
@reverse_dependency.transform_values! do |data|
|
193
|
+
{
|
194
|
+
example_count: data[:example_count],
|
195
|
+
examples: data[:examples].sort_by { |file_name, count| [-count, file_name] }.to_h
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
report = @reverse_dependency.sort_by do |file_name, data|
|
200
|
+
[-data[:example_count], file_name]
|
201
|
+
end
|
202
|
+
|
203
|
+
@reverse_dependency = report.to_h
|
204
|
+
end
|
205
|
+
|
206
|
+
def write_all_examples_report
|
207
|
+
file_name = File.join(@cache_dir, 'all_examples.json')
|
208
|
+
|
209
|
+
File.write(file_name, JSON.pretty_generate(@all_examples))
|
210
|
+
end
|
211
|
+
|
212
|
+
def write_failed_examples_report
|
213
|
+
file_name = File.join(@cache_dir, 'failed_examples.json')
|
214
|
+
|
215
|
+
File.write(file_name, JSON.pretty_generate(@failed_examples.to_a))
|
216
|
+
end
|
217
|
+
|
218
|
+
def write_pending_examples_report
|
219
|
+
file_name = File.join(@cache_dir, 'pending_examples.json')
|
220
|
+
|
221
|
+
File.write(file_name, JSON.pretty_generate(@pending_examples.to_a))
|
222
|
+
end
|
223
|
+
|
224
|
+
def write_all_files_report
|
225
|
+
file_name = File.join(@cache_dir, 'all_files.json')
|
226
|
+
|
227
|
+
File.write(file_name, JSON.pretty_generate(@all_files))
|
228
|
+
end
|
229
|
+
|
230
|
+
def write_dependency_report
|
231
|
+
file_name = File.join(@cache_dir, 'dependency.json')
|
232
|
+
|
233
|
+
File.write(file_name, JSON.pretty_generate(@dependency))
|
234
|
+
end
|
235
|
+
|
236
|
+
def write_reverse_dependency_report
|
237
|
+
file_name = File.join(@cache_dir, 'reverse_dependency.json')
|
238
|
+
|
239
|
+
File.write(file_name, JSON.pretty_generate(@reverse_dependency))
|
240
|
+
end
|
241
|
+
|
242
|
+
def write_examples_coverage_report
|
243
|
+
file_name = File.join(@cache_dir, 'examples_coverage.json')
|
244
|
+
|
245
|
+
File.write(file_name, JSON.pretty_generate(@examples_coverage))
|
246
|
+
end
|
247
|
+
|
248
|
+
def write_last_run_report
|
249
|
+
file_name = File.join(RSpecTracer.cache_path, 'last_run.json')
|
250
|
+
last_run_data = @last_run.merge(run_id: @run_id, timestamp: Time.now.utc)
|
251
|
+
|
252
|
+
File.write(file_name, JSON.pretty_generate(last_run_data))
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RSpecReporter
|
5
|
+
def example_started(example)
|
6
|
+
RSpecTracer.coverage_reporter.record_coverage
|
7
|
+
RSpecTracer.start_example_trace if RSpecTracer.trace_example?
|
8
|
+
|
9
|
+
super(example)
|
10
|
+
end
|
11
|
+
|
12
|
+
def example_finished(example)
|
13
|
+
passed = example.execution_result.status == :passed
|
14
|
+
RSpecTracer.stop_example_trace(passed) if RSpecTracer.trace_example?
|
15
|
+
|
16
|
+
example_id = example.metadata[:rspec_tracer_example_id]
|
17
|
+
RSpecTracer.coverage_reporter.compute_diff(example_id)
|
18
|
+
|
19
|
+
super(example)
|
20
|
+
end
|
21
|
+
|
22
|
+
def example_passed(example)
|
23
|
+
example_id = example.metadata[:rspec_tracer_example_id]
|
24
|
+
RSpecTracer.runner.on_example_passed(example_id, example.execution_result)
|
25
|
+
|
26
|
+
super(example)
|
27
|
+
end
|
28
|
+
|
29
|
+
def example_failed(example)
|
30
|
+
example_id = example.metadata[:rspec_tracer_example_id]
|
31
|
+
RSpecTracer.runner.on_example_failed(example_id, example.execution_result)
|
32
|
+
|
33
|
+
super(example)
|
34
|
+
end
|
35
|
+
|
36
|
+
def example_pending(example)
|
37
|
+
example_id = example.metadata[:rspec_tracer_example_id]
|
38
|
+
RSpecTracer.runner.on_example_pending(example_id, example.execution_result)
|
39
|
+
|
40
|
+
super(example)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|