Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/java/com/seleniumboot/reporting/HtmlReportGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,10 @@ static String buildHtml(JsonNode root) {
String template = loadTemplate();

return template
.replace("{{CHART_JS}}", loadResource("report/chart.umd.min.js"))
.replace("{{REPORT_CSS}}", loadResource("report/report.css"))
.replace("{{REPORT_JS}}", loadResource("report/report.js"))
.replace("{{REPORT_MARKUP}}", loadMarkup())
.replace("{{RUN_SUMMARY}}", metadata[0])
.replace("{{METADATA}}", metadata[1])
.replace("{{PASSED}}", String.valueOf(passedTests))
Expand Down Expand Up @@ -1025,6 +1027,18 @@ private static String loadTemplate() {
return loadResource("report-template.html");
}

private static String loadMarkup() {
return loadResource("report/partials/appbar.html")
+ loadResource("report/partials/lightbox.html")
+ "<div class=\"layout\">\n"
+ loadResource("report/partials/sidebar.html")
+ "<main class=\"main-content\">\n"
+ loadResource("report/partials/dashboard.html")
+ loadResource("report/partials/tests.html")
+ loadResource("report/partials/failures.html")
+ "</main>\n</div>\n";
}

private static String loadResource(String name) {
try (InputStream is = HtmlReportGenerator.class
.getClassLoader()
Expand Down
209 changes: 5 additions & 204 deletions src/main/resources/report-template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,215 +4,16 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Selenium Boot Report</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js" onerror="window.Chart=function(){this.destroy=function(){}};Chart.prototype={};"></script>

<script>
{{CHART_JS}}
</script>
<style>
{{REPORT_CSS}}
</style>
</head>
<body>

<div class="app-bar">
<div class="app-bar-left">
<h1>Selenium Boot Report</h1>
<p>Execution Metrics &amp; Performance Analysis</p>
</div>
<button type="button" class="dark-toggle" onclick="toggleDark()">&#9790; Dark Mode</button>
</div>

<!-- Lightbox modal -->
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
<span class="lightbox-close" onclick="closeLightbox()">&times;</span>
<img id="lightbox-img" src="" alt="Screenshot">
</div>

<div class="layout">

<!-- ── Left sidebar ── -->
<nav class="sidebar">
<div class="sidebar-brand">&#9889; Selenium Boot</div>
<ul class="nav-list">
<li class="nav-item active" onclick="showTab('dashboard', this)">
<span class="nav-icon">&#128200;</span>
<span class="nav-label">Dashboard</span>
</li>
<li class="nav-item" onclick="showTab('tests', this)">
<span class="nav-icon">&#9635;</span>
<span class="nav-label">Test Cases</span>
<span class="nav-count">{{TOTAL_TESTS}}</span>
</li>
<li class="nav-item" onclick="showTab('failures', this)">
<span class="nav-icon">&#10005;</span>
<span class="nav-label">Failures</span>
{{FAILURE_BADGE}}
</li>
</ul>
<div class="sidebar-stats">
<div>Duration: <strong>{{SUITE_DURATION}}</strong></div>
<div>Pass rate: <strong class="{{PASS_RATE_CLASS}}">{{PASS_RATE}}%</strong></div>
</div>
</nav>

<!-- ── Main content ── -->
<main class="main-content">

<!-- ═══════════════ DASHBOARD TAB ═══════════════ -->
<div id="tab-dashboard" class="tab-pane active">
<div class="tab-heading">Dashboard</div>

{{RUN_SUMMARY}}

<!-- Stat cards -->
<div class="summary-row">
<div class="card stat-card passed-card">
<div class="stat-value">{{PASSED}}</div>
<div class="stat-label">Passed</div>
</div>
<div class="card stat-card failed-card">
<div class="stat-value">{{FAILED}}</div>
<div class="stat-label">Failed</div>
</div>
<div class="card stat-card skipped-card">
<div class="stat-value">{{SKIPPED}}</div>
<div class="stat-label">Skipped</div>
</div>
<div class="card stat-card">
<div class="stat-value">{{TOTAL_TESTS}}</div>
<div class="stat-label">Total Tests</div>
</div>
<div class="card stat-card">
<div class="stat-value {{PASS_RATE_CLASS}}">{{PASS_RATE}}%</div>
<div class="stat-label">Pass Rate</div>
</div>
<div class="card stat-card">
<div class="stat-value">{{SUITE_DURATION}}</div>
<div class="stat-label">Suite Time (hh:mm:ss)</div>
</div>
</div>

{{FAILURES_SHORTCUT}}

{{AI_RUN_SUMMARY}}

<!-- Charts -->
<div class="charts-row">
<div class="card chart-card">
<div class="card-header">Pass / Fail / Skip</div>
<div class="card-body-centered">
<canvas id="donutChart" class="donut-canvas"></canvas>
</div>
</div>
<div class="card chart-card">
<div class="card-header">Execution Time Percentiles (ms)</div>
<div class="card-body"><canvas id="executionChart"></canvas></div>
</div>
</div>

<!-- Trend over runs (hidden until there are 2+ runs of history) -->
<div class="charts-row" id="trendRow" style="display:none">
<div class="card chart-card">
<div class="card-header">Pass Rate Trend (%)</div>
<div class="card-body"><canvas id="trendRateChart"></canvas></div>
</div>
<div class="card chart-card">
<div class="card-header">Suite Duration Trend (s)</div>
<div class="card-body"><canvas id="trendTimeChart"></canvas></div>
</div>
<div class="card chart-card">
<div class="card-header">Flaky Test Trend</div>
<div class="card-body"><canvas id="trendFlakyChart"></canvas></div>
</div>
</div>

<!-- Retry summary -->
{{RETRY_SECTION}}

<!-- Slowest 5 -->
<div class="card section-mb">
<div class="card-header">Slowest 5 Tests</div>
<div class="card-body">{{SLOWEST_TESTS}}</div>
</div>

{{TAG_RESULTS}}

<!-- Flakiness Radar -->
{{FLAKINESS_SECTION}}

<!-- Run configuration (collapsed) -->
{{METADATA}}

<div class="footer">Report generated by <strong>Selenium Boot</strong> &mdash; suite ran {{SUITE_DURATION}} (hh:mm:ss)</div>
</div>

<!-- ═══════════════ TEST CASES TAB ═══════════════ -->
<div id="tab-tests" class="tab-pane">
<div class="tab-heading">Test Cases</div>
<div class="card table-card">
<div class="filter-bar">
<button type="button" class="filter-btn active" onclick="setFilter('ALL', this)">All</button>
<button type="button" class="filter-btn" onclick="setFilter('PASSED', this)">Passed</button>
<button type="button" class="filter-btn" onclick="setFilter('FAILED', this)">Failed</button>
<button type="button" class="filter-btn" onclick="setFilter('SKIPPED', this)">Skipped</button>
<button type="button" class="filter-btn" onclick="setFilter('FLAKY', this)">Flaky</button>
<span id="activeTagPill" class="active-tag-pill" style="display:none;">
<span id="activeTagName"></span>
<button type="button" class="tag-clear-btn" onclick="clearTagFilter()" title="Clear tag filter">&times;</button>
</span>
<div class="search-wrap">
<span class="search-icon">&#128269;</span>
<input type="text" id="searchInput" placeholder="Search by test, class, or tag..." oninput="applySearch()">
</div>
</div>
<div class="table-wrap">
<table id="testTable">
<thead>
<tr>
<th>Test</th>
<th>Description</th>
<th>Status</th>
<th>Logic (ms)</th>
<th>Total (ms)</th>
<th>Screenshot</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{{ROWS}}
</tbody>
</table>
</div>
</div>
</div>

<!-- ═══════════════ FAILURES TAB ═══════════════ -->
<div id="tab-failures" class="tab-pane">
<div class="tab-heading">Failures <span style="font-size:14px;font-weight:400;color:var(--text-secondary)">({{FAILED}} failed)</span></div>
{{FAILURE_GROUPS}}
<div class="card table-card">
<div class="table-wrap">
<table id="failureTable">
<thead>
<tr>
<th>Test</th>
<th>Description</th>
<th>Status</th>
<th>Logic (ms)</th>
<th>Total (ms)</th>
<th>Screenshot</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{{FAILURE_ROWS}}
</tbody>
</table>
</div>
</div>
</div>

</main>
</div>

{{REPORT_MARKUP}}
<script>
{{REPORT_JS}}
</script>
Expand Down
20 changes: 20 additions & 0 deletions src/main/resources/report/chart.umd.min.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/main/resources/report/partials/appbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="app-bar">
<div class="app-bar-left">
<h1>Selenium Boot Report</h1>
<p>Execution Metrics &amp; Performance Analysis</p>
</div>
<button type="button" class="dark-toggle" onclick="toggleDark()">&#9790; Dark Mode</button>
</div>
86 changes: 86 additions & 0 deletions src/main/resources/report/partials/dashboard.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<div id="tab-dashboard" class="tab-pane active">
<div class="tab-heading">Dashboard</div>

{{RUN_SUMMARY}}

<!-- Stat cards -->
<div class="summary-row">
<div class="card stat-card passed-card">
<div class="stat-value">{{PASSED}}</div>
<div class="stat-label">Passed</div>
</div>
<div class="card stat-card failed-card">
<div class="stat-value">{{FAILED}}</div>
<div class="stat-label">Failed</div>
</div>
<div class="card stat-card skipped-card">
<div class="stat-value">{{SKIPPED}}</div>
<div class="stat-label">Skipped</div>
</div>
<div class="card stat-card">
<div class="stat-value">{{TOTAL_TESTS}}</div>
<div class="stat-label">Total Tests</div>
</div>
<div class="card stat-card">
<div class="stat-value {{PASS_RATE_CLASS}}">{{PASS_RATE}}%</div>
<div class="stat-label">Pass Rate</div>
</div>
<div class="card stat-card">
<div class="stat-value">{{SUITE_DURATION}}</div>
<div class="stat-label">Suite Time (hh:mm:ss)</div>
</div>
</div>

{{FAILURES_SHORTCUT}}

{{AI_RUN_SUMMARY}}

<!-- Charts -->
<div class="charts-row">
<div class="card chart-card">
<div class="card-header">Pass / Fail / Skip</div>
<div class="card-body-centered">
<canvas id="donutChart" class="donut-canvas"></canvas>
</div>
</div>
<div class="card chart-card">
<div class="card-header">Execution Time Percentiles (ms)</div>
<div class="card-body"><canvas id="executionChart"></canvas></div>
</div>
</div>

<!-- Trend over runs (hidden until there are 2+ runs of history) -->
<div class="charts-row" id="trendRow" style="display:none">
<div class="card chart-card">
<div class="card-header">Pass Rate Trend (%)</div>
<div class="card-body"><canvas id="trendRateChart"></canvas></div>
</div>
<div class="card chart-card">
<div class="card-header">Suite Duration Trend (s)</div>
<div class="card-body"><canvas id="trendTimeChart"></canvas></div>
</div>
<div class="card chart-card">
<div class="card-header">Flaky Test Trend</div>
<div class="card-body"><canvas id="trendFlakyChart"></canvas></div>
</div>
</div>

<!-- Retry summary -->
{{RETRY_SECTION}}

<!-- Slowest 5 -->
<div class="card section-mb">
<div class="card-header">Slowest 5 Tests</div>
<div class="card-body">{{SLOWEST_TESTS}}</div>
</div>

{{TAG_RESULTS}}

<!-- Flakiness Radar -->
{{FLAKINESS_SECTION}}

<!-- Run configuration (collapsed) -->
{{METADATA}}

<div class="footer">Report generated by <strong>Selenium Boot</strong> &mdash; suite ran {{SUITE_DURATION}} (hh:mm:ss)</div>
</div>
24 changes: 24 additions & 0 deletions src/main/resources/report/partials/failures.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<div id="tab-failures" class="tab-pane">
<div class="tab-heading">Failures <span style="font-size:14px;font-weight:400;color:var(--text-secondary)">({{FAILED}} failed)</span></div>
{{FAILURE_GROUPS}}
<div class="card table-card">
<div class="table-wrap">
<table id="failureTable">
<thead>
<tr>
<th>Test</th>
<th>Description</th>
<th>Status</th>
<th>Logic (ms)</th>
<th>Total (ms)</th>
<th>Screenshot</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{{FAILURE_ROWS}}
</tbody>
</table>
</div>
</div>
</div>
5 changes: 5 additions & 0 deletions src/main/resources/report/partials/lightbox.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- Lightbox modal -->
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
<span class="lightbox-close" onclick="closeLightbox()">&times;</span>
<img id="lightbox-img" src="" alt="Screenshot">
</div>
Loading
Loading