minitest-heat 0.0.10 → 0.0.11

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.
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minitest
4
+ module Heat
5
+ # Convenience methods for determining the file and line number where the problem occurred.
6
+ # There are several layers of specificity to help make it easy to communicate the relative
7
+ # location of the failure:
8
+ # - 'final' represents the final line of the backtrace regardless of where it is
9
+ # - 'test_definition' represents where the test is defined
10
+ # - 'test_failure' represents the last line from the project's tests. It is further differentiated by
11
+ # the line where the test is defined and the actual line of code in the test that geneated
12
+ # the failure or exception
13
+ # - 'source_code' represents the last line from the project's source code
14
+ # - 'project' represents the last source line, but falls back to the last test line
15
+ # - 'most_relevant' represents the most specific file to investigate starting with the source
16
+ # code and then looking to the test code with final line of the backtrace as a fallback
17
+ class Locations
18
+ attr_reader :test_definition, :backtrace
19
+
20
+ def initialize(test_definition_location, backtrace = [])
21
+ test_definition_pathname, test_definition_line_number = test_definition_location
22
+ @test_definition = ::Minitest::Heat::Location.new(pathname: test_definition_pathname, line_number: test_definition_line_number)
23
+
24
+ @backtrace = Backtrace.new(backtrace)
25
+ end
26
+
27
+ # Prints the pathname and line number of the location most likely to be the source of the
28
+ # test failure
29
+ #
30
+ # @return [String] ex. 'path/to/file.rb:12'
31
+ def to_s
32
+ "#{most_relevant.absolute_filename}:#{most_relevant.line_number}"
33
+ end
34
+
35
+ # Knows if the failure is contained within the test. For example, if there's bad code in a
36
+ # test, and it raises an exception, then it's really a broken test rather than a proper
37
+ # faiure.
38
+ #
39
+ # @return [Boolean] true if final file in the backtrace is the same as the test location file
40
+ def broken_test?
41
+ !test_failure.nil? && test_failure == final
42
+ end
43
+
44
+ # Knows if the failure occurred in the actual project source code—as opposed to the test or
45
+ # an external piece of code like a gem.
46
+ #
47
+ # @return [Boolean] true if there's a non-test project file in the stacktrace but it's not
48
+ # a result of a broken test
49
+ def proper_failure?
50
+ !source_code.nil? && !broken_test?
51
+ end
52
+
53
+ # The file most likely to be the source of the underlying problem. Often, the most recent
54
+ # backtrace files will be a gem or external library that's failing indirectly as a result
55
+ # of a problem with local source code (not always, but frequently). In that case, the best
56
+ # first place to focus is on the code you control.
57
+ #
58
+ # @return [Array] file and line number of the most likely source of the problem
59
+ def most_relevant
60
+ [
61
+ source_code,
62
+ test_failure,
63
+ final
64
+ ].compact.first
65
+ end
66
+
67
+ def freshest
68
+ backtrace.recently_modified_locations.first
69
+ end
70
+
71
+ # Returns the final test location based on the backtrace if present. Otherwise falls back to
72
+ # the test location which represents the test definition. The `test_definition` attribute
73
+ # provides the location of where the test is defined. `test_failure` represents the actual
74
+ # line from within the test where the problem occurred
75
+ #
76
+ # @return [Location] the final location from the test files
77
+ def test_failure
78
+ backtrace.test_locations.any? ? backtrace.test_locations.first : test_definition
79
+ end
80
+
81
+ # Returns the final source code location based on the backtrace
82
+ #
83
+ # @return [Location] the final location from the source code files
84
+ def source_code
85
+ backtrace.source_code_locations.first
86
+ end
87
+
88
+ # Returns the final project location based on the backtrace if present. Otherwise falls back
89
+ # to the test location which represents the test definition.
90
+ #
91
+ # @return [Location] the final location from the project files
92
+ def project
93
+ backtrace.project_locations.any? ? backtrace.project_locations.first : test_definition
94
+ end
95
+
96
+ # The line number from within the `test_file` test definition where the failure occurred
97
+ #
98
+ # @return [Location] the last location from the backtrace or the test location if a backtrace
99
+ # was not passed to the initializer
100
+ def final
101
+ backtrace.locations.any? ? backtrace.locations.first : test_definition
102
+ end
103
+ end
104
+ end
105
+ end
@@ -18,10 +18,9 @@ module Minitest
18
18
  # @param type [Symbol] the type of issue that was encountered (i.e. :failure, :error, etc.)
