minitest-heat 0.0.9 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,7 @@
3
3
  module Minitest
4
4
  module Heat
5
5
  class Output
6
+ # Generates the tokens to output the resulting heat map
6
7
  class Map
7
8
  attr_accessor :results
8
9
 
@@ -12,16 +13,36 @@ module Minitest
12
13
  end
13
14
 
14
15
  def tokens
15
- map.file_hits.each do |hit|
16
- file_tokens = pathname(hit)
17
- line_number_tokens = line_numbers(hit)
16
+ results.heat_map.file_hits.each do |hit|
17
+ # Focus on the relevant issues based on most significant problems. i.e. If there are
18
+ # legitimate failures or errors, skips and slows aren't relevant
19
+ next unless relevant_issue_types?(hit)
18
20
 
19
- next if line_number_tokens.empty?
21
+ # Add a new line
22
+ @tokens << [[:muted, ""]]
20
23
 
21
- @tokens << [
22
- *file_tokens,
23
- *line_number_tokens
24
- ]
24
+ # Build the summary line for the file
25
+ @tokens << file_summary_tokens(hit)
26
+
27
+ # Get the set of line numbers that appear more than once
28
+ repeated_line_numbers = find_repeated_line_numbers_in(hit)
29
+
30
+ # Only display more details if the same line number shows up more than once
31
+ next unless repeated_line_numbers.any?
32
+
33
+ repeated_line_numbers.each do |line_number|
34
+ # Get the backtraces for the given line numbers
35
+ traces = hit.lines[line_number.to_s]
36
+
37
+ # If there aren't any traces there's no way to provide additional details
38
+ break unless traces.any?
39
+
40
+ # A short summary explaining the details that will follow
41
+ @tokens << [[:muted, " Issues on Line #{line_number} initially triggered from these locations:"]]
42
+
43
+ # The last relevant location for each error's backtrace
44
+ @tokens += origination_sources(traces)
45
+ end
25
46
  end
26
47
 
27
48
  @tokens
@@ -29,13 +50,41 @@ module Minitest
29
50
 
30
51
  private
31
52
 
