Skip to content

Commit 43a5dc5

Browse files
committed
Emit root-relative paths in JSON coverage output
Per-file coverage keys, group file lists, and minimum_coverage_by_file error keys now use `project_filename` (root-relative) instead of `filename` (absolute). Consumers already have `meta.root` if they need to reconstruct an absolute path. The frontend's `shortenFilename` simplifies to stripping the leading `/` — no more root parameter threaded through the render pipeline.
1 parent 827afd3 commit 43a5dc5

9 files changed

Lines changed: 37 additions & 36 deletions

File tree

features/step_definitions/json_steps.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
cd(".") do
55
json_report = JSON.parse(File.read("coverage/coverage.json"))
66
coverage_hash = json_report.fetch "coverage"
7-
directory = Dir.pwd
87

9-
faked_project = coverage_hash.fetch("#{directory}/lib/faked_project.rb")
8+
faked_project = coverage_hash.fetch("/lib/faked_project.rb")
109
expect(faked_project["lines"]).to eq [nil, nil, 1, 1, 1, nil, nil, nil, 5, 3, nil, nil, 1]
1110
expect(faked_project["lines_covered_percent"]).to be_a(Float)
1211

13-
some_class = coverage_hash.fetch("#{directory}/lib/faked_project/some_class.rb")
12+
some_class = coverage_hash.fetch("/lib/faked_project/some_class.rb")
1413
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]
1514
expect(some_class["lines_covered_percent"]).to be_a(Float)
1615
end

html_frontend/src/app.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,9 @@ function fmtPct(pct: number): string {
272272
return (Math.floor(pct * 100) / 100).toFixed(2);
273273
}
274274