19
19
  #
20
20
  # @return [void]
21
- def add(filename, line_number, type)
22
- @hits[filename] ||= Hit.new(filename)
23
-
24
- @hits[filename].log(type.to_sym, line_number)
21
+ def add(pathname, line_number, type, backtrace: [])
22
+ @hits[pathname.to_s] ||= Hit.new(pathname)
23
+ @hits[pathname.to_s].log(type.to_sym, line_number, backtrace: backtrace)
25
24
  end
26
25
 
27
26
  # Returns a subset of affected files to keep the list from being overwhelming
@@ -8,11 +8,11 @@ module Minitest
8
8
  DEFAULT_LINE_COUNT = 10
9
9
  DEFAULT_INDENTATION_SPACES = 2
10
10
 
11
- attr_accessor :location, :backtrace
11
+ attr_accessor :locations, :backtrace
12
12
 
13
- def initialize(location)
14
- @location = location
15
- @backtrace = location.backtrace
13
+ def initialize(locations)
14
+ @locations = locations
15
+ @backtrace = locations.backtrace
16
16
  @tokens = []
17
17
  end
18
18
 
@@ -21,14 +21,8 @@ module Minitest
21
21
  # final backtrace line if it might be relevant/helpful?
22
22
 
23
23
  # Iterate over the selected lines from the backtrace
24
- backtrace_entries.each do |backtrace_entry|
25
- # Get the source code for the line from the backtrace
26
- parts = backtrace_line_tokens(backtrace_entry)
27
-
28
- # If it's the most recently modified file in the trace, add the token for that
29
- parts << file_freshness(backtrace_entry) if most_recently_modified?(backtrace_entry)
30
-
31
- @tokens << parts
24
+ backtrace_locations.each do |location|
25
+ @tokens << backtrace_location_tokens(location)
32
26
  end
33
27
 
34
28
  @tokens
@@ -47,71 +41,74 @@ module Minitest
47
41
  # ...it could be influenced by a "compact" or "robust" reporter super-style?
48
42
  # ...it's smart about exceptions that were raised outside of the project?
49
43
  # ...it's smart about highlighting lines of code differently based on whether it's source code, test code, or external code?
50
- def backtrace_entries
51
- all_entries
44
+ def backtrace_locations
45
+ backtrace.locations.take(line_count)
52
46
  end
53
47
 
54
48
  private
55
49
 
56
- def backtrace_line_tokens(backtrace_entry)
50
+ def backtrace_location_tokens(location)
57
51
  [
58
52
  indentation_token,
59
- path_token(backtrace_entry),
60
- *file_and_line_number_tokens(backtrace_entry),
61
- source_code_line_token(backtrace_entry.source_code)
62
- ]
63
- end
64
-
65
- def all_backtrace_entries_from_project?
66
- backtrace_entries.all? { |line| line.path.to_s.include?(project_root_dir) }
67
- end
68
-
69
- def project_root_dir
70
- Dir.pwd
71
- end
72
-
73
- def project_entries
74
- backtrace.project_entries.take(line_count)
53
+ path_token(location),
54
+ *file_and_line_number_tokens(location),
55
+ containining_element_token(location),
56
+ source_code_line_token(location),
57
+ most_recently_modified_token(location),
58
+ ].compact
75
59
  end
76
60
 
77
- def all_entries
78
- backtrace.parsed_entries.take(line_count)
61
+ # Determines if all lines to be displayed are from within the project directory
62
+ #
63
+ # @return [Boolean] true if all lines of the backtrace being displayed are from the project
64
+ def all_backtrace_from_project?
65
+ backtrace_locations.all?(&:project_file?)
79
66
  end
80
67
 
81
- def most_recently_modified?(line)
68
+ def most_recently_modified?(location)
82
69
  # If there's more than one line being displayed, and the current line is the freshest
83
- backtrace_entries.size > 1 && line == backtrace.freshest_project_location
70
+ backtrace_locations.size > 1 && location == locations.freshest
84
71
  end
85
72
 
86
73
  def indentation_token
87
74
  [:default, ' ' * indentation]
88
75
  end