32
- def map
33
- results.heat_map
53
+ def origination_sources(traces)
54
+ # 1. Sort the traces by the most recent line number so they're displayed in numeric order
55
+ # 2. Get the final relevant location from the trace
56
+ traces.
57
+ sort_by { |trace| trace.locations.last.line_number }.
58
+ map { |trace| origination_location_token(trace) }
59
+ end
60
+
61
+ def file_summary_tokens(hit)
62
+ pathname_tokens = pathname(hit)
63
+ line_number_list_tokens = sorted_line_number_list(hit)
64
+
65
+ [*pathname_tokens, *line_number_list_tokens]
66
+ end
67
+
68
+ def origination_location_token(trace)
69
+ # The earliest project line from the backtrace—this is probabyl wholly incorrect in terms
70
+ # of what would be the most helpful line to display, but it's a start.
71
+ location = trace.locations.last
72
+
73
+ [
74
+ [:muted, " #{Output::SYMBOLS[:arrow]} "],
75
+ [:default, location.relative_filename],
76
+ [:muted, ':'],
77
+ [:default, location.line_number],
78
+ [:muted, " in #{location.container}"],
79
+ [:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"],
80
+ ]
34
81
  end
35
82
 
36
83
  def relevant_issue_types
84
+ # These are always relevant.
37
85
  issue_types = %i[error broken failure]
38
86
 
87
+ # These are only relevant if there aren't more serious isues.
39
88
  issue_types << :skipped unless results.problems?
40
89
  issue_types << :painful unless results.problems? || results.skips.any?
41
90
  issue_types << :slow unless results.problems? || results.skips.any?
@@ -43,32 +92,85 @@ module Minitest
43
92
  issue_types
44
93
  end
45
94
 
46
- def pathname(file)
47
- directory = "#{file.pathname.dirname.to_s.delete_prefix(Dir.pwd)}/"
48
- filename = file.pathname.basename.to_s
95
+ def relevant_issue_types?(hit)
96
+ # The intersection of which issue types are relevant based on the context and which issues
97
+ # matc those issue types
98
+ intersection_issue_types = relevant_issue_types & hit.issues.keys
99
+
100
+ intersection_issue_types.any?
101
+ end
102
+
103
+ def find_repeated_line_numbers_in(hit)
104
+ repeated_line_numbers = []
105
+
106
+ hit.lines.each_pair do |line_number, traces|
107
+ # If there aren't multiple traces for a line number, it's not a repeat
108
+ next unless traces.size > 1
109
+
110
+ repeated_line_numbers << Integer(line_number)
111
+ end
112
+
113
+ repeated_line_numbers.sort
114
+ end
115
+
116
+ def pathname(hit)
117
+ directory = hit.pathname.dirname.to_s.delete_prefix("#{Dir.pwd}/")
118
+ filename = hit.pathname.basename.to_s
49
119
 
50
120
  [
51
- [:default, directory],
121
+ [:default, "#{directory}/"],
52
122
  [:bold, filename],
53
123
  [:default, ' · ']
54
124
  ]
55
125
  end
56
126
 
57
- def hit_line_numbers(file, issue_type)
58
- numbers = []
59
- line_numbers_for_issue_type = file.issues.fetch(issue_type) { [] }
60
- line_numbers_for_issue_type.sort.map do |line_number|
61
- numbers << [issue_type, "#{line_number} "]
127
+ # Gets the list of line numbers for a given hit location (i.e. file) so they can be
128
+ # displayed after the file name to show which lines were problematic
129
+ # @param hit [Hit] the instance to extract line numbers from
130
+ #
131
+ # @return [Array<Symbol,String>] [description]
132
+ def line_number_tokens_for_hit(hit)
133
+ line_number_tokens = []
134
+
135
+ relevant_issue_types.each do |issue_type|
136
+ # Retrieve any line numbers for the issue type
137
+ line_numbers_for_issue_type = hit.issues.fetch(issue_type) { [] }
138
+
139
+ # Build the list of tokens representing styled line numbers
140
+ line_numbers_for_issue_type.each do |line_number|
141
+ line_number_tokens << line_number_token(issue_type, line_number)
142
+ end
62
143
  end
63
- numbers
144
+
145
+ line_number_tokens.compact
64
146
  end
65
147
 
66
- def line_numbers(file)
67
- line_number_tokens = []
68
- relevant_issue_types.each do |issue_type|
69
- line_number_tokens += hit_line_numbers(file, issue_type)
148
+ # Builds a token representing a styled line number
149
+ #
150
+ # @param style [Symbol] the relevant display style for the issue
151
+ # @param line_number [Integer] the affected line number
152
+ #
153
+ # @return [Array<Symbol,Integer>] array token representing the line number and issue type
154
+ def line_number_token(style, line_number)
155
+ [style, "#{line_number} "]
156
+ end
157
+
158
+ # Sorts line number tokens so that line numbers are displayed in order regardless of their
159
+ # underlying issue type
160
+ #
161
+ # @param hit [Hit] the instance of the hit file details to build the heat map entry
162
+ #
163
+ # @return [Array] the arrays representing the line number tokens to display next to a file
164
+ # name in the heat map. ex [[:error, 12], [:falure, 13]]
165
+ def sorted_line_number_list(hit)
166
+ # Sort the collected group of line number hits so they're in order
167
+ line_number_tokens_for_hit(hit).sort do |a, b|
168
+ # Ensure the line numbers are integers for sorting (otherwise '100' comes before '12')
169
+ first_line_number = Integer(a[1].strip)
170
+ second_line_number = Integer(b[1].strip)
171
+
172
+ first_line_number <=> second_line_number
70
173
  end
71
- line_number_tokens.compact.sort_by { |number_token| number_token[1] }
72
174
  end
73
175
  end
74
176
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Minitest
4
4
  module Heat
5
- # Friendly API for printing nicely-formatted output to the console
6
5
  class Output
6
+ # Friendly API for printing consistent markers for the various issue types
7
7
  class Marker
8
8
  SYMBOLS = {
9
9
  success: '·',
@@ -12,7 +12,8 @@ module Minitest
12
12
  broken: 'B',
13
13
  error: 'E',
14
14
  skipped: 'S',
15
- failure: 'F'
15
+ failure: 'F',
16
+ reporter: '✖'
16
17
  }.freeze
17
18
 
18
19
  STYLES = {
@@ -22,7 +23,8 @@ module Minitest
22
23
  broken: :error,
23
24
  error: :error,
24
25
  skipped: :skipped,
25
- failure: :failure
26
+ failure: :failure,
27
+ reporter: :error
26
28
  }.freeze
27
29
 
28
30
  attr_accessor :issue_type
@@ -3,6 +3,7 @@
3
3
  module Minitest
4
4
  module Heat
5
5
  class Output
6
+ # Generates the output tokens to display the results summary
6
7
  class Results
7
8
  extend Forwardable
8
9
 
@@ -90,7 +91,7 @@ module Minitest
90
91
  end
91
92
 
92
93
  def test_count_token
93
- [:default, "#{pluralize(timer.test_count, 'test')}"]
94
+ [:default, pluralize(timer.test_count, 'test').to_s]
94
95
  end
95
96
 
96
97
  def tests_performance_token
@@ -98,7 +99,7 @@ module Minitest
98
99
  end
99
100
 
100
101
  def assertions_count_token
101
- [:default, "#{pluralize(timer.assertion_count, 'assertion')}"]
102
+ [:default, pluralize(timer.assertion_count, 'assertion').to_s]
102
103
  end
103
104
 
104
105
  def assertions_performance_token
@@ -3,7 +3,7 @@
3
3
  module Minitest
4
4
  module Heat
5
5
  class Output
6
- # Builds the collection of tokens representing a specific set of source code lines
6
+ # Generates the tokens representing a specific set of source code lines
7
7
  class SourceCode
8
8
  DEFAULT_LINE_COUNT = 3
9
9
  DEFAULT_INDENTATION_SPACES = 2
@@ -2,8 +2,9 @@
2
2
 
3
3
  module Minitest
4
4
  module Heat
5
- # Friendly API for printing nicely-formatted output to the console
6
5
  class Output
6
+ # Provides a convenient interface for creating console-friendly output while ensuring
7
+ # consistency in the applied styles.
7
8
  class Token
8
9
  class InvalidStyle < ArgumentError; end
9
10
 
@@ -11,18 +11,18 @@ require_relative 'output/token'
11
11
  module Minitest
12
12
  module Heat
13
13
  # Friendly API for printing nicely-formatted output to the console
14
- class Output
14
+ class Output # rubocop:disable Metrics/ClassLength
15
15
  SYMBOLS = {
16
16
  middot: '·',
17
17
  arrow: '➜',
18
- lead: '|',
18
+ lead: '|'
19
19
  }.freeze
20
20
 
21
21
  TOKENS = {
22
- spacer: [:muted, " #{SYMBOLS[:middot]} "],
22
+ spacer: [:muted, " #{SYMBOLS[:middot]} "],
23
23
  muted_arrow: [:muted, " #{SYMBOLS[:arrow]} "],
24
- muted_lead: [:muted, "#{SYMBOLS[:lead]} "],
25
- }
24
+ muted_lead: [:muted, "#{SYMBOLS[:lead]} "]
25
+ }.freeze
26
26
 
