Skip to content

Commit 30e31e9

Browse files
sferikshetty-tejas
andcommitted
Add richer metrics to JSON formatter
Enhance the JSON coverage output with per-file coverage percentages, a total section with aggregate statistics, full group stats, and method coverage support. Per-file output now includes covered_percent (always), and when enabled: branches_covered_percent and methods array with methods_covered_percent. The total and groups sections report full statistics (covered, missed, total, percent, strength) for each enabled coverage type (line, branch, method). Breaking change: group stats change from { covered_percent: 80.0 } to the full stats shape with the key renamed to percent. Based on the ideas in codeclimate-community/simplecov_json_formatter#12. Co-Authored-By: Tejas <tejas.shetty@mailbox.org>
1 parent ad90bc9 commit 30e31e9

9 files changed

Lines changed: 255 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
Unreleased
22
==========
33

4+
## Breaking Changes
5+
* JSON formatter: group stats changed from `{ "covered_percent": 80.0 }` to full stats shape `{ "covered": 8, "missed": 2, "total": 10, "percent": 80.0, "strength": 0.0 }`. The key `covered_percent` is renamed to `percent`.
6+
* JSON formatter: `simplecov_json_formatter` gem is now built in. `require "simplecov_json_formatter"` continues to work via a shim.
7+
* `StringFilter` now matches at path-segment boundaries. `"lib"` matches `/lib/` but no longer matches `/library/`. Use a `Regexp` filter for substring matching.
8+
* Removed `docile` gem dependency. The `SimpleCov.configure` DSL block is now evaluated via `instance_exec` with instance variable proxying.
9+
10+
## Enhancements
11+
* JSON formatter: added `total` section with aggregate coverage statistics (covered, missed, total, percent, strength) for line, branch, and method coverage
12+
* JSON formatter: per-file output now includes `covered_percent`, and when enabled: `branches_covered_percent`, `methods` array, and `methods_covered_percent`
13+
* JSON formatter: group stats now include full statistics for all enabled coverage types, not just line coverage percent
14+
* JSON formatter: added `silent:` keyword to `JSONFormatter.new` to suppress console output
15+
* Merged `simplecov-html` formatter into the main gem. A backward-compatibility shim ensures `require "simplecov-html"` still works.
16+
* Merged `simplecov_json_formatter` into the main gem. A backward-compatibility shim ensures `require "simplecov_json_formatter"` still works.
17+
* Added `rake assets:compile` task for building the HTML formatter's frontend assets via esbuild
18+
* Added TypeScript type checking CI workflow
19+
* Separated rubocop into its own `lint.yml` CI workflow
20+
* `CommandGuesser` now appends the framework name to parallel test data (e.g. `"RSpec (1/2)"` instead of `"(1/2)"`)
21+
422
## Bugfixes
523
* Don't report misleading 100% branch/method coverage for files added via `track_files` that were never loaded. See #902
24+
* Fix HTML formatter tab bar layout: dark mode toggle no longer wraps onto two lines, and tabs connect seamlessly with the content panel
25+
* Fix branch coverage cucumber feature to match the HTML formatter's updated output format
626

727
0.22.1 (2024-09-02)
828
==========

features/step_definitions/json_steps.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
coverage_hash = json_report.fetch "coverage"
77
directory = Dir.pwd
88

9-
expect(coverage_hash.fetch("#{directory}/lib/faked_project.rb")).to eq "lines" => [nil, nil, 1, 1, 1, nil, nil, nil, 5, 3, nil, nil, 1]
10-
expect(coverage_hash.fetch("#{directory}/lib/faked_project/some_class.rb")).to eq "lines" => [nil, nil, 1, 1, 1, nil, 1, 2, nil, nil, 1, 1, nil, nil, 1, 1, 1, nil, 0, nil, nil, 0, nil, nil, 1, nil, 1, 0, nil, nil]
9+
faked_project = coverage_hash.fetch("#{directory}/lib/faked_project.rb")
10+
expect(faked_project["lines"]).to eq [nil, nil, 1, 1, 1, nil, nil, nil, 5, 3, nil, nil, 1]
11+
expect(faked_project["covered_percent"]).to be_a(Float)
12+
13+
some_class = coverage_hash.fetch("#{directory}/lib/faked_project/some_class.rb")
14+
expect(some_class["lines"]).to eq [nil, nil, 1, 1, 1, nil, 1, 2, nil, nil, 1, 1, nil, nil, 1, 1, 1, nil, 0, nil, nil, 0, nil, nil, 1, nil, 1, 0, nil, nil]
15+
expect(some_class["covered_percent"]).to be_a(Float)
1116
end
1217
end