89
76
 
90
- def path_token(line)
91
- style = line.to_s.include?(Dir.pwd) ? :default : :muted
92
- path = "#{line.path}/"
77
+ def path_token(location)
78
+ # If the line is a project file, help it stand out from the backtrace noise
79
+ style = location.project_file? ? :default : :muted
93
80
 
94
- # If all of the backtrace lines are from the project, no point in the added redundant
95
- # noise of showing the project root directory over and over again
96
- path = path.delete_prefix(project_root_dir) if all_backtrace_entries_from_project?
81
+ # If *all* of the backtrace lines are from the project, no point in the added redundant
82
+ # noise of showing the project root directory over and over again
83
+ path_format = all_backtrace_from_project? ? :relative_path : :absolute_path
97
84
 
98
- [style, path]
85
+ [style, location.send(path_format)]
99
86
  end
100
87
 
101
- def file_and_line_number_tokens(backtrace_entry)
102
- style = backtrace_entry.to_s.include?(Dir.pwd) ? :bold : :muted
88
+ def file_and_line_number_tokens(location)
89
+ style = location.to_s.include?(Dir.pwd) ? :bold : :muted
103
90
  [
104
- [style, backtrace_entry.file],
91
+ [style, location.filename],
105
92
  [:muted, ':'],
106
- [style, backtrace_entry.line_number]
93
+ [style, location.line_number]
107
94
  ]
108
95
  end
109
96
 