27
27
  attr_reader :stream
28
28
 
@@ -42,8 +42,33 @@ module Minitest
42
42
  end
43
43
  alias newline puts
44
44
 
45
+ def issues_list(results)
46
+ # A couple of blank lines to create some breathing room
47
+ newline
48
+ newline
49
+
50
+ # Issues start with the least critical and go up to the most critical so that the most
51
+ # pressing issues are displayed at the bottom of the report in order to reduce scrolling.
52
+ #
53
+ # This way, as you fix issues, the list gets shorter, and eventually the least critical
54
+ # issues will be displayed without scrolling once more problematic issues are resolved.
55
+ %i[slows painfuls skips failures brokens errors].each do |issue_category|
56
+ # Only show categories for the most pressing issues after the suite runs, otherwise,
57
+ # suppress them until the more critical issues are resolved.
58
+ next unless show?(issue_category, results)
59
+
60
+ results.send(issue_category).each { |issue| issue_details(issue) }
61
+ end
62
+ rescue StandardError => e
63
+ message = "Sorry, but Minitest Heat couldn't display the details of any failures."
64
+ exception_guidance(message, e)
65
+ end
66
+
45
67
  def issue_details(issue)
46
68
  print_tokens Minitest::Heat::Output::Issue.new(issue).tokens