275-
function shortenFilename(filename: string, root: string): string {
276-
return filename.replace(root, '.').replace(/^\.\//, '');
275+
function shortenFilename(filename: string): string {
276+
// Paths in the JSON output are already root-relative with a leading `/`.
277+
return filename.replace(/^\//, '');
277278
}
278279

279280
function fileId(filename: string): string {
@@ -413,9 +414,9 @@ function buildMissedMethodLines(methods: MethodEntry[] | undefined): Set<number>
413414
return set;
414415
}
415416

416-
function renderSourceFile(filename: string, data: FileCoverage, root: string, branchCoverage: boolean, methodCoverage: boolean): string {
417+
function renderSourceFile(filename: string, data: FileCoverage, branchCoverage: boolean, methodCoverage: boolean): string {
417418
const id = fileId(filename);
418-
const shortName = shortenFilename(filename, root);
419+
const shortName = shortenFilename(filename);
419420
const coveredLines = data.covered_lines;
420421
const missedLines = data.missed_lines;
421422
const totalLines = coveredLines + missedLines;
@@ -481,7 +482,6 @@ function renderFileList(
481482
title: string,
482483
filenames: string[],
483484
allCoverage: Record<string, FileCoverage>,
484-
root: string,
485485
branchCoverage: boolean,
486486
methodCoverage: boolean
487487
): string {
@@ -534,7 +534,7 @@ function renderFileList(
534534
for (const fn of filenames) {
535535
const f = allCoverage[fn];
536536
if (!f) continue;
537-
const shortName = shortenFilename(fn, root);
537+
const shortName = shortenFilename(fn);
538538
const id = fileId(fn);
539539
const coveredLines = f.covered_lines;
540540
const relevantLines = coveredLines + f.missed_lines;
@@ -569,7 +569,6 @@ function renderPage(data: CoverageData): void {
569569
const meta = data.meta;
570570
const branchCoverage = meta.branch_coverage;
571571
const methodCoverage = meta.method_coverage;
572-
const root = meta.root;
573572

574573
// Page title and favicon
575574
document.title = `Code coverage for ${meta.project_name}`;
@@ -585,11 +584,11 @@ function renderPage(data: CoverageData): void {
585584

586585
// Content: file lists
587586
const content = document.getElementById('content')!;
588-
content.innerHTML = renderFileList('All Files', allFiles, data.coverage, root, branchCoverage, methodCoverage);
587+
content.innerHTML = renderFileList('All Files', allFiles, data.coverage, branchCoverage, methodCoverage);
589588

590589
for (const groupName of Object.keys(data.groups)) {
591590
const groupFiles = data.groups[groupName].files || [];
592-
content.innerHTML += renderFileList(groupName, groupFiles, data.coverage, root, branchCoverage, methodCoverage);
591+
content.innerHTML += renderFileList(groupName, groupFiles, data.coverage, branchCoverage, methodCoverage);
593592
}
594593

595594
// Build id → filename lookup map for O(1) source file materialization
@@ -599,7 +598,6 @@ function renderPage(data: CoverageData): void {
599598
}
600599
(window as any)._simplecovIdMap = idToFilename;
601600
(window as any)._simplecovFiles = data.coverage;
602-
(window as any)._simplecovRoot = root;
603601
(window as any)._simplecovBranchCoverage = branchCoverage;
604602
(window as any)._simplecovMethodCoverage = methodCoverage;
605603

@@ -849,14 +847,13 @@ function materializeSourceFile(sourceFileId: string): HTMLElement | null {
849847

850848
const idMap = (window as any)._simplecovIdMap as Record<string, string>;
851849
const coverage = (window as any)._simplecovFiles as Record<string, FileCoverage>;
852-
const root = (window as any)._simplecovRoot as string;
853850
const branchCov = (window as any)._simplecovBranchCoverage as boolean;
854851
const methodCov = (window as any)._simplecovMethodCoverage as boolean;
855852

856853
const targetFilename = idMap[sourceFileId];
857854
if (!targetFilename) return null;
858855

859-
const html = renderSourceFile(targetFilename, coverage[targetFilename], root, branchCov, methodCov);
856+
const html = renderSourceFile(targetFilename, coverage[targetFilename], branchCov, methodCov);
860857
const container = document.querySelector('.source_files')!;
861858
const wrapper = document.createElement('div');
862859
wrapper.innerHTML = html;

lib/simplecov/formatter/html_formatter/public/application.js

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/simplecov/formatter/json_formatter/result_hash_formatter.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ def format_total
2727

2828
def format_files
2929
@result.files.each do |source_file|
30-
formatted_result[:coverage][source_file.filename] =
30+
formatted_result[:coverage][source_file.project_filename] =
3131
format_source_file(source_file)
3232
end
3333
end
3434

3535
def format_groups
3636
@result.groups.each do |name, file_list|
3737
group_data = format_coverage_statistics(file_list.coverage_statistics)
38-
group_data[:files] = file_list.map(&:filename)
38+
group_data[:files] = file_list.map(&:project_filename)
3939
formatted_result[:groups][name] = group_data
4040
end
4141
end
@@ -63,7 +63,7 @@ def format_minimum_coverage_by_file_errors
6363
key = CRITERION_KEYS.fetch(violation.fetch(:criterion))
6464
bucket = formatted_result[:errors][:minimum_coverage_by_file] ||= {}
6565
criterion_errors = bucket[key] ||= {}
66-
criterion_errors[violation.fetch(:filename)] = {expected: violation.fetch(:expected), actual: violation.fetch(:actual)}
66+
criterion_errors[violation.fetch(:project_filename)] = {expected: violation.fetch(:expected), actual: violation.fetch(:actual)}
6767
end
6868
end
6969

spec/fixtures/json/sample.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
}
2020
},
2121
"coverage": {
22-
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
22+
"/spec/fixtures/json/sample.rb": {
2323
"lines": [
2424
null,
2525
1,

spec/fixtures/json/sample_groups.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
}
2020
},
2121
"coverage": {
22-
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
22+
"/spec/fixtures/json/sample.rb": {
2323
"lines": [
2424
null,
2525
1,
@@ -90,7 +90,7 @@
9090
"strength": 0.0
9191
},
9292
"files": [
93-
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb"
93+
"/spec/fixtures/json/sample.rb"
9494
]
9595
}
9696
},

spec/fixtures/json/sample_with_branch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727
},
2828
"coverage": {
29-
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
29+
"/spec/fixtures/json/sample.rb": {
3030
"lines": [
3131
null,
3232
1,

spec/fixtures/json/sample_with_method.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727
},
2828
"coverage": {
29-
"/STUB_WORKING_DIRECTORY/spec/fixtures/json/sample.rb": {
29+
"/spec/fixtures/json/sample.rb": {
3030
"lines": [
3131
null,
3232
1,

spec/json_formatter_spec.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"percent" => 66.66666666666667,
3535
"strength" => 0.6666666666666666
3636
)
37-
expect(json_output.fetch("coverage").fetch(source_fixture("json/sample.rb"))).to include(
37+
expect(json_output.fetch("coverage").fetch(project_fixture_filename("json/sample.rb"))).to include(
3838
"lines_covered_percent" => 66.66666666666667
3939
)
4040
end
@@ -150,7 +150,7 @@
150150
errors = json_output.fetch("errors")
151151
expect(errors).to eq(
152152
"minimum_coverage_by_file" => {
153-
"lines" => {source_fixture("json/sample.rb") => {"expected" => 95, "actual" => 90.0}}
153+
"lines" => {project_fixture_filename("json/sample.rb") => {"expected" => 95, "actual" => 90.0}}
154154
}
155155
)
156156
end
@@ -182,7 +182,7 @@
182182
errors = json_output.fetch("errors")
183183
expect(errors).to eq(
184184
"minimum_coverage_by_file" => {
185-
"branches" => {source_fixture("json/sample.rb") => {"expected" => 75, "actual" => 50.0}}
185+
"branches" => {project_fixture_filename("json/sample.rb") => {"expected" => 75, "actual" => 50.0}}
186186
}
187187
)
188188
end
@@ -289,10 +289,11 @@
289289
res.created_at = fixed_time
290290

291291
# right now SimpleCov works mostly on global state, hence setting the groups that way
292-
# would be global state --> Mocking is better here
292+
# would be global state --> Mocking is better here. `map` ignores the block
293+
# and returns the stubbed value — so stub it to the project-relative path directly.
293294
mock_file_list = double("File List",
294295
coverage_statistics: {line: line_stats},
295-
map: [sample_filename])
296+
map: [project_fixture_filename("json/sample.rb")])
296297
allow(res).to receive_messages(
297298
groups: {"My Group" => mock_file_list}
298299
)
@@ -324,6 +325,10 @@ def json_result(filename)
324325
JSON.parse(file)
325326
end
326327

328+
def project_fixture_filename(path)
329+
source_fixture(path).delete_prefix(SimpleCov.root)
330+
end
331+
327332
STUB_WORKING_DIRECTORY = "STUB_WORKING_DIRECTORY"
328333
STUB_COMMAND_NAME = "STUB_COMMAND_NAME"
329334
STUB_PROJECT_NAME = "STUB_PROJECT_NAME"

0 commit comments

Comments
 (0)