110
- def source_code_line_token(source_code)
111
- [:muted, " #{Output::SYMBOLS[:arrow]} `#{source_code.line.strip}`"]
97
+ def source_code_line_token(location)
98
+ return nil unless location.project_file?
99
+
100
+ [:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"]
101
+ end
102
+
103
+ def containining_element_token(location)
104
+ return nil if !location.project_file? || location.container.nil? || location.container.empty?
105
+
106
+ [:muted, " in #{location.container}"]
112
107
  end
113
108
 
114
- def file_freshness(_line)
109
+ def most_recently_modified_token(location)
110
+ return nil unless most_recently_modified?(location)
111
+
115
112
  [:default, " #{Output::SYMBOLS[:middot]} Most Recently Modified File"]
116
113
  end
117
114
 
@@ -126,10 +123,6 @@ module Minitest
126
123
  def indentation
127
124
  DEFAULT_INDENTATION_SPACES
128
125
  end
129
-
130
- def style_for(path)
131
- path.to_s.include?(Dir.pwd) ? :default : :muted
132
- end
133
126
  end
134
127
  end
135
128
  end
@@ -5,10 +5,11 @@ module Minitest
5
5
  class Output
6
6
  # Formats issues to output based on the issue type
7
7
  class Issue # rubocop:disable Metrics/ClassLength
8
- attr_accessor :issue
8
+ attr_accessor :issue, :locations
9
9
 
10
10
  def initialize(issue)
11
11
  @issue = issue
12
+ @locations = issue.locations
12
13
  end
13
14
 
14
15
  def tokens
@@ -92,7 +93,7 @@ module Minitest
92
93
  end
93
94
  end
94
95
 
95
- def label(issue)
96
+ def label(issue) # rubocop:disable Metrics
96
97
  if issue.error? && issue.in_test?
97
98
  # When the exception came out of the test itself, that's a different kind of exception
98
99
  # that really only indicates there's a problem with the code in the test. It's kind of
@@ -118,24 +119,34 @@ module Minitest
118
119
  end
119
120
 
120
121
  def backtrace_tokens
121
- backtrace = ::Minitest::Heat::Output::Backtrace.new(issue.location)
122
-
123
- backtrace.tokens
122
+ @backtrace_tokens ||= ::Minitest::Heat::Output::Backtrace.new(locations).tokens
124
123
  end
125
124
 
126
125
  def test_location_tokens
127
- [[:default, test_file_short_location], [:muted, ':'], [:default, issue.test_definition_line], arrow_token, [:default, issue.test_failure_line], [:muted, test_line_source]]
126
+ [
127
+ [:default, locations.test_definition.relative_filename],
128
+ [:muted, ':'],
129
+ [:default, locations.test_definition.line_number],
130
+ arrow_token,
131
+ [:default, locations.test_failure.line_number],
132
+ [:muted, "\n #{locations.test_failure.source_code.line.strip}"]
133
+ ]
128
134
  end
129
135
 
130
136
  def location_tokens
131
- [[:default, most_relevant_short_location], [:muted, ':'], [:default, issue.location.most_relevant_failure_line], [:muted, most_relevant_line_source]]
137
+ [
138
+ [:default, locations.most_relevant.relative_filename],
139
+ [:muted, ':'],
140
+ [:default, locations.most_relevant.line_number],
141
+ [:muted, "\n #{locations.most_relevant.source_code.line.strip}"]
142
+ ]
132
143
  end
133
144
 
134
145
  def source_tokens
135
- filename = issue.location.project_file
136
- line_number = issue.location.project_failure_line
137
-
146
+ filename = locations.project.filename
147
+ line_number = locations.project.line_number
138
148
  source = Minitest::Heat::Source.new(filename, line_number: line_number)
149
+
139
150
  [[:muted, " #{Output::SYMBOLS[:arrow]} `#{source.line.strip}`"]]
140
151
  end
141
152
 
@@ -147,9 +158,10 @@ module Minitest
147
158
  [
148
159
  [:bold, slowness(issue)],
149
160
  spacer_token,
150
- [:default, issue.location.test_file.to_s.delete_prefix(Dir.pwd)],
161
+ [:default, locations.test_definition.relative_path],
162
+ [:default, locations.test_definition.filename],
151
163
  [:muted, ':'],
152
- [:default, issue.location.test_definition_line]
164
+ [:default, locations.test_definition.line_number]
153
165
  ]
154
166
  end
155
167
 
@@ -161,30 +173,6 @@ module Minitest
161
173
  []
162
174
  end
163
175
 
164
- def most_relevant_short_location
165
- issue.location.most_relevant_file.to_s.delete_prefix("#{Dir.pwd}/")
166
- end
167
-
168
- def test_file_short_location
169
- issue.location.test_file.to_s.delete_prefix("#{Dir.pwd}/")
170
- end
171
-
172
- def most_relevant_line_source
173
- filename = issue.location.project_file
174
- line_number = issue.location.project_failure_line
175
-
176
- source = Minitest::Heat::Source.new(filename, line_number: line_number)
177
- "\n #{source.line.strip}"
178
- end
179
-
180
- def test_line_source
181
- filename = issue.location.test_file
182
- line_number = issue.location.test_failure_line
183
-
184
- source = Minitest::Heat::Source.new(filename, line_number: line_number)
185
- "\n #{source.line.strip}"
186
- end
187
-
188
176
  def spacer_token
189
177
  Output::TOKENS[:spacer]
190
178
  end
@@ -13,16 +13,26 @@ module Minitest
13
13
  end
14
14
 
15
15
  def tokens
16
- map.file_hits.each do |hit|
17
- file_tokens = pathname(hit)
18
- line_number_tokens = line_numbers(hit)
16
+ results.heat_map.file_hits.each do |hit|
17
+ # If there's legitimate failures or errors, skips and slows aren't relevant
18
+ next unless relevant_issue_types?(hit)
19
19
 
20
- next if line_number_tokens.empty?
20
+ @tokens << [[:muted, ""]]
21
+ @tokens << file_summary_tokens(hit)
21
22
 
22
- @tokens << [
23
- *file_tokens,
24
- *line_number_tokens
25
- ]
23
+ repeats = repeated_line_numbers(hit)
24
+ next unless repeats.any?
25
+
26
+ repeats.each do |line_number|
27
+ @tokens << [[:muted, " Issues on Line #{line_number} initially triggered from these locations:"]]
28
+
29
+ traces = hit.lines[line_number.to_s]
30
+ sorted_traces = traces.sort_by { |trace| trace.locations.last.line_number }
31
+
32
+ sorted_traces.each do |trace|
33
+ @tokens << origination_location_token(trace)
34
+ end
35
+ end
26
36
  end
27
37
 
28
38
  @tokens
@@ -30,8 +40,26 @@ module Minitest
30
40
 
31
41
  private
32
42
 
33
- def map
34
- results.heat_map
43
+ def file_summary_tokens(hit)
44
+ pathname_tokens = pathname(hit)
45
+ line_number_list_tokens = sorted_line_number_list(hit)
46
+
47
+ [*pathname_tokens, *line_number_list_tokens]
48
+ end
49
+
50
+ def origination_location_token(trace)
51
+ # The earliest project line from the backtrace—this is probabyl wholly incorrect in terms
52
+ # of what would be the most helpful line to display, but it's a start.
53
+ location = trace.locations.last
54
+
55
+ [
56
+ [:muted, " #{Output::SYMBOLS[:arrow]} "],
57
+ [:default, location.relative_filename],
58
+ [:muted, ':'],
59
+ [:default, location.line_number],
60
+ [:muted, " in #{location.container}"],
61
+ [:muted, " #{Output::SYMBOLS[:arrow]} `location.source_code.line.strip`"],
62
+ ]
35
63
  end
36
64
 
37
65
  def relevant_issue_types
@@ -46,37 +74,69 @@ module Minitest
46
74
  issue_types
47
75
  end
48
76
 
49
- def pathname(file)
50
- directory = "#{file.pathname.dirname.to_s.delete_prefix(Dir.pwd)}/".delete_prefix('/')
51
- filename = file.pathname.basename.to_s
77
+ def relevant_issue_types?(hit)
78
+ intersection_issue_types = relevant_issue_types & hit.issues.keys
52
79
 
53
- [
54
- [:default, directory],
55
- [:bold, filename],
56
- [:default, ' · ']
57
- ]
80
+ intersection_issue_types.any?
58
81
  end
59
82
 
60
- def hit_line_numbers(file, issue_type)
61
- numbers = []
62
- line_numbers_for_issue_type = file.issues.fetch(issue_type) { [] }
63
- line_numbers_for_issue_type.map do |line_number|
64
- numbers << [issue_type, "#{line_number} "]
83
+ def repeated_line_numbers(hit)
84
+ repeated_line_numbers = []
85
+
86
+ hit.lines.each_pair do |line_number, traces|
87
+ # If there aren't multiple traces for a line number, it's not a repeat, right?
88
+ next unless traces.size > 1
89
+
90
+ repeated_line_numbers << Integer(line_number)
65
91
  end
66
92
 
67
- numbers
93
+ repeated_line_numbers.sort
68
94
  end
69
95
 
70
- def line_numbers(file)
96
+ def repeated_line_numbers?(hit)
97
+ repeated_line_numbers(hit).any?
98
+ end
99
+
100
+ def pathname(hit)
101
+ directory = hit.pathname.dirname.to_s.delete_prefix("#{Dir.pwd}/")
102
+ filename = hit.pathname.basename.to_s
103
+
104
+ [
105
+ [:default, "#{directory}/"],
106
+ [:bold, filename],
107
+ [:default, ' · ']
108
+ ]
109
+ end
110
+
111
+ def line_number_tokens_for_hit(hit)
71
112
  line_number_tokens = []
72
113
 
73
- # Merge the hits for all issue types into one list
74
114
  relevant_issue_types.each do |issue_type|
75
- line_number_tokens += hit_line_numbers(file, issue_type)
115
+ # Retrieve any line numbers for the issue type
116
+ line_numbers_for_issue_type = hit.issues.fetch(issue_type) { [] }
117
+
118
+ # Build the list of tokens representing styled line numbers
119
+ line_numbers_for_issue_type.each do |line_number|
120
+ line_number_tokens << line_number_token(issue_type, line_number)
121
+ end
76
122
  end
77
123
 
124
+ line_number_tokens.compact
125
+ end
126
+
127
+ def line_number_token(style, line_number)
128
+ [style, "#{line_number} "]
129
+ end
130
+
131
+ # Generates the line number tokens styled based on their error type
132
+ #
133
+ # @param [Hit] hit the instance of the hit file details to build the heat map entry
134
+ #
135
+ # @return [Array] the arrays representing the line number tokens to display next to a file
136
+ # name in the heat map
137
+ def sorted_line_number_list(hit)
78
138
  # Sort the collected group of line number hits so they're in order
79
- line_number_tokens.compact.sort do |a, b|
139
+ line_number_tokens_for_hit(hit).sort do |a, b|
80
140
  # Ensure the line numbers are integers for sorting (otherwise '100' comes before '12')
81
141
  first_line_number = Integer(a[1].strip)
82
142
  second_line_number = Integer(b[1].strip)
@@ -11,7 +11,7 @@ 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: '➜',
@@ -56,14 +56,14 @@ module Minitest
56
56
 
57
57
  results.send(issue_category).each { |issue| issue_details(issue) }
58
58
  end
59
- rescue => e
59
+ rescue StandardError => e
60
60
  message = "Sorry, but Minitest Heat couldn't display the details of any failures."
61
61
  exception_guidance(message, e)
62
62
  end
63
63
 
64
64
  def issue_details(issue)
65
65
  print_tokens Minitest::Heat::Output::Issue.new(issue).tokens
66
- rescue => e
66
+ rescue StandardError => e
67
67
  message = "Sorry, but Minitest Heat couldn't display output for a failure."
68
68
  exception_guidance(message, e)
69
69
  end
@@ -75,7 +75,7 @@ module Minitest
75
75
  def compact_summary(results, timer)
76
76
  newline
77
77
  print_tokens ::Minitest::Heat::Output::Results.new(results, timer).tokens
78
- rescue => e
78
+ rescue StandardError => e
79
79
  message = "Sorry, but Minitest Heat couldn't display the summary."
80
80
  exception_guidance(message, e)
81
81
  end
@@ -84,7 +84,7 @@ module Minitest
84
84
  newline
85
85
  print_tokens ::Minitest::Heat::Output::Map.new(map).tokens
86
86
  newline
87
- rescue => e
87
+ rescue StandardError => e
88
88
  message = "Sorry, but Minitest Heat couldn't display the heat map."
89
89
  exception_guidance(message, e)
90
90
  end
@@ -92,7 +92,7 @@ module Minitest
92
92
  def exception_guidance(message, exception)
93
93
  newline
94
94
  puts "#{message} Disabling Minitest Heat can get you back on track until the problem can be fixed."
95
- puts "Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues"
95
+ puts 'Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues'
96
96
  puts "#{exception.message}:"
97
97
  exception.backtrace.each do |line|
98
98
  puts " #{line}"
@@ -138,7 +138,11 @@ module Minitest
138
138
  def print_tokens(lines_of_tokens)
139
139
  lines_of_tokens.each do |tokens|
140
140
  tokens.each do |token|
141
- print Token.new(*token).to_s(token_format)
141
+ begin
142
+ print Token.new(*token).to_s(token_format)
143
+ rescue
144
+ puts token.inspect
145
+ end
142
146
  end
143
147
  newline
144
148
  end
@@ -19,16 +19,21 @@ module Minitest
19
19
  # Record everything—even if it's a success
20
20
  @issues.push(issue)
21
21
 
22
- # If it's not a genuine problem, we're done here, otherwise update the heat map
23
- update_heat_map(issue) if issue.hit?
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)
24
27
  end