69
+ rescue StandardError => e
70
+ message = "Sorry, but Minitest Heat couldn't display output for a specific failure."
71
+ exception_guidance(message, e)
47
72
  end
48
73
 
49
74
  def marker(issue_type)
@@ -53,15 +78,56 @@ module Minitest
53
78
  def compact_summary(results, timer)
54
79
  newline
55
80
  print_tokens ::Minitest::Heat::Output::Results.new(results, timer).tokens
81
+ rescue StandardError => e
82
+ message = "Sorry, but Minitest Heat couldn't display the summary."
83
+ exception_guidance(message, e)
56
84
  end
57
85
 
58
86
  def heat_map(map)
59
87
  newline
60
88
  print_tokens ::Minitest::Heat::Output::Map.new(map).tokens
89
+ newline
90
+ rescue StandardError => e
91
+ message = "Sorry, but Minitest Heat couldn't display the heat map."
92
+ exception_guidance(message, e)
61
93
  end
62
94
 
63
95
  private
64
96
 
97
+ # Displays some guidance related to exceptions generated by Minitest Heat in order to help
98
+ # people get back on track (and ideally submit issues)
99
+ # @param message [String] a slightly more specific explanation of which part of minitest-heat
100
+ # caused the failure
101
+ # @param exception [Exception] the exception that caused the problem
102
+ #
103
+ # @return [void] displays the guidance to the console
104
+ def exception_guidance(message, exception)
105
+ newline
106
+ puts "#{message} Disabling Minitest Heat can get you back on track until the problem can be fixed."
107
+ puts 'Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues'
108
+ puts "#{exception.message}:"
109
+ exception.backtrace.each do |line|
110
+ puts " #{line}"
111
+ end
112
+ newline
113
+ end
114
+
115
+ def no_problems?(results)
116
+ !results.problems?
117
+ end
118
+
119
+ def no_problems_or_skips?(results)
120
+ !results.problems? && results.skips.none?
121
+ end
122
+
123
+ def show?(issue_category, results)
124
+ case issue_category
125
+ when :skips then no_problems?(results)
126
+ when :painfuls, :slows then no_problems_or_skips?(results)
127
+ else true
128
+ end
129
+ end
130
+
65
131
  def style_enabled?
66
132
  stream.tty?
67
133
  end
@@ -82,7 +148,11 @@ module Minitest
82
148
  def print_tokens(lines_of_tokens)
83
149
  lines_of_tokens.each do |tokens|
84
150
  tokens.each do |token|
85
- print Token.new(*token).to_s(token_format)
151
+ begin
152
+ print Token.new(*token).to_s(token_format)
153
+ rescue
154
+ puts token.inspect
155
+ end
86
156
  end
87
157
  newline
88
158
  end
@@ -11,9 +11,29 @@ module Minitest
11
11
  @heat_map = Heat::Map.new
12
12
  end
13
13
 
14
+ # Logs an issue to the results for later reporting
15
+ # @param issue [Issue] the issue generated from a given test result
16
+ #
17
+ # @return [type] [description]
14
18
  def record(issue)
19
+ # Record everything—even if it's a success
15
20
  @issues.push(issue)
16
- @heat_map.add(*issue.to_hit) if issue.hit?
21
+
22
+ # If it's not a genuine problem, we're done here...
23
+ return unless issue.hit?
24
+
25
+ # ...otherwise update the heat map
26
+ update_heat_map(issue)
27
+ end
28
+
29
+ def update_heat_map(issue)
30
+ # For heat map purposes, only the project backtrace lines are interesting
31
+ pathname, line_number = issue.locations.project.to_a
32
+
33
+ # Backtrace is only relevant for exception-generating issues, not slows, skips, or failures
34
+ backtrace = issue.error? ? issue.locations.backtrace.project_locations : []
35
+
36
+ @heat_map.add(pathname, line_number, issue.type, backtrace: backtrace)
17
37
  end