lib/simplecov/formatter/json_formatter/result_hash_formatter.rb

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def initialize(result)
1111
end
1212

1313
def format
14+
format_total
1415
format_files
1516
format_groups
1617

@@ -19,6 +20,10 @@ def format
1920

2021
private
2122

23+
def format_total
24+
formatted_result[:total] = format_coverage_statistics(@result.coverage_statistics)
25+
end
26+
2227
def format_files
2328
@result.files.each do |source_file|
2429
formatted_result[:coverage][source_file.filename] =
@@ -28,11 +33,7 @@ def format_files
2833

2934
def format_groups
3035
@result.groups.each do |name, file_list|
31-
formatted_result[:groups][name] = {
32-
lines: {
33-
covered_percent: file_list.covered_percent
34-
}
35-
}
36+
formatted_result[:groups][name] = format_coverage_statistics(file_list.coverage_statistics)
3637
end
3738
end
3839

@@ -41,6 +42,7 @@ def formatted_result
4142
meta: {
4243
simplecov_version: SimpleCov::VERSION
4344
},
45+
total: {},
4446
coverage: {},
4547
groups: {}
4648
}
@@ -50,6 +52,23 @@ def format_source_file(source_file)
5052
source_file_formatter = SourceFileFormatter.new(source_file)
5153
source_file_formatter.format
5254
end
55+
56+
def format_coverage_statistics(statistics)
57+
result = {lines: format_single_statistic(statistics[:line])}
58+
result[:branches] = format_single_statistic(statistics[:branch]) if SimpleCov.branch_coverage? && statistics[:branch]
59+
result[:methods] = format_single_statistic(statistics[:method]) if SimpleCov.method_coverage? && statistics[:method]
60+
result
61+
end
62+
63+
def format_single_statistic(stat)
64+
{
65+
covered: stat.covered,
66+
missed: stat.missed,
67+
total: stat.total,
68+
percent: stat.percent.round(2),
69+
strength: stat.strength.round(2)
70+
}
71+
end
5372
end
5473
end
5574
end

lib/simplecov/formatter/json_formatter/source_file_formatter.rb

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,32 @@ def initialize(source_file)
1010
end
1111

1212
def format
13-
if SimpleCov.branch_coverage?
14-
line_coverage.merge(branch_coverage)
15-
else
16-
line_coverage
17-
end
13+
result = line_coverage
14+
result.merge!(branch_coverage) if SimpleCov.branch_coverage?
15+
result.merge!(method_coverage) if SimpleCov.method_coverage?
16+
result
1817
end
1918

2019
private
2120

2221
def line_coverage
2322
@line_coverage ||= {
24-
lines: lines
23+
lines: lines,
24+
covered_percent: @source_file.covered_percent.round(2)
2525
}
2626
end
2727

2828
def branch_coverage
2929
{
30-
branches: branches
30+
branches: branches,
31+
branches_covered_percent: @source_file.branches_coverage_percent.round(2)
32+
}
33+
end
34+
35+
def method_coverage
36+
{
37+
methods: format_methods,
38+
methods_covered_percent: @source_file.methods_coverage_percent.round(2)
3139
}
3240
end
3341

@@ -43,6 +51,12 @@ def branches
4351
end
4452
end
4553

54+
def format_methods
55+
@source_file.methods.collect do |method|
56+
parse_method(method)
57+
end
58+
end
59+
4660
def parse_line(line)
4761
return line.coverage unless line.skipped?
4862

@@ -57,6 +71,15 @@ def parse_branch(branch)
5771
coverage: parse_line(branch)
5872
}
5973
end
74+
75+
def parse_method(method)
76+
{
77+
name: method.to_s,
78+
start_line: method.start_line,
79+
end_line: method.end_line,
80+
coverage: parse_line(method)
81+
}
82+
end
6083
end
6184
end
6285
end