25
28
 
26
29
  def update_heat_map(issue)
27
- # Get the elements we need to generate a heat map entry
28
- pathname = issue.location.project_file.to_s
29
- line_number = issue.location.project_failure_line.to_i
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 : []
30
35
 
31
- @heat_map.add(pathname, line_number, issue.type)
36
+ @heat_map.add(pathname, line_number, issue.type, backtrace: backtrace)
32
37
  end
33
38
 
34
39
  def problems?
@@ -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.10'
5
+ VERSION = '0.0.11'
6
6
  end
7
7
  end
data/lib/minitest/heat.rb CHANGED
@@ -4,6 +4,7 @@ require_relative 'heat/backtrace'
4
4
  require_relative 'heat/hit'
5
5
  require_relative 'heat/issue'
6
6
  require_relative 'heat/location'
7
+ require_relative 'heat/locations'
7
8
  require_relative 'heat/map'
8
9
  require_relative 'heat/output'
9
10
  require_relative 'heat/results'
@@ -6,7 +6,7 @@ module Minitest # rubocop:disable Style/Documentation
6
6
  def self.plugin_heat_init(options)
7
7
  io = options.fetch(:io, $stdout)
8
8
 
9
- self.reporter.reporters.reject! do |reporter|
9
+ reporter.reporters.reject! do |reporter|
10
10
  # Minitest Heat acts as a unified Progress *and* Summary reporter. Using other reporters of
11
11
  # those types in conjunction with it creates some overly-verbose output
12
12
  reporter.is_a?(ProgressReporter) || reporter.is_a?(SummaryReporter)