diff --git a/lib/coverband.rb b/lib/coverband.rb index 7c357e83..8e4d00fc 100644 --- a/lib/coverband.rb +++ b/lib/coverband.rb @@ -137,6 +137,7 @@ def initialize require "coverband/utils/lines_classifier" require "coverband/utils/results" require "coverband/reporters/html_report" + require "coverband/reporters/json_report" init_web end end diff --git a/lib/coverband/reporters/json_report.rb b/lib/coverband/reporters/json_report.rb new file mode 100644 index 00000000..09d7a1e6 --- /dev/null +++ b/lib/coverband/reporters/json_report.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Outputs data in json format similar to what is shown in the HTML page +# Top level and file level coverage numbers +module Coverband + module Reporters + class JSONReport < Base + attr_accessor :filtered_report_files + + def initialize(store, options = {}) + coverband_reports = Coverband::Reporters::Base.report(store, options) + self.filtered_report_files = self.class.fix_reports(coverband_reports) + end + + def report + report_as_json + end + + private + + def report_as_json + result = Coverband::Utils::Results.new(filtered_report_files) + source_files = result.source_files + { + **coverage_totals(source_files), + files: coverage_files(result, source_files) + }.to_json + end + + def coverage_totals(source_files) + { + total_files: source_files.length, + lines_of_code: source_files.lines_of_code, + lines_covered: source_files.covered_lines, + lines_missed: source_files.missed_lines, + covered_strength: source_files.covered_strength, + covered_percent: source_files.covered_percent + } + end + + # Using a hash indexed by file name for quick lookups + def coverage_files(result, source_files) + source_files.each_with_object({}) do |source_file, hash| + hash[source_file.short_name] = { + never_loaded: source_file.never_loaded, + runtime_percentage: result.runtime_relevant_coverage(source_file), + lines_of_code: source_file.lines.count, + lines_covered: source_file.covered_lines.count, + lines_missed: source_file.missed_lines.count, + covered_percent: source_file.covered_percent, + covered_strength: source_file.covered_strength + } + end + end + end + end +end diff --git a/lib/coverband/reporters/web.rb b/lib/coverband/reporters/web.rb index e1d58f09..2a44cc4d 100644 --- a/lib/coverband/reporters/web.rb +++ b/lib/coverband/reporters/web.rb @@ -90,6 +90,8 @@ def call(env) [200, coverband_headers(content_type: "text/json"), [debug_data]] when %r{\/load_file_details} [200, coverband_headers(content_type: "text/json"), [load_file_details]] + when %r{\/json} + [200, coverband_headers(content_type: "text/json"), [json]] when %r{\/$} [200, coverband_headers, [index]] else @@ -109,6 +111,10 @@ def index open_report: false).report end + def json + Coverband::Reporters::JSONReport.new(Coverband.configuration.store).report + end + def settings Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings! end diff --git a/test/coverband/reporters/json_test.rb b/test/coverband/reporters/json_test.rb new file mode 100644 index 00000000..7baa123b --- /dev/null +++ b/test/coverband/reporters/json_test.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require File.expand_path("../../test_helper", File.dirname(__FILE__)) + +class ReportJSONTest < Minitest::Test + def setup + super + @store = Coverband.configuration.store + Coverband.configure do |config| + config.store = @store + config.root = fixtures_root + config.ignore = ["notsomething.rb", "lib/*"] + end + mock_file_hash + end + + test "includes totals" do + @store.send(:save_report, basic_coverage) + + json = Coverband::Reporters::JSONReport.new(@store).report + parsed = JSON.parse(json) + expected_keys = ["total_files", "lines_of_code", "lines_covered", "lines_missed", "covered_strength", "covered_percent"] + assert expected_keys - parsed.keys == [] + end + + test "honors ignore list" do + @store.send(:save_report, basic_coverage) + + json = Coverband::Reporters::JSONReport.new(@store).report + parsed = JSON.parse(json) + expected_files = ["app/controllers/sample_controller.rb", "app/models/user.rb"] + assert_equal parsed["files"].keys, expected_files + end + + test "includes metrics for files" do + @store.send(:save_report, basic_coverage) + + json = Coverband::Reporters::JSONReport.new(@store).report + parsed = JSON.parse(json) + + expected_keys = ["never_loaded", "runtime_percentage", "lines_of_code", "lines_covered", "lines_missed", "covered_percent", "covered_strength"] + + assert_equal parsed["files"].length, 2 + parsed["files"].keys.each do |file| + assert_equal parsed["files"][file].keys, expected_keys + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index dd9e1841..3638119b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -33,6 +33,7 @@ require "coverband/utils/lines_classifier" require "coverband/utils/results" require "coverband/reporters/html_report" +require "coverband/reporters/json_report" require "webmock/minitest" require_relative "unique_files" @@ -93,13 +94,13 @@ def setup def test(name, &block) test_name = "test_#{name.gsub(/\s+/, "_")}".to_sym defined = begin - instance_method(test_name) + instance_method(test_name) rescue false - end + end raise "#{test_name} is already defined in #{self}" if defined - if block_given? + if block define_method(test_name, &block) else define_method(test_name) do