rspec-tracer 0.9.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +41 -14
- data/lib/rspec_tracer/cache.rb +76 -36
- data/lib/rspec_tracer/configuration.rb +16 -9
- data/lib/rspec_tracer/coverage_merger.rb +41 -0
- data/lib/rspec_tracer/coverage_reporter.rb +31 -28
- data/lib/rspec_tracer/coverage_writer.rb +58 -0
- data/lib/rspec_tracer/html_reporter/reporter.rb +56 -16
- data/lib/rspec_tracer/html_reporter/views/duplicate_examples.erb +34 -0
- data/lib/rspec_tracer/html_reporter/views/examples.erb +5 -0
- data/lib/rspec_tracer/html_reporter/views/layout.erb +8 -5
- data/lib/rspec_tracer/remote_cache/validator.rb +1 -1
- data/lib/rspec_tracer/report_generator.rb +158 -0
- data/lib/rspec_tracer/report_merger.rb +81 -0
- data/lib/rspec_tracer/report_writer.rb +141 -0
- data/lib/rspec_tracer/reporter.rb +42 -174
- data/lib/rspec_tracer/rspec_runner.rb +2 -4
- data/lib/rspec_tracer/runner.rb +41 -113
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +202 -29
- metadata +23 -17
@@ -8,35 +8,40 @@ module RSpecTracer
|
|
8
8
|
class Reporter
|
9
9
|
attr_reader :last_run, :examples, :flaky_examples, :examples_dependency, :files_dependency
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@
|
13
|
-
|
14
|
-
format_last_run
|
15
|
-
format_examples
|
16
|
-
format_flaky_examples
|
17
|
-
format_examples_dependency
|
18
|
-
format_files_dependency
|
11
|
+
def initialize(report_dir, reporter)
|
12
|
+
@report_dir = report_dir
|
13
|
+
@reporter = reporter
|
19
14
|
end
|
20
15
|
|
21
16
|
def generate_report
|
22
17
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
18
|
|
24
|
-
|
19
|
+
prepare
|
25
20
|
|
26
|
-
file_name = File.join(
|
21
|
+
file_name = File.join(@report_dir, 'index.html')
|
27
22
|
|
28
23
|
File.open(file_name, 'wb') do |file|
|
29
24
|
file.puts(template('layout').result(binding))
|
30
25
|
end
|
31
26
|
|
32
27
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
33
|
-
|
28
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
34
29
|
|
35
|
-
puts "RSpecTracer generated HTML report to #{file_name} (took #{
|
30
|
+
puts "RSpecTracer generated HTML report to #{file_name} (took #{elapsed})"
|
36
31
|
end
|
37
32
|
|
38
33
|
private
|
39
34
|
|
35
|
+
def prepare
|
36
|
+
format_last_run
|
37
|
+
format_examples
|
38
|
+
format_duplicate_examples
|
39
|
+
format_flaky_examples
|
40
|
+
format_examples_dependency
|
41
|
+
format_files_dependency
|
42
|
+
copy_assets
|
43
|
+
end
|
44
|
+
|
40
45
|
def copy_assets
|
41
46
|
Dir[File.join(File.dirname(__FILE__), 'public/*')].each do |path|
|
42
47
|
FileUtils.cp_r(path, asset_output_path)
|
@@ -46,6 +51,7 @@ module RSpecTracer
|
|
46
51
|
def format_last_run
|
47
52
|
@last_run = @reporter.last_run.slice(
|
48
53
|
:actual_count,
|
54
|
+
:duplicate_examples,
|
49
55
|
:failed_examples,
|
50
56
|
:pending_examples,
|
51
57
|
:skipped_examples
|
@@ -60,13 +66,39 @@ module RSpecTracer
|
|
60
66
|
id: example_id,
|
61
67
|
description: example[:full_description],
|
62
68
|
location: example_location(example[:rerun_file_name], example[:rerun_line_number]),
|
63
|
-
status: example[:run_reason] || 'Skipped'
|
69
|
+
status: example[:run_reason] || 'Skipped'
|
70
|
+
}.merge(example_result(example_id, example))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def example_result(example_id, example)
|
75
|
+
if example[:execution_result].nil?
|
76
|
+
{
|
77
|
+
result: @reporter.example_interrupted?(example_id) ? 'Interrupted' : '_',
|
78
|
+
last_run: '_'
|
79
|
+
}
|
80
|
+
else
|
81
|
+
{
|
64
82
|
result: example[:execution_result][:status].capitalize,
|
65
83
|
last_run: example_run_local_time(example[:execution_result][:finished_at])
|
66
84
|
}
|
67
85
|
end
|
68
86
|
end
|
69
87
|
|
88
|
+
def format_duplicate_examples
|
89
|
+
@duplicate_examples = []
|
90
|
+
|
91
|
+
@reporter.duplicate_examples.each_pair do |example_id, examples|
|
92
|
+
examples.each do |example|
|
93
|
+
@duplicate_examples << {
|
94
|
+
id: example_id,
|
95
|
+
description: example[:full_description],
|
96
|
+
location: example_location(example[:rerun_file_name], example[:rerun_line_number])
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
70
102
|
def format_flaky_examples
|
71
103
|
@flaky_examples = @examples.slice(*@reporter.flaky_examples).values
|
72
104
|
end
|
@@ -122,7 +154,7 @@ module RSpecTracer
|
|
122
154
|
|
123
155
|
def asset_output_path
|
124
156
|
@asset_output_path ||= begin
|
125
|
-
asset_output_path = File.join(
|
157
|
+
asset_output_path = File.join(@report_dir, 'assets', RSpecTracer::VERSION)
|
126
158
|
|
127
159
|
FileUtils.mkdir_p(asset_output_path)
|
128
160
|
|
@@ -142,6 +174,14 @@ module RSpecTracer
|
|
142
174
|
template(title_id).result(current_binding)
|
143
175
|
end
|
144
176
|
|
177
|
+
def formatted_duplicate_examples(title, duplicate_examples)
|
178
|
+
title_id = report_container_id(title)
|
179
|
+
current_binding = binding
|
180
|
+
|
181
|
+
current_binding.local_variable_set(:title_id, title_id)
|
182
|
+
template(title_id).result(current_binding)
|
183
|
+
end
|
184
|
+
|
145
185
|
def formatted_flaky_examples(title, flaky_examples)
|
146
186
|
title_id = report_container_id(title)
|
147
187
|
current_binding = binding
|
@@ -176,7 +216,7 @@ module RSpecTracer
|
|
176
216
|
|
177
217
|
def example_status_css_class(example_status)
|
178
218
|
case example_status.split.first
|
179
|
-
when 'Failed', 'Flaky'
|
219
|
+
when 'Failed', 'Flaky', 'Interrupted'
|
180
220
|
'red'
|
181
221
|
when 'Pending'
|
182
222
|
'yellow'
|
@@ -189,7 +229,7 @@ module RSpecTracer
|
|
189
229
|
case example_result
|
190
230
|
when 'Passed'
|
191
231
|
'green'
|
192
|
-
when 'Failed'
|
232
|
+
when 'Failed', 'Interrupted'
|
193
233
|
'red'
|
194
234
|
when 'Pending'
|
195
235
|
'yellow'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<div class="report_container" id="<%= title_id %>">
|
2
|
+
<h2>
|
3
|
+
<span class="group_name"><%= title %></span>
|
4
|
+
(
|
5
|
+
<span class="blue">
|
6
|
+
<strong><%= duplicate_examples.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>ID</th>
|
18
|
+
<th>Description</th>
|
19
|
+
<th>Location</th>
|
20
|
+
</tr>
|
21
|
+
</thead>
|
22
|
+
|
23
|
+
<tbody>
|
24
|
+
<% duplicate_examples.each do |example| %>
|
25
|
+
<tr>
|
26
|
+
<td><%= example[:id] %></td>
|
27
|
+
<td><%= example[:description] %></td>
|
28
|
+
<td><%= example[:location] %></td>
|
29
|
+
</tr>
|
30
|
+
<% end %>
|
31
|
+
</tbody>
|
32
|
+
</table>
|
33
|
+
</div>
|
34
|
+
</div>
|
@@ -5,6 +5,11 @@
|
|
5
5
|
<span class="blue">
|
6
6
|
<strong><%= last_run[:actual_count] %></strong>
|
7
7
|
</span> examples,
|
8
|
+
<% if last_run[:duplicate_examples].positive? %>
|
9
|
+
<span class="blue">
|
10
|
+
<strong><%= last_run[:duplicate_examples] %></strong>
|
11
|
+
</span> duplicates,
|
12
|
+
<% end %>
|
8
13
|
<% if last_run[:failed_examples].positive? %>
|
9
14
|
<span class="red">
|
10
15
|
<strong><%= last_run[:failed_examples] %></strong>
|
@@ -18,12 +18,15 @@
|
|
18
18
|
<ul class="group_tabs"></ul>
|
19
19
|
|
20
20
|
<div id="content">
|
21
|
-
<%= formatted_examples('Examples', examples.values) %>
|
22
|
-
<% unless
|
23
|
-
<%=
|
21
|
+
<%= formatted_examples('Examples', @examples.values) %>
|
22
|
+
<% unless @duplicate_examples.empty? %>
|
23
|
+
<%= formatted_duplicate_examples('Duplicate Examples', @duplicate_examples) %>
|
24
24
|
<% end %>
|
25
|
-
|
26
|
-
|
25
|
+
<% unless @flaky_examples.empty? %>
|
26
|
+
<%= formatted_flaky_examples('Flaky Examples', @flaky_examples) %>
|
27
|
+
<% end %>
|
28
|
+
<%= formatted_examples_dependency('Examples Dependency', @examples_dependency) %>
|
29
|
+
<%= formatted_files_dependency("Files Dependency", @files_dependency) %>
|
27
30
|
</div>
|
28
31
|
|
29
32
|
<div id="footer">
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
class ReportGenerator
|
5
|
+
def initialize(reporter, cache)
|
6
|
+
@reporter = reporter
|
7
|
+
@cache = cache
|
8
|
+
end
|
9
|
+
|
10
|
+
def reverse_dependency_report
|
11
|
+
reverse_dependency = Hash.new do |examples, file_name|
|
12
|
+
examples[file_name] = {
|
13
|
+
example_count: 0,
|
14
|
+
examples: Hash.new(0)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
@reporter.dependency.each_pair do |example_id, files|
|
19
|
+
next if @reporter.interrupted_examples.include?(example_id)
|
20
|
+
|
21
|
+
example_file = @reporter.all_examples[example_id][:rerun_file_name]
|
22
|
+
|
23
|
+
files.each do |file_name|
|
24
|
+
reverse_dependency[file_name][:example_count] += 1
|
25
|
+
reverse_dependency[file_name][:examples][example_file] += 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
reverse_dependency.transform_values! do |data|
|
30
|
+
{
|
31
|
+
example_count: data[:example_count],
|
32
|
+
examples: data[:examples].sort_by { |file_name, count| [-count, file_name] }.to_h
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
reverse_dependency.sort_by { |file_name, data| [-data[:example_count], file_name] }.to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_report
|
40
|
+
generate_last_run_report
|
41
|
+
generate_examples_status_report
|
42
|
+
|
43
|
+
%i[all_files all_examples dependency examples_coverage reverse_dependency].each do |report_type|
|
44
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
45
|
+
|
46
|
+
send("generate_#{report_type}_report")
|
47
|
+
|
48
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
49
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
50
|
+
|
51
|
+
puts "RSpec tracer generated #{report_type.to_s.tr('_', ' ')} report (took #{elapsed})" if RSpecTracer.verbose?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def generate_last_run_report
|
58
|
+
@reporter.last_run = {
|
59
|
+
pid: RSpecTracer.pid,
|
60
|
+
actual_count: RSpec.world.example_count + @reporter.skipped_examples.count,
|
61
|
+
example_count: RSpec.world.example_count,
|
62
|
+
duplicate_examples: @reporter.duplicate_examples.sum { |_, examples| examples.count },
|
63
|
+
interrupted_examples: @reporter.interrupted_examples.count,
|
64
|
+
failed_examples: @reporter.failed_examples.count,
|
65
|
+
skipped_examples: @reporter.skipped_examples.count,
|
66
|
+
pending_examples: @reporter.pending_examples.count,
|
67
|
+
flaky_examples: @reporter.flaky_examples.count
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_examples_status_report
|
72
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
73
|
+
|
74
|
+
generate_flaky_examples_report
|
75
|
+
generate_failed_examples_report
|
76
|
+
generate_pending_examples_report
|
77
|
+
|
78
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
79
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
80
|
+
|
81
|
+
puts "RSpec tracer generated flaky, failed, and pending examples report (took #{elapsed})" if RSpecTracer.verbose?
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_flaky_examples_report
|
85
|
+
@reporter.possibly_flaky_examples.each do |example_id|
|
86
|
+
next if @reporter.example_deleted?(example_id)
|
87
|
+
next unless @cache.flaky_examples.include?(example_id) ||
|
88
|
+
@reporter.example_passed?(example_id)
|
89
|
+
|
90
|
+
@reporter.register_flaky_example(example_id)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def generate_failed_examples_report
|
95
|
+
@cache.failed_examples.each do |example_id|
|
96
|
+
next if @reporter.example_deleted?(example_id) ||
|
97
|
+
@reporter.all_examples.key?(example_id)
|
98
|
+
|
99
|
+
@reporter.register_failed_example(example_id)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def generate_pending_examples_report
|
104
|
+
@cache.pending_examples.each do |example_id|
|
105
|
+
next if @reporter.example_deleted?(example_id) ||
|
106
|
+
@reporter.all_examples.key?(example_id)
|
107
|
+
|
108
|
+
@reporter.register_pending_example(example_id)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_all_files_report
|
113
|
+
@cache.all_files.each_pair do |file_name, data|
|
114
|
+
next if @reporter.all_files.key?(file_name) ||
|
115
|
+
@reporter.file_deleted?(file_name)
|
116
|
+
|
117
|
+
@reporter.all_files[file_name] = data
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def generate_all_examples_report
|
122
|
+
@cache.all_examples.each_pair do |example_id, data|
|
123
|
+
next if @reporter.all_examples.key?(example_id) ||
|
124
|
+
@reporter.example_deleted?(example_id)
|
125
|
+
|
126
|
+
@reporter.all_examples[example_id] = data
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def generate_dependency_report
|
131
|
+
@cache.dependency.each_pair do |example_id, data|
|
132
|
+
next if @reporter.dependency.key?(example_id) ||
|
133
|
+
@reporter.example_deleted?(example_id)
|
134
|
+
|
135
|
+
@reporter.dependency[example_id] = data.reject do |file_name|
|
136
|
+
@reporter.file_deleted?(file_name)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
@reporter.dependency.transform_values!(&:to_a)
|
141
|
+
end
|
142
|
+
|
143
|
+
def generate_examples_coverage_report
|
144
|
+
@cache.cached_examples_coverage.each_pair do |example_id, data|
|
145
|
+
next if @reporter.examples_coverage.key?(example_id) ||
|
146
|
+
@reporter.example_deleted?(example_id)
|
147
|
+
|
148
|
+
@reporter.examples_coverage[example_id] = data.reject do |file_name|
|
149
|
+
@reporter.file_deleted?(file_name)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def generate_reverse_dependency_report
|
155
|
+
@reporter.reverse_dependency = reverse_dependency_report
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
class ReportMerger
|
5
|
+
attr_reader :all_examples, :duplicate_examples, :interrupted_examples,
|
6
|
+
:flaky_examples, :failed_examples, :pending_examples, :skipped_examples,
|
7
|
+
:all_files, :dependency, :reverse_dependency, :examples_coverage, :last_run
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@last_run = {}
|
11
|
+
@all_examples = {}
|
12
|
+
@duplicate_examples = {}
|
13
|
+
@interrupted_examples = Set.new
|
14
|
+
@flaky_examples = Set.new
|
15
|
+
@failed_examples = Set.new
|
16
|
+
@pending_examples = Set.new
|
17
|
+
@skipped_examples = Set.new
|
18
|
+
@all_files = {}
|
19
|
+
@dependency = Hash.new { |hash, key| hash[key] = Set.new }
|
20
|
+
@reverse_dependency = {}
|
21
|
+
@examples_coverage = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge(reports_dir)
|
25
|
+
reports_dir.each do |report_dir|
|
26
|
+
next unless File.directory?(report_dir)
|
27
|
+
|
28
|
+
merge_cache(load_cache(report_dir))
|
29
|
+
merge_last_run_report(File.dirname(report_dir))
|
30
|
+
end
|
31
|
+
|
32
|
+
@dependency.transform_values!(&:to_a)
|
33
|
+
|
34
|
+
@reverse_dependency = RSpecTracer::ReportGenerator.new(self, nil).reverse_dependency_report
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def load_cache(cache_dir)
|
40
|
+
cache = RSpecTracer::Cache.new
|
41
|
+
|
42
|
+
cache.send(:load_all_examples_cache, cache_dir, discard_run_reason: false)
|
43
|
+
cache.send(:load_duplicate_examples_cache, cache_dir)
|
44
|
+
cache.send(:load_interrupted_examples_cache, cache_dir)
|
45
|
+
cache.send(:load_flaky_examples_cache, cache_dir)
|
46
|
+
cache.send(:load_failed_examples_cache, cache_dir)
|
47
|
+
cache.send(:load_pending_examples_cache, cache_dir)
|
48
|
+
cache.send(:load_skipped_examples_cache, cache_dir)
|
49
|
+
cache.send(:load_all_files_cache, cache_dir)
|
50
|
+
cache.send(:load_dependency_cache, cache_dir)
|
51
|
+
cache.send(:load_examples_coverage_cache, cache_dir)
|
52
|
+
|
53
|
+
cache
|
54
|
+
end
|
55
|
+
|
56
|
+
def merge_cache(cache)
|
57
|
+
@all_examples.merge!(cache.all_examples) { |_, v1, v2| v1[:run_reason] ? v1 : v2 }
|
58
|
+
@duplicate_examples.merge!(cache.duplicate_examples) { |_, v1, v2| v1 + v2 }
|
59
|
+
@interrupted_examples.merge(cache.interrupted_examples)
|
60
|
+
@flaky_examples.merge(cache.flaky_examples)
|
61
|
+
@failed_examples.merge(cache.failed_examples)
|
62
|
+
@pending_examples.merge(cache.pending_examples)
|
63
|
+
@skipped_examples.merge(cache.skipped_examples)
|
64
|
+
@all_files.merge!(cache.all_files)
|
65
|
+
@dependency.merge!(cache.dependency) { |_, v1, v2| v1.merge(v2) }
|
66
|
+
@examples_coverage.merge!(cache.examples_coverage) do |_, v1, v2|
|
67
|
+
v1.merge(v2) { |_, v3, v4| v3.merge(v4) { |_, v5, v6| v5 + v6 } }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def merge_last_run_report(cache_dir)
|
72
|
+
file_name = File.join(cache_dir, 'last_run.json')
|
73
|
+
cached_last_run = JSON.parse(File.read(file_name), symbolize_names: true)
|
74
|
+
cached_last_run[:pid] = [cached_last_run[:pid]]
|
75
|
+
|
76
|
+
cached_last_run.delete_if { |key, _| %i[run_id timestamp].include?(key) }
|
77
|
+
|
78
|
+
@last_run.merge!(cached_last_run) { |_, v1, v2| v1 + v2 }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
class ReportWriter
|
5
|
+
def initialize(report_dir, reporter)
|
6
|
+
@report_dir = report_dir
|
7
|
+
@reporter = reporter
|
8
|
+
end
|
9
|
+
|
10
|
+
def write_report
|
11
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
12
|
+
|
13
|
+
@run_id = Digest::MD5.hexdigest(@reporter.all_examples.keys.sort.to_json)
|
14
|
+
@cache_dir = File.join(@report_dir, @run_id)
|
15
|
+
|
16
|
+
FileUtils.mkdir_p(@cache_dir)
|
17
|
+
|
18
|
+
write_all_examples_report
|
19
|
+
write_duplicate_examples_report
|
20
|
+
write_interrupted_examples_report
|
21
|
+
write_flaky_examples_report
|
22
|
+
write_failed_examples_report
|
23
|
+
write_pending_examples_report
|
24
|
+
write_skipped_examples_report
|
25
|
+
write_all_files_report
|
26
|
+
write_dependency_report
|
27
|
+
write_reverse_dependency_report
|
28
|
+
write_examples_coverage_report
|
29
|
+
write_last_run_report
|
30
|
+
|
31
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
32
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
33
|
+
|
34
|
+
puts "RSpec tracer reports written to #{@cache_dir} (took #{elapsed})"
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_duplicate_examples
|
38
|
+
return if @reporter.duplicate_examples.empty?
|
39
|
+
|
40
|
+
total = @reporter.duplicate_examples.sum { |_, examples| examples.count }
|
41
|
+
|
42
|
+
puts '=' * 80
|
43
|
+
puts ' IMPORTANT NOTICE -- RSPEC TRACER COULD NOT IDENTIFY SOME EXAMPLES UNIQUELY'
|
44
|
+
puts '=' * 80
|
45
|
+
puts "RSpec tracer could not uniquely identify the following #{total} examples:"
|
46
|
+
|
47
|
+
justify = ' ' * 2
|
48
|
+
nested_justify = justify * 3
|
49
|
+
|
50
|
+
@reporter.duplicate_examples.each_pair do |example_id, examples|
|
51
|
+
puts "#{justify}- Example ID: #{example_id} (#{examples.count} examples)"
|
52
|
+
|
53
|
+
examples.each do |example|
|
54
|
+
description = example[:full_description].strip
|
55
|
+
file_name = example[:rerun_file_name].sub(%r{^/}, '')
|
56
|
+
line_number = example[:rerun_line_number]
|
57
|
+
location = "#{file_name}:#{line_number}"
|
58
|
+
|
59
|
+
puts "#{nested_justify}* #{description} (#{location})"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
puts
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def write_all_examples_report
|
69
|
+
file_name = File.join(@cache_dir, 'all_examples.json')
|
70
|
+
|
71
|
+
File.write(file_name, JSON.pretty_generate(@reporter.all_examples))
|
72
|
+
end
|
73
|
+
|
74
|
+
def write_duplicate_examples_report
|
75
|
+
file_name = File.join(@cache_dir, 'duplicate_examples.json')
|
76
|
+
|
77
|
+
File.write(file_name, JSON.pretty_generate(@reporter.duplicate_examples))
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_interrupted_examples_report
|
81
|
+
file_name = File.join(@cache_dir, 'interrupted_examples.json')
|
82
|
+
|
83
|
+
File.write(file_name, JSON.pretty_generate(@reporter.interrupted_examples.sort.to_a))
|
84
|
+
end
|
85
|
+
|
86
|
+
def write_flaky_examples_report
|
87
|
+
file_name = File.join(@cache_dir, 'flaky_examples.json')
|
88
|
+
|
89
|
+
File.write(file_name, JSON.pretty_generate(@reporter.flaky_examples.sort.to_a))
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_failed_examples_report
|
93
|
+
file_name = File.join(@cache_dir, 'failed_examples.json')
|
94
|
+
|
95
|
+
File.write(file_name, JSON.pretty_generate(@reporter.failed_examples.sort.to_a))
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_pending_examples_report
|
99
|
+
file_name = File.join(@cache_dir, 'pending_examples.json')
|
100
|
+
|
101
|
+
File.write(file_name, JSON.pretty_generate(@reporter.pending_examples.sort.to_a))
|
102
|
+
end
|
103
|
+
|
104
|
+
def write_skipped_examples_report
|
105
|
+
file_name = File.join(@cache_dir, 'skipped_examples.json')
|
106
|
+
|
107
|
+
File.write(file_name, JSON.pretty_generate(@reporter.skipped_examples.sort.to_a))
|
108
|
+
end
|
109
|
+
|
110
|
+
def write_all_files_report
|
111
|
+
file_name = File.join(@cache_dir, 'all_files.json')
|
112
|
+
|
113
|
+
File.write(file_name, JSON.pretty_generate(@reporter.all_files))
|
114
|
+
end
|
115
|
+
|
116
|
+
def write_dependency_report
|
117
|
+
file_name = File.join(@cache_dir, 'dependency.json')
|
118
|
+
|
119
|
+
File.write(file_name, JSON.pretty_generate(@reporter.dependency))
|
120
|
+
end
|
121
|
+
|
122
|
+
def write_reverse_dependency_report
|
123
|
+
file_name = File.join(@cache_dir, 'reverse_dependency.json')
|
124
|
+
|
125
|
+
File.write(file_name, JSON.pretty_generate(@reporter.reverse_dependency))
|
126
|
+
end
|
127
|
+
|
128
|
+
def write_examples_coverage_report
|
129
|
+
file_name = File.join(@cache_dir, 'examples_coverage.json')
|
130
|
+
|
131
|
+
File.write(file_name, JSON.pretty_generate(@reporter.examples_coverage))
|
132
|
+
end
|
133
|
+
|
134
|
+
def write_last_run_report
|
135
|
+
file_name = File.join(@report_dir, 'last_run.json')
|
136
|
+
last_run_data = @reporter.last_run.merge(run_id: @run_id, timestamp: Time.now.utc)
|
137
|
+
|
138
|
+
File.write(file_name, JSON.pretty_generate(last_run_data))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|