rspec-tracer 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|