18
38
 
19
39
  def problems?
@@ -37,11 +57,11 @@ module Minitest
37
57
  end
38
58
 
39
59
  def painfuls
40
- @painfuls ||= select_issues(:painful).sort_by(&:time).reverse
60
+ @painfuls ||= select_issues(:painful).sort_by(&:execution_time).reverse
41
61
  end
42
62
 
43
63
  def slows
44
- @slows ||= select_issues(:slow).sort_by(&:time).reverse
64
+ @slows ||= select_issues(:slow).sort_by(&:execution_time).reverse
45
65
  end
46
66
 
47
67
  private
@@ -35,7 +35,7 @@ module Minitest
35
35
  #
36
36
  # @return [Array<String>] the range of lines of code around
37
37
  def lines
38
- return [line] if max_line_count == 1
38
+ return [line].compact if max_line_count == 1
39
39
 
40
40
  file_lines[(line_numbers.first - 1)..(line_numbers.last - 1)]
41
41
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Heat
5
- VERSION = '0.0.9'
5
+ VERSION = '0.0.13'
6
6
  end
7
7
  end
data/lib/minitest/heat.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  require_relative 'heat/backtrace'
4
4
  require_relative 'heat/hit'
5
5
  require_relative 'heat/issue'
6
- require_relative 'heat/line'
7
6
  require_relative 'heat/location'
7
+ require_relative 'heat/locations'
8
8
  require_relative 'heat/map'
9
9
  require_relative 'heat/output'
10
10
  require_relative 'heat/results'
@@ -13,7 +13,8 @@ require_relative 'heat/timer'
13
13
  require_relative 'heat/version'
14
14
 
15
15
  module Minitest
16
- # Custom minitest reporter just for Reviewer. Focuses on printing directly actionable guidance.
16
+ # Custom Minitest reporter focused on generating output designed around efficiently identifying
17
+ # issues and potential solutions
17
18
  # - Colorize the Output
18
19
  # - What files had the most errors?
19
20
  # - Show the most impacted areas first.
@@ -2,25 +2,17 @@
2
2
 
3
3
  require_relative 'heat_reporter'
4
4
 
5
- module Minitest
6
- def self.plugin_heat_options(opts, _options)
7
- opts.on '--show-fast', 'Show failures as they happen instead of waiting for the entire suite.' do
8
- # Heat.show_fast!
9
- end
10
-
11
- # TODO: options.
12
- # 1. Fail Fast
13
- # 2. Don't worry about skips.
14
- # 3. Skip coverage.
15
- end
16
-
5
+ module Minitest # rubocop:disable Style/Documentation
17
6
  def self.plugin_heat_init(options)
18
- io = options[:io]
7
+ io = options.fetch(:io, $stdout)
19
8
 
20
- # Clean out the existing reporters.
21
- reporter.reporters = []
9
+ reporter.reporters.reject! do |reporter|
10
+ # Minitest Heat acts as a unified Progress *and* Summary reporter. Using other reporters of
11
+ # those types in conjunction with it creates some overly-verbose output
12
+ reporter.is_a?(ProgressReporter) || reporter.is_a?(SummaryReporter)
13
+ end
22
14
 
23
- # Use Reviewer as the sole reporter.
24
- reporter << HeatReporter.new(io, options)
15
+ # Hook up Reviewer
16
+ self.reporter.reporters << HeatReporter.new(io, options)
25
17
  end
26
18
  end
@@ -35,6 +35,8 @@ module Minitest
35
35
  :results
36
36
 
37
37
  def initialize(io = $stdout, options = {})
38
+ super()
39
+
38
40
  @options = options
39
41
 
40
42
  @timer = Heat::Timer.new
@@ -62,33 +64,39 @@ module Minitest
62
64
  def record(result)
63
65
  # Convert a Minitest Result into an "issue" to more consistently expose the data needed to
64
66
  # adjust the failure output to the type of failure
