Class: RSpecLetAnalyzer::Formatters::HtmlFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/rspec_let_analyzer/formatters/html_formatter.rb

Instance Method Summary collapse

Instance Method Details

#add_runtime_metrics(output, metrics) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rspec_let_analyzer/formatters/html_formatter.rb', line 134

def add_runtime_metrics(output, metrics)
  # Insert runtime metrics before closing body tag
  metrics_html = []
  metrics_html << '    <div class="table-container">'
  metrics_html << '      <h2>Runtime Metrics</h2>'
  metrics_html << '      <table class="table table-sm" style="width: auto;">'
  metrics_html << '        <tbody>'
  metrics_html << "          <tr><td class=\"fw-bold\">Analysis:</td><td>#{format_duration(metrics[:analyze_time])}</td></tr>"
  metrics_html << "          <tr><td class=\"fw-bold\">Formatting:</td><td>#{format_duration(metrics[:format_time])}</td></tr>"
  metrics_html << "          <tr class=\"table-secondary\"><td class=\"fw-bold\">Total:</td><td class=\"fw-bold\">#{format_duration(metrics[:total_time])}</td></tr>"
  metrics_html << '        </tbody>'
  metrics_html << '      </table>'
  metrics_html << '    </div>'

  output.sub('  </div>', "#{metrics_html.join("\n")}\n  </div>")
end

#format(analyzer:, limit:, sort_by:) ⇒ Object



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>'

  # Summary cards
  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>'

  # Determine sort indicator
  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

  # Main table
  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>'

  # FactoryBot section
  if analyzer.factory_stats
    html << format_factory_stats_html(analyzer, limit)
  end

  html << '  </div>'
  html << '</body>'
  html << '</html>'

  html.join("\n")
end