6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# File 'lib/rspec_let_analyzer/formatters/html_formatter.rb', line 6
def format(analyzer:, limit:, sort_by:)
sorted = analyzer.top(limit, sort_by)
totals = analyzer.totals
total_files = analyzer.results.size
nesting_depth = analyzer.nesting_depth
html = []
html << '<!DOCTYPE html>'
html << '<html lang="en">'
html << '<head>'
html << ' <meta charset="UTF-8">'
html << ' <meta name="viewport" content="width=device-width, initial-scale=1.0">'
html << ' <title>RSpec Stats Report</title>'
html << ' <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">'
html << ' <style>'
html << ' body { padding: 2rem; background-color: #f8f9fa; }'
html << ' .table-container { background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 2rem; }'
html << ' h1 { color: #212529; margin-bottom: 1.5rem; }'
html << ' h2 { color: #495057; margin-top: 2rem; margin-bottom: 1rem; font-size: 1.5rem; }'
html << ' .stats-summary { display: flex; gap: 1rem; margin-bottom: 2rem; }'
html << ' .stat-card { flex: 1; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 1.5rem; border-radius: 8px; }'
html << ' .stat-card h3 { font-size: 0.875rem; margin: 0; opacity: 0.9; }'
html << ' .stat-card .value { font-size: 2rem; font-weight: bold; margin-top: 0.5rem; }'
html << ' .table { margin-bottom: 0; }'
html << ' .table thead { background-color: #f8f9fa; }'
html << ' </style>'
html << '</head>'
html << '<body>'
html << ' <div class="container-fluid">'
html << ' <h1>RSpec Stats Report</h1>'
html << ' <div class="stats-summary">'
html << ' <div class="stat-card">'
html << ' <h3>Total Files</h3>'
html << " <div class=\"value\">#{total_files}</div>"
html << ' </div>'
html << ' <div class="stat-card" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">'
html << ' <h3>Root Lets</h3>'
html << " <div class=\"value\">#{totals[:root_lets]}</div>"
html << ' </div>'
html << ' <div class="stat-card" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">'
html << ' <h3>It Blocks</h3>'
html << " <div class=\"value\">#{totals[:it_blocks]}</div>"
html << ' </div>'
html << ' <div class="stat-card" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">'
html << ' <h3>Redefinitions</h3>'
html << " <div class=\"value\">#{totals[:redefinitions]}</div>"
html << ' </div>'
html << ' </div>'
sort_column = case sort_by
when 'total' then 'Total'
when 'root' then 'Root'
when 'it' then 'it blocks'
when 'redef' then 'Redefinitions'
when 'before' then 'Before Creates'
else sort_by
end
html << ' <div class="table-container">'
html << " <h2>Top #{limit} Files (sorted by #{sort_column})</h2>"
html << ' <table class="table table-striped table-hover">'
html << ' <thead>'
html << ' <tr>'
html << ' <th>File</th>'
html << (sort_by == 'total' ? ' <th class="text-end table-primary">Total ▼</th>' : ' <th class="text-end">Total</th>')
html << (sort_by == 'root' ? ' <th class="text-end table-primary">Root ▼</th>' : ' <th class="text-end">Root</th>')
html << (sort_by == 'it' ? ' <th class="text-end table-primary">it blocks ▼</th>' : ' <th class="text-end">it blocks</th>')
if nesting_depth
(1...nesting_depth).each { |i| html << " <th class=\"text-end\">Nest #{i}</th>" }
html << " <th class=\"text-end\">Nest #{nesting_depth}+</th>"
end
html << (sort_by == 'redef' ? ' <th class="text-end table-primary">Redefinitions ▼</th>' : ' <th class="text-end">Redefinitions</th>')
html << (sort_by == 'before' ? ' <th class="text-end table-primary">Before Creates ▼</th>' : ' <th class="text-end">Before Creates</th>')
html << ' </tr>'
html << ' </thead>'
html << ' <tbody>'
sorted.each do |result|
html << ' <tr>'
html << " <td><code>#{escape_html(result[:file])}</code></td>"
html << " <td class=\"text-end\">#{result[:total_score]}</td>"
html << " <td class=\"text-end\">#{result[:root_lets]}</td>"
html << " <td class=\"text-end\">#{result[:it_blocks]}</td>"
nesting_depth&.times do |i|
html << " <td class=\"text-end\">#{result[:"nesting_#{i + 1}"]}</td>"
end
html << " <td class=\"text-end\">#{result[:redefinitions]}</td>"
html << " <td class=\"text-end\">#{result[:before_creates]}</td>"
html << ' </tr>'
end
html << ' </tbody>'
html << ' <tfoot>'
html << ' <tr class="table-secondary fw-bold">'
html << " <td>TOTAL (all #{total_files} files)</td>"
html << " <td class=\"text-end\">#{totals[:total_score]}</td>"
html << " <td class=\"text-end\">#{totals[:root_lets]}</td>"
html << " <td class=\"text-end\">#{totals[:it_blocks]}</td>"
nesting_depth&.times { html << ' <td class="text-end">-</td>' }
html << " <td class=\"text-end\">#{totals[:redefinitions]}</td>"
html << " <td class=\"text-end\">#{totals[:before_creates]}</td>"
html << ' </tr>'
html << ' </tfoot>'
html << ' </table>'
html << ' </div>'
if analyzer.factory_stats
html << format_factory_stats_html(analyzer, limit)
end
html << ' </div>'
html << '</body>'
html << '</html>'
html.join("\n")
end
|