65
- issue = Heat::Issue.new(result)
67
+ issue = Heat::Issue.from_result(result)
68
+
69
+ # Note the number of assertions for the performance summary
70
+ timer.increment_counts(issue.assertions)
66
71
 
67
- timer.increment_counts(issue.result.assertions)
72
+ # Record the issue to show details later
68
73
  results.record(issue)
69
74
 
75
+ # Show the marker
70
76
  output.marker(issue.type)
77
+ rescue StandardError => e
78
+ display_exception_guidance(e)
71
79
  end
72
80
 
73
- # Outputs the summary of the run.
74
- def report
75
- timer.stop!
76
-
77
- # A couple of blank lines to create some breathing room
81
+ def display_exception_guidance(exception)
78
82
  output.newline
83
+ puts 'Sorry, but Minitest Heat encountered an exception recording an issue. Disabling Minitest Heat will get you back on track.'
84
+ puts 'Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues'
85
+ puts "#{exception.message}:"
86
+ exception.backtrace.each do |line|
87
+ puts " #{line}"
88
+ end
79
89
  output.newline
90
+ end
80
91
 
81
- # Issues start with the least critical and go up to the most critical so that the most
82
- # pressing issues are displayed at the bottom of the report in order to reduce scrolling.
83
- # This way, as you fix issues, the list gets shorter, and eventually the least critical
84
- # issues will be displayed without scrolling once more problematic issues are resolved.
85
- %i[slows painfuls skips failures brokens errors].each do |issue_category|
86
- next unless show?(issue_category)
92
+ # Outputs the summary of the run.
93
+ def report
94
+ timer.stop!
87
95
 
88
- results.send(issue_category).each { |issue| output.issue_details(issue) }
89
- end
96
+ # The list of individual issues and their associated details
97
+ output.issues_list(results)
90
98
 
91
- # Display a short summary of the total issue counts fore ach category as well as performance
99
+ # Display a short summary of the total issue counts for each category as well as performance
92
100
  # details for the test suite as a whole
93
101
  output.compact_summary(results, timer)
94
102
 
@@ -96,7 +104,7 @@ module Minitest
96
104
  # common sources of issues
97
105
  output.heat_map(results)
98
106
 
99
- # A blank line to create some breathing room
107
+ output.newline
100
108
  output.newline
101
109
  end
102
110
 
@@ -104,23 +112,5 @@ module Minitest
104
112
  def passed?
105
113
  results.errors.empty? && results.failures.empty?
106
114
  end
107
-
108
- private
109
-
110
- def no_problems?
111
- !results.problems?
112
- end
113
-
114
- def no_problems_or_skips?
115
- !results.problems? && !results.skips.any?
116
- end
117
-
118
- def show?(issue_category)
119
- case issue_category
120
- when :skips then no_problems?
121
- when :painfuls, :slows then no_problems_or_skips?
122
- else true
123
- end
124
- end
125
115
  end
126
116
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-heat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garrett Dimon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-26 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -141,12 +141,19 @@ files:
141
141
  - Rakefile
142
142
  - bin/console
143
143
  - bin/setup
144
+ - examples/exceptions.png
145
+ - examples/failures.png
146
+ - examples/map.png
147
+ - examples/markers.png
148
+ - examples/skips.png
149
+ - examples/slows.png
144
150
  - lib/minitest/heat.rb
145
151
  - lib/minitest/heat/backtrace.rb
152
+ - lib/minitest/heat/backtrace/line_parser.rb
146
153
  - lib/minitest/heat/hit.rb
147
154
  - lib/minitest/heat/issue.rb
148
- - lib/minitest/heat/line.rb
149
155
  - lib/minitest/heat/location.rb
156
+ - lib/minitest/heat/locations.rb
150
157
  - lib/minitest/heat/map.rb
151
158
  - lib/minitest/heat/output.rb
152
159
  - lib/minitest/heat/output/backtrace.rb
@@ -188,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
195
  - !ruby/object:Gem::Version
189
196
  version: '0'
190
197
  requirements: []
191
- rubygems_version: 3.1.6
198
+ rubygems_version: 3.2.22
192
199
  signing_key:
193
200
  specification_version: 4
194
201
  summary: Presents test results in a visual manner to guide you to where to look first.