11# frozen_string_literal: true
22
3- require "digest/sha1 "
3+ require "digest/md5 "
44require "set"
55
66module SimpleCov
77 module Formatter
88 class HTMLFormatter
99 # Helper methods used by ERB templates for rendering coverage data.
10- module ViewHelpers
10+ module ViewHelpers # rubocop:disable Metrics/ModuleLength
1111 def line_status? ( source_file , line )
1212 if branch_coverage? && source_file . line_with_missed_branch? ( line . number )
1313 "missed-branch"
@@ -44,18 +44,8 @@ def coverage_css_class(covered_percent)
4444 end
4545 end
4646
47- def strength_css_class ( covered_strength )
48- if covered_strength > 1
49- "green"
50- elsif covered_strength == 1
51- "yellow"
52- else
53- "red"
54- end
55- end
56-
5747 def id ( source_file )
58- Digest ::SHA1 . hexdigest ( source_file . filename )
48+ Digest ::MD5 . hexdigest ( source_file . filename )
5949 end
6050
6151 def timeago ( time )
@@ -83,8 +73,85 @@ def fmt(number)
8373 number . to_s . gsub ( /(\d )(?=(\d {3})+(?!\d ))/ , '\\1,' )
8474 end
8575
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 "> <</ option > < option value ="lte " selected > ≤</ option > < option value ="eq "> =</ option > < option value ="gte "> ≥</ option > < option value ="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+
86153 def coverage_summary ( stats , show_method_toggle : false )
87- @ _summary = {
154+ _summary = {
88155 line : build_stats ( stats . fetch ( :covered_lines ) , stats . fetch ( :total_lines ) ) ,
89156 branch : build_stats ( stats . fetch ( :covered_branches , 0 ) , stats . fetch ( :total_branches , 0 ) ) ,
90157 method : build_stats ( stats . fetch ( :covered_methods , 0 ) , stats . fetch ( :total_methods , 0 ) ) ,
@@ -97,6 +164,21 @@ def build_stats(covered, total)
97164 pct = total . positive? ? ( covered * 100.0 / total ) : 100.0
98165 { covered : covered , total : total , missed : total - covered , pct : pct }
99166 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
100182 end
101183 end
102184 end
0 commit comments