Skip to content

Instantly share code, notes, and snippets.

@oddboehs
Created June 9, 2025 01:39
Show Gist options
  • Save oddboehs/8ca545a23fa7edd509947ff09951c67c to your computer and use it in GitHub Desktop.
Save oddboehs/8ca545a23fa7edd509947ff09951c67c to your computer and use it in GitHub Desktop.
SimpleCov Coverage Report Parser - Extract and display line and branch coverage from SimpleCov HTML reports in human-readable format
#!/usr/bin/env ruby
require 'nokogiri'
require 'set'
# Path to the SimpleCov HTML report
coverage_file = File.join(Dir.pwd, 'coverage', 'index.html')
unless File.exist?(coverage_file)
puts "❌ No coverage report found at #{coverage_file}"
puts "Run tests first: bin/rails test"
exit 1
end
# Parse the HTML file
doc = Nokogiri::HTML(File.read(coverage_file))
# Extract overall line coverage
line_percent = doc.css('.covered_percent .green').first&.text&.strip
total_lines = doc.css('.t-line-summary b').first&.text&.strip
covered_lines = doc.css('.t-line-summary .green b').first&.text&.strip
missed_lines = doc.css('.t-line-summary .red b').first&.text&.strip
# Extract overall branch coverage (first t-branch-summary is the overall stats)
overall_branch_summary = doc.css('.t-branch-summary').first
branch_percent = overall_branch_summary.css('span').last.text.strip.gsub(/[()]/,'')
branch_summary_spans = overall_branch_summary.css('span b')
total_branches = branch_summary_spans[0]&.text&.strip
covered_branches = branch_summary_spans[1]&.text&.strip
missed_branches = branch_summary_spans[2]&.text&.strip
# Extract timestamp
timestamp = doc.css('.timestamp .timeago').first&.attr('title')
puts "πŸ“Š SimpleCov Coverage Report"
puts "Generated: #{timestamp}"
puts ""
puts "πŸ“ˆ Line Coverage: #{line_percent}"
puts " βœ… #{covered_lines}/#{total_lines} lines covered"
puts " ❌ #{missed_lines} lines missed"
puts ""
puts "🌳 Branch Coverage: #{branch_percent}"
puts " βœ… #{covered_branches}/#{total_branches} branches covered"
puts " ❌ #{missed_branches} branches missed"
puts ""
# Show file-by-file breakdown if there are missed lines or branches
if missed_lines.to_i > 0 || missed_branches.to_i > 0
puts "πŸ“‹ Files with missing coverage:"
puts ""
files_shown = Set.new
doc.css('tbody .t-file').each do |row|
file_name = row.css('.t-file__name a').first&.text&.strip
line_coverage = row.css('.t-file__coverage').first&.text&.strip
branch_coverage = row.css('.t-file__branch-coverage').first&.text&.strip
# Only show files that aren't 100% covered and haven't been shown yet
if !files_shown.include?(file_name) && (line_coverage != "100.00 %" || branch_coverage != "100.00 %")
files_shown.add(file_name)
puts " #{file_name}"
puts " Line: #{line_coverage}, Branch: #{branch_coverage}"
end
end
end
@oddboehs
Copy link
Author

oddboehs commented Jun 9, 2025

SimpleCov Coverage Report Parser

A Ruby script that extracts and displays SimpleCov coverage data in a clean, human-readable format.

Features

  • πŸ“Š Overall Coverage Summary: Shows line and branch coverage percentages with totals
  • πŸ•’ Timestamp: Displays when the coverage report was generated
  • πŸ” File Breakdown: Lists files with incomplete coverage, showing specific percentages
  • βœ… Error Handling: Gracefully handles missing coverage reports

Usage

# Make executable
chmod +x bin/coverage

# Run after tests generate coverage
bin/coverage

Sample Output

πŸ“Š SimpleCov Coverage Report
Generated: 2025-06-08T20:36:33-05:00

πŸ“ˆ Line Coverage: 98.86%
  βœ… 174/176 lines covered
  ❌ 2 lines missed

🌳 Branch Coverage: 97.06%
  βœ… 33/34 branches covered
  ❌ 1 branches missed

πŸ“‹ Files with missing coverage:

  app/controllers/account_controller.rb
    Line: 92.31 %, Branch: 50.00 %
  app/controllers/concerns/authentication.rb
    Line: 96.88 %, Branch: 100.00 %

Requirements

  • Ruby with Nokogiri gem
  • SimpleCov HTML reports (generated in coverage/index.html)

Integration with Rails Testing

Add to your test/test_helper.rb to automatically show detailed coverage after test runs:

# In parallelize_teardown block
parallelize_teardown do |worker|
  SimpleCov.result
  
  # Show detailed coverage only from the main process after all workers finish
  if worker == 1
    sleep 0.1 # Give a moment for coverage to be written
    if File.exist?("coverage/index.html") && File.exist?("bin/coverage")
      puts ""
      system("bin/coverage")
    end
  end
end

This provides immediate visibility into which files need more test coverage!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment