Skip to content

Commit

Permalink
Add main formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
gondalez committed Aug 21, 2024
1 parent c9cf5da commit 03ec8db
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ PATH
remote: .
specs:
simplecov-inline (0.1.0)
rainbow (~> 3.1)
simplecov (~> 0.22)

GEM
Expand Down
1 change: 1 addition & 0 deletions lib/simplecov/inline.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require_relative 'inline/version'
require_relative 'inline/formatter'

module SimpleCov
module Inline
Expand Down
101 changes: 101 additions & 0 deletions lib/simplecov/inline/formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
require 'rainbow'

module Simplecov
module Inline
class Formatter
Result = Struct.new(:file, :start_line, :end_line, :type) do
def to_s
lines = [start_line, end_line].uniq.join('-')
"#{file}:#{lines} (#{type})"
end
end

Config = Struct.new(:files) do
def no_output!(reason:)
@no_output = reason
end

attr_reader :no_output
end

def self.reset_config = @config = Config.new
reset_config

def self.config(&block)
return @config if block.nil?

block.call(@config)

return if @config.files.nil?

@config.files = @config.files.to_set.freeze
end

def format(result, putter: Kernel)
missing_coverage = process_files(filtered_files(result:).reject { |file| fully_covered?(file) })
success_message = build_success_message(missing_coverage:)

if success_message
putter.puts success_message
return
end

putter.puts
putter.puts Rainbow('Missing coverage:').yellow
putter.puts Rainbow(missing_coverage).yellow
putter.puts
end

private

def build_success_message(missing_coverage:)
if self.class.config.no_output
return Rainbow("Coverage output skipped. Reason: #{self.class.config.no_output}.").yellow
end

return Rainbow("All branches covered (#{human_filter} files) ✔ ").green if missing_coverage.empty?

nil
end

def fully_covered?(file)
[file.covered_percent, file.branches_coverage_percent].all? { |coverage| coverage == 100 }
end

def human_filter
self.class.config.files&.length || 'all'
end

def filtered_files(result:)
filter = self.class.config.files

return result.files if filter.nil?

result.files.filter { |file| filter.include?(file.filename) }
end

def process_files(files)
files.map do |file|
[
build_line_coverage(file),
build_branch_coverage(file),
]
end.flatten.compact.join("\n")
end

def build_line_coverage(file)
file.missed_lines.map do |uncovered|
# uncovered is a SimpleCov::SourceFile::Line
Result.new(file.project_filename, uncovered.line_number, uncovered.line_number, 'line')
end
end

def build_branch_coverage(file)
file.missed_branches.map do |uncovered|
# uncovered is a SimpleCov::SourceFile::Branch
Result.new(file.project_filename, uncovered.report_line, uncovered.report_line, "branch:#{uncovered.type}")
end
end
end
end
end
1 change: 1 addition & 0 deletions simplecov-inline.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_dependency 'rainbow', '~> 3.1'
spec.add_dependency 'simplecov', '~> 0.22'

# For more information and examples about making a new gem, check out our
Expand Down
5 changes: 5 additions & 0 deletions spec/fixtures/file1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class File1
def method(arg)
arg ? 1 : 2
end
end
5 changes: 5 additions & 0 deletions spec/fixtures/file2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class File2
def method(arg)
arg ? 1 : 2
end
end
101 changes: 101 additions & 0 deletions spec/lib/simplecov/inline/formatter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
RSpec.describe Simplecov::Inline::Formatter do
after { described_class.reset_config }

describe '#format' do
subject { described_class.new.format(result, putter:) }

let(:putter) { class_double(Kernel) }

before { allow(putter).to receive(:puts) }

context 'missing coverage' do
let(:result) do
SimpleCov::Result.new({
File.join(fixture_directory, 'file1.rb') => {
# 0 means line 3 is uncovered
'lines' => [1, 1, 0, 1, 1],
# => 0 means the else branch is uncovered.
# 2 is an idenditifier, 3 is the start line, 12 is the start col, 3 is the end line and 14 is the end col.
'branches' => {[:if, 0, 3, 4, 3, 8] => {[:then, 1, 3, 8, 3, 10] => 1, [:else, 2, 3, 12, 3, 14] => 0}},
},
})
end

it 'prints a yellow message saying coverage is missing' do
subject

expect(putter).to have_received(:puts).with(Rainbow('Missing coverage:').yellow)
end

it 'prints the missing lines and branches in yellow' do
subject

expect(putter).to have_received(:puts).with(Rainbow([
'/spec/fixtures/file1.rb:3 (line)',
'/spec/fixtures/file1.rb:3 (branch:else)',
].join("\n")).yellow)
end
end

context 'fully covered' do
let(:result) do
SimpleCov::Result.new({
File.join(fixture_directory, 'file1.rb') => {
'lines' => [1, 1, 1, 1, 1],
'branches' => {[:if, 0, 3, 4, 3, 8] => {[:then, 1, 3, 8, 3, 10] => 1, [:else, 2, 3, 12, 3, 14] => 1}},
},
})
end

it 'prints a green message saying fully covered' do
subject
expect(putter).to have_received(:puts).with(Rainbow('All branches covered (all files) ✔ ').green)
end
end

context 'output disabled' do
let(:result) { SimpleCov::Result.new({}) }

before do
described_class.config { |config| config.no_output!(reason: 'no output thanks') }
end

it 'prints a yellow message saying output is disabled' do
subject
expect(putter).to have_received(:puts).with(Rainbow('Coverage output skipped. Reason: no output thanks.').yellow)
end
end

context 'filtering out of uncovered files' do
let(:result) do
SimpleCov::Result.new({
File.join(fixture_directory, 'file1.rb') => {'lines' => [1, 1, 1, 1, 1]},
File.join(fixture_directory, 'file2.rb') => {'lines' => [1, 1, 0, 1, 1]},
})
end

before do
described_class.config { |config| config.files = [File.join(fixture_directory, 'file1.rb')]}
end

it 'prints a green message saying fully covered, but notes only 1 file was considered' do
subject
expect(putter).to have_received(:puts).with(Rainbow('All branches covered (1 files) ✔ ').green)
end
end
end

describe '.config' do
subject { described_class.config }

it { is_expected.to have_attributes files: nil }
it { is_expected.to respond_to :no_output! }

it 'can change the files' do
expect { described_class.config { |config| config.files = ['test.txt'] } }
.to change { described_class.config.files }
.from(nil)
.to(['test.txt'])
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@
c.syntax = :expect
end
end

def fixture_directory = @fixture_directory ||= File.join(File.dirname(__FILE__), 'fixtures')

0 comments on commit 03ec8db

Please sign in to comment.