spec/fixtures/json/sample.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
"meta": {
33
"simplecov_version": "0.22.0"
44
},
5+
"total": {
6+
"lines": {
7+
"covered": 9,
8+
"missed": 1,
9+
"total": 10,
10+
"percent": 90.0,
11+
"strength": 1.0
12+
}
13+
},
514
"coverage": {
615
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
716
"lines": [
@@ -30,7 +39,8 @@
3039
"ignored",
3140
"ignored",
3241
null
33-
]
42+
],
43+
"covered_percent": 90.0
3444
}
3545
},
3646
"groups": {}

spec/fixtures/json/sample_groups.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
"meta": {
33
"simplecov_version": "0.22.0"
44
},
5+
"total": {
6+
"lines": {
7+
"covered": 9,
8+
"missed": 1,
9+
"total": 10,
10+
"percent": 90.0,
11+
"strength": 1.0
12+
}
13+
},
514
"coverage": {
615
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
716
"lines": [
@@ -30,13 +39,18 @@
3039
"ignored",
3140
"ignored",
3241
null
33-
]
42+
],
43+
"covered_percent": 90.0
3444
}
3545
},
3646
"groups": {
3747
"My Group": {
3848
"lines": {
39-
"covered_percent": 80.0
49+
"covered": 8,
50+
"missed": 2,
51+
"total": 10,
52+
"percent": 80.0,
53+
"strength": 0.0
4054
}
4155
}
4256
}

spec/fixtures/json/sample_with_branch.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
"meta": {
33
"simplecov_version": "0.22.0"
44
},
5+
"total": {
6+
"lines": {
7+
"covered": 9,
8+
"missed": 1,
9+
"total": 10,
10+
"percent": 90.0,
11+
"strength": 1.0
12+
},
13+
"branches": {
14+
"covered": 1,
15+
"missed": 1,
16+
"total": 2,
17+
"percent": 50.0,
18+
"strength": 0.0
19+
}
20+
},
521
"coverage": {
622
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
723
"lines": [
@@ -31,6 +47,7 @@
3147
"ignored",
3248
null
3349
],
50+
"covered_percent": 90.0,
3451
"branches": [
3552
{
3653
"type": "then",
@@ -44,7 +61,8 @@
4461
"end_line": 16,
4562
"coverage": 1
4663
}
47-
]
64+
],
65+
"branches_covered_percent": 50.0
4866
}
4967
},
5068
"groups": {}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"meta": {
3+
"simplecov_version": "0.22.0"
4+
},
5+
"total": {
6+
"lines": {
7+
"covered": 9,
8+
"missed": 1,
9+
"total": 10,
10+
"percent": 90.0,
11+
"strength": 1.0
12+
},
13+
"methods": {
14+
"covered": 3,
15+
"missed": 0,
16+
"total": 3,
17+
"percent": 100.0,
18+
"strength": 0.0
19+
}
20+
},
21+
"coverage": {
22+
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
23+
"lines": [
24+
null,
25+
1,
26+
1,
27+
1,
28+
1,
29+
null,
30+
null,
31+
1,
32+
1,
33+
null,
34+
null,
35+
1,
36+
1,
37+
0,
38+
null,
39+
1,
40+
null,
41+
null,
42+
null,
43+
"ignored",
44+
"ignored",
45+
"ignored",
46+
"ignored",
47+
"ignored",
48+
null
49+
],
50+
"covered_percent": 90.0,
51+
"methods": [
52+
{
53+
"name": "Foo#initialize",
54+
"start_line": 3,
55+
"end_line": 6,
56+
"coverage": 1
57+
},
58+
{
59+
"name": "Foo#bar",
60+
"start_line": 8,
61+
"end_line": 10,
62+
"coverage": 1
63+
},
64+
{
65+
"name": "Foo#foo",
66+
"start_line": 12,
67+
"end_line": 18,
68+
"coverage": 1
69+
},
70+
{
71+
"name": "Foo#skipped",
72+
"start_line": 21,
73+
"end_line": 23,
74+
"coverage": "ignored"
75+
}
76+
],
77+
"methods_covered_percent": 100.0
78+
}
79+
},
80+
"groups": {}
81+
}

0 commit comments

Comments
 (0)