Created
March 30, 2025 00:48
-
-
Save mfittko/a83a5e30fc5a39c724a34e30372df4f4 to your computer and use it in GitHub Desktop.
The missing SimpleCov terminal formatter. Shows only missing lines.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'colorize' | |
require 'tty-table' | |
module SimpleCov | |
module Formatter | |
class MissingFormatter | |
def initialize(output = nil) | |
@output = output || $stdout | |
end | |
def format(result) | |
has_imperfect_files = print_groups(result) | |
print_summary(result, has_imperfect_files) | |
@output.puts "\n" | |
end | |
def color(percent) | |
case percent | |
when 90..100 then :green | |
when 80..90 then :yellow | |
else :red | |
end | |
end | |
private | |
def print_groups(result) | |
has_imperfect_files = false | |
result.groups.each do |name, files| | |
imperfect_files = files.select { |file| file.covered_percent < 100 } | |
next if imperfect_files.empty? | |
unless has_imperfect_files | |
@output.puts "\nSimpleCov Missing Coverage Report:".colorize(mode: :bold) | |
@output.puts '==============================================' | |
has_imperfect_files = true | |
end | |
print_group_header(name, files.covered_percent) | |
print_imperfect_files(imperfect_files) | |
end | |
has_imperfect_files | |
end | |
def print_group_header(name, covered_percent) | |
percentage = covered_percent.round(1) | |
emoji = if percentage >= 90 | |
'✅' | |
else | |
percentage >= 80 ? '⚠️' : '❌' | |
end | |
header = " #{emoji} #{name}: #{percentage.to_s.rjust(5)}%" | |
@output.puts "\n#{header}".colorize(color: color(percentage), mode: :bold) | |
end | |
def print_imperfect_files(files) | |
table = TTY::Table.new(header: ['Coverage', 'File', 'Missing Lines']) | |
files.sort_by(&:covered_percent).each do |file| | |
percentage = file.covered_percent.round(2) | |
filename = file.filename.gsub(%r{^#{Dir.pwd}/}, '') | |
missed_lines = file.missed_lines.map(&:line_number) | |
lines_str = missed_lines.empty? ? '' : format_line_ranges(missed_lines) | |
table << [ | |
" • #{percentage.to_s.rjust(5)}%".colorize(color(percentage)), | |
filename, | |
wrap_long_lines(lines_str) | |
] | |
end | |
@output.puts table.render(:unicode, padding: [0, 1, 0, 1], multiline: true) | |
end | |
def print_summary(result, has_imperfect_files) | |
if has_imperfect_files | |
percentage = result.covered_percent.round(1) | |
emoji = if percentage >= 90 | |
'✅' | |
else | |
percentage >= 80 ? '⚠️' : '❌' | |
end | |
summary = " #{emoji} Total: #{percentage.to_s.rjust(5)}%" | |
@output.puts "\n#{summary}".colorize(color: color(percentage), mode: :bold) | |
else | |
@output.puts "\n🎉 Perfect Coverage! All files at 100%! 🎉".colorize(color: :green, mode: :bold) | |
end | |
end | |
def format_line_ranges(numbers) | |
return '' if numbers.empty? | |
ranges = [] | |
start_num = numbers.first | |
prev_num = start_num | |
numbers.drop(1).each do |num| | |
if num == prev_num + 1 | |
prev_num = num | |
else | |
ranges << (start_num == prev_num ? start_num.to_s : "#{start_num}-#{prev_num}") | |
start_num = prev_num = num | |
end | |
end | |
ranges << (start_num == prev_num ? start_num.to_s : "#{start_num}-#{prev_num}") | |
ranges.join(', ') | |
end | |
def wrap_long_lines(lines_str) | |
return '' if lines_str.empty? | |
if lines_str.length > 60 | |
lines_str.scan(/.{1,60}(?:,|$)/).join("\n").colorize(:red) | |
else | |
lines_str.colorize(:red) | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment