Skip to content

Commit 559abd1

Browse files
committed
Shorten table column headings for narrower coverage columns
1 parent 28d43a4 commit 559abd1

File tree

10 files changed

+245
-248
lines changed

10 files changed

+245
-248
lines changed

assets/stylesheets/screen.css

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ table.file_list th.cell--coverage .th-with-filter {
410410
/* --- File list table --------------------------------------- */
411411

412412
.file_list--responsive {
413+
overflow-x: auto;
413414
}
414415

415416
table.file_list {
@@ -502,30 +503,28 @@ a.src_link {
502503

503504
/* --- Coverage columns -------------------------------------- */
504505

505-
table.file_list td.cell--bar {
506-
padding-right: 4px;
507-
padding-left: 0;
506+
table.file_list td.cell--coverage {
507+
white-space: nowrap;
508508
}
509509

510-
table.file_list td.cell--bar .coverage-bar {
511-
margin-left: auto;
510+
.coverage-cell {
511+
display: flex;
512+
flex-wrap: nowrap;
513+
align-items: center;
514+
justify-content: flex-end;
515+
gap: 10px;
512516
}
513517

514-
table.file_list td.cell--pct {
515-
text-align: right;
518+
.coverage-cell .coverage-pct {
519+
flex: 0 0 4.5em;
516520
font-variant-numeric: tabular-nums;
517-
white-space: nowrap;
518-
width: 1%;
519-
padding-left: 4px;
520-
}
521-
522-
table.file_list .totals-row td.cell--bar {
523-
padding-right: 4px;
524-
padding-left: 0;
525521
}
526522

527-
table.file_list .totals-row td.cell--pct {
528-
padding-left: 4px;
523+
.coverage-cell .bar-sizer {
524+
flex: 0 0 auto;
525+
width: 240px;
526+
min-width: 160px;
527+
max-width: 240px;
529528
}
530529

531530
table.file_list td.cell--numerator {
@@ -567,7 +566,6 @@ table.file_list .totals-row td.cell--denominator {
567566
background: var(--bar-bg);
568567
border-radius: 6px;
569568
overflow: hidden;
570-
min-width: 40px;
571569
}
572570

573571
.coverage-bar__fill {
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# frozen_string_literal: true
2+
3+
module SimpleCov
4+
module Formatter
5+
class HTMLFormatter
6+
# Helpers for rendering coverage bars, cells, and summaries in ERB templates.
7+
module CoverageHelpers
8+
def coverage_bar(pct)
9+
css = coverage_css_class(pct)
10+
width = Kernel.format("%.1f", pct.floor(1))
11+
fill = %(<div class="coverage-bar__fill coverage-bar__fill--#{css}" style="width: #{width}%"></div>)
12+
%(<div class="bar-sizer"><div class="coverage-bar">#{fill}</div></div>)
13+
end
14+
15+
def coverage_cells(pct, covered, total, type:, totals: false)
16+
cov_cls, num_cls, den_cls, order = coverage_cell_attrs(pct, type, totals)
17+
pct_str = Kernel.format("%.2f", pct.floor(2))
18+
bar_and_pct = %(<div class="coverage-cell">#{coverage_bar(pct)}<span class="coverage-pct">#{pct_str}%</span></div>)
19+
%(<td class="#{cov_cls}"#{order}>#{bar_and_pct}</td>) +
20+
%(<td class="#{num_cls}">#{fmt(covered)}/</td>) +
21+
%(<td class="#{den_cls}">#{fmt(total)}</td>)
22+
end
23+
24+
def coverage_header_cells(label, type, covered_label, total_label)
25+
<<~HTML
26+
<th class="cell--coverage">
27+
<div class="th-with-filter">
28+
<span class="th-label">#{label}</span>
29+
<div class="col-filter__coverage">
30+
<select class="col-filter__op" data-type="#{type}"><option value="lt">&lt;</option><option value="lte" selected>&le;</option><option value="eq">=</option><option value="gte">&ge;</option><option value="gt">&gt;</option></select>
31+
<span class="col-filter__pct-wrap"><input type="number" class="col-filter__value" min="0" max="100" data-type="#{type}" value="100" step="any"></span>
32+
</div>
33+
</div>
34+
</th>
35+
<th class="cell--numerator">#{covered_label}</th>
36+
<th class="cell--denominator">#{total_label}</th>
37+
HTML
38+
end
39+
40+
def file_data_attrs(source_file)
41+
build_data_attr_pairs(source_file).map { |k, v| %(data-#{k}="#{v}") }.join(" ")
42+
end
43+
44+
def coverage_type_summary(type, label, summary, enabled:, **opts)
45+
return disabled_summary(type, label) unless enabled
46+
47+
enabled_type_summary(type, label, summary.fetch(type.to_sym), opts)
48+
end
49+
50+
def coverage_summary(stats, show_method_toggle: false)
51+
_summary = {
52+
line: build_stats(stats.fetch(:covered_lines), stats.fetch(:total_lines)),
53+
branch: build_stats(stats.fetch(:covered_branches, 0), stats.fetch(:total_branches, 0)),
54+
method: build_stats(stats.fetch(:covered_methods, 0), stats.fetch(:total_methods, 0)),
55+
show_method_toggle: show_method_toggle,
56+
}
57+
template("coverage_summary").result(binding)
58+
end
59+
60+
def build_stats(covered, total)
61+
pct = total.positive? ? (covered * 100.0 / total) : 100.0
62+
{covered: covered, total: total, missed: total - covered, pct: pct}
63+
end
64+
65+
private
66+
67+
def totals_cell_attrs(type, css)
68+
["cell--coverage strong t-totals__#{type}-pct #{css}",
69+
"cell--numerator strong t-totals__#{type}-num",
70+
"cell--denominator strong t-totals__#{type}-den", ""]
71+
end
72+
73+
def regular_cell_attrs(pct, type, css)
74+
["cell--coverage cell--#{type}-pct #{css}",
75+
"cell--numerator", "cell--denominator",
76+
%( data-order="#{Kernel.format('%.2f', pct)}")]
77+
end
78+
79+
def coverage_cell_attrs(pct, type, totals)
80+
css = coverage_css_class(pct)
81+
totals ? totals_cell_attrs(type, css) : regular_cell_attrs(pct, type, css)
82+
end
83+
84+
def build_data_attr_pairs(source_file)
85+
covered = source_file.covered_lines.count
86+
pairs = {"covered-lines" => covered, "relevant-lines" => covered + source_file.missed_lines.count}
87+
append_branch_attrs(pairs, source_file)
88+
append_method_attrs(pairs, source_file)
89+
pairs
90+
end
91+
92+
def append_branch_attrs(pairs, source_file)
93+
return unless branch_coverage?
94+
95+
pairs["covered-branches"] = source_file.covered_branches.count
96+
pairs["total-branches"] = source_file.total_branches.count
97+
end
98+
99+
def append_method_attrs(pairs, source_file)
100+
return unless method_coverage?
101+
102+
pairs["covered-methods"] = source_file.covered_methods.count
103+
pairs["total-methods"] = source_file.methods.count
104+
end
105+
106+
def enabled_type_summary(type, label, stats, opts)
107+
css = coverage_css_class(stats.fetch(:pct))
108+
missed = stats.fetch(:missed)
109+
parts = [
110+
%(<div class="t-#{type}-summary">\n #{label}: ),
111+
%(<span class="#{css}"><b>#{Kernel.format('%.2f', stats.fetch(:pct).floor(2))}%</b></span>),
112+
%(<span class="coverage-cell__fraction"> #{stats.fetch(:covered)}/#{stats.fetch(:total)} #{opts.fetch(:suffix, 'covered')}</span>),
113+
]
114+
parts << missed_summary_html(missed, opts.fetch(:missed_class, "red"), opts.fetch(:toggle, false)) if missed.positive?
115+
parts << "\n </div>"
116+
parts.join
117+
end
118+
119+
def disabled_summary(type, label)
120+
%(<div class="t-#{type}-summary">\n #{label}: <span class="coverage-disabled">disabled</span>\n </div>)
121+
end
122+
123+
def missed_summary_html(count, missed_class, toggle)
124+
missed = if toggle
125+
%(<a href="#" class="t-missed-method-toggle"><b>#{count}</b> missed</a>)
126+
else
127+
%(<span class="#{missed_class}"><b>#{count}</b> missed</span>)
128+
end
129+
%(<span class="coverage-cell__fraction">,</span>\n #{missed})
130+
end
131+
end
132+
end
133+
end
134+
end

lib/simplecov-html/view_helpers.rb

Lines changed: 4 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
require "digest/md5"
44
require "set"
5+
require_relative "coverage_helpers"
56

67
module SimpleCov
78
module Formatter
89
class HTMLFormatter
910
# Helper methods used by ERB templates for rendering coverage data.
10-
module ViewHelpers # rubocop:disable Metrics/ModuleLength
11+
module ViewHelpers
12+
include CoverageHelpers
13+
1114
def line_status?(source_file, line)
1215
if branch_coverage? && source_file.line_with_missed_branch?(line.number)
1316
"missed-branch"
14-
# :nocov:
1517
elsif method_coverage? && missed_method_lines(source_file).include?(line.number)
1618
"missed-method"
17-
# :nocov:
1819
else
1920
line.status
2021
end
2122
end
2223

23-
# :nocov:
2424
def missed_method_lines(source_file)
2525
@missed_method_lines ||= {}
2626
@missed_method_lines[source_file.filename] ||= missed_method_line_set(source_file)
@@ -32,7 +32,6 @@ def missed_method_line_set(source_file)
3232
.flat_map { |m| (m.start_line..m.end_line).to_a }
3333
.to_set
3434
end
35-
# :nocov:
3635

3736
def coverage_css_class(covered_percent)
3837
if covered_percent >= 90
@@ -72,113 +71,6 @@ def to_id(value)
7271
def fmt(number)
7372
number.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')
7473
end
75-
76-
def coverage_bar(pct)
77-
css = coverage_css_class(pct)
78-
width = Kernel.format("%.1f", pct.floor(1))
79-
%(<div class="coverage-bar"><div class="coverage-bar__fill coverage-bar__fill--#{css}" style="width: #{width}%"></div></div>)
80-
end
81-
82-
def coverage_cells(pct, covered, total, type:, totals: false) # rubocop:disable Metrics/MethodLength
83-
css = coverage_css_class(pct)
84-
if totals
85-
bar_cls = "cell--bar t-totals__#{type}-bar"
86-
pct_cls = "cell--pct strong t-totals__#{type}-pct #{css}"
87-
num_cls = "cell--numerator strong t-totals__#{type}-num"
88-
den_cls = "cell--denominator strong t-totals__#{type}-den"
89-
else
90-
bar_cls = "cell--bar"
91-
pct_cls = "cell--pct cell--#{type}-pct #{css}"
92-
num_cls = "cell--numerator"
93-
den_cls = "cell--denominator"
94-
end
95-
pct_str = Kernel.format("%.2f", pct.floor(2))
96-
pct_td = if totals
97-
%(<td class="#{pct_cls}">#{pct_str}%</td>)
98-
else
99-
%(<td class="#{pct_cls}" data-order="#{Kernel.format('%.2f', pct)}">#{pct_str}%</td>)
100-
end
101-
%(<td class="#{bar_cls}">#{coverage_bar(pct)}</td>) +
102-
pct_td +
103-
%(<td class="#{num_cls}">#{fmt(covered)}/</td>) +
104-
%(<td class="#{den_cls}">#{fmt(total)}</td>)
105-
end
106-
107-
def coverage_header_cells(label, type, covered_label, total_label)
108-
<<~HTML
109-
<th class="cell--coverage" colspan="2">
110-
<div class="th-with-filter">
111-
<span class="th-label">#{label}</span>
112-
<div class="col-filter__coverage">
113-
<select class="col-filter__op" data-type="#{type}"><option value="lt">&lt;</option><option value="lte" selected>&le;</option><option value="eq">=</option><option value="gte">&ge;</option><option value="gt">&gt;</option></select>
114-
<span class="col-filter__pct-wrap"><input type="number" class="col-filter__value" min="0" max="100" data-type="#{type}" value="100" step="any"></span>
115-
</div>
116-
</div>
117-
</th>
118-
<th class="cell--numerator">#{covered_label}</th>
119-
<th class="cell--denominator">#{total_label}</th>
120-
HTML
121-
end
122-
123-
def file_data_attrs(source_file) # rubocop:disable Metrics/AbcSize
124-
covered = source_file.covered_lines.count
125-
relevant = covered + source_file.missed_lines.count
126-
pairs = {"covered-lines" => covered, "relevant-lines" => relevant}
127-
pairs["covered-branches"] = source_file.covered_branches.count if branch_coverage?
128-
pairs["total-branches"] = source_file.total_branches.count if branch_coverage?
129-
pairs["covered-methods"] = source_file.covered_methods.count if method_coverage?
130-
pairs["total-methods"] = source_file.methods.count if method_coverage?
131-
pairs.map { |k, v| %(data-#{k}="#{v}") }.join(" ")
132-
end
133-
134-
def coverage_type_summary(type, label, summary, enabled:, **opts) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
135-
return disabled_summary(type, label) unless enabled
136-
137-
s = summary.fetch(type.to_sym)
138-
pct = s.fetch(:pct)
139-
covered = s.fetch(:covered)
140-
total = s.fetch(:total)
141-
missed = s.fetch(:missed)
142-
css = coverage_css_class(pct)
143-
parts = [
144-
%(<div class="t-#{type}-summary">\n #{label}: ),
145-
%(<span class="#{css}"><b>#{Kernel.format('%.2f', pct.floor(2))}%</b></span>),
146-
%(<span class="coverage-cell__fraction"> #{covered}/#{total} #{opts.fetch(:suffix, 'covered')}</span>),
147-
]
148-
parts << missed_summary_html(missed, opts.fetch(:missed_class, "red"), opts.fetch(:toggle, false)) if missed.positive?
149-
parts << "\n </div>"
150-
parts.join
151-
end
152-
153-
def coverage_summary(stats, show_method_toggle: false)
154-
_summary = {
155-
line: build_stats(stats.fetch(:covered_lines), stats.fetch(:total_lines)),
156-
branch: build_stats(stats.fetch(:covered_branches, 0), stats.fetch(:total_branches, 0)),
157-
method: build_stats(stats.fetch(:covered_methods, 0), stats.fetch(:total_methods, 0)),
158-
show_method_toggle: show_method_toggle,
159-
}
160-
template("coverage_summary").result(binding)
161-
end
162-
163-
def build_stats(covered, total)
164-
pct = total.positive? ? (covered * 100.0 / total) : 100.0
165-
{covered: covered, total: total, missed: total - covered, pct: pct}
166-
end
167-
168-
private
169-
170-
def disabled_summary(type, label)
171-
%(<div class="t-#{type}-summary">\n #{label}: <span class="coverage-disabled">disabled</span>\n </div>)
172-
end
173-
174-
def missed_summary_html(count, missed_class, toggle)
175-
missed = if toggle
176-
%(<a href="#" class="t-missed-method-toggle"><b>#{count}</b> missed</a>)
177-
else
178-
%(<span class="#{missed_class}"><b>#{count}</b> missed</span>)
179-
end
180-
%(<span class="coverage-cell__fraction">,</span>\n #{missed})
181-
end
18274
end
18375
end
18476
end

public/application.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)