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
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private static String buildScreenshotCell(JsonNode test) {
String base64 = Base64.getEncoder().encodeToString(bytes);
String src = "data:image/png;base64," + base64;
return "<img src=\"" + src + "\" class=\"screenshot-thumb\" "
+ "onclick=\"openLightbox(this.src)\" title=\"Click to view full size\" />";
+ "onclick=\"openLightbox(this)\" title=\"Click to view full size\" />";
} catch (java.io.IOException e) {
return "<span class=\"screenshot-error\">load error</span>";
}
Expand Down Expand Up @@ -1066,7 +1066,7 @@ private static String buildStepTimeline(JsonNode test) {
if (step.has("screenshotBase64")) {
String src = "data:image/png;base64," + step.get("screenshotBase64").asText();
thumbHtml = "<img src=\"" + src + "\" class=\"step-thumb\" "
+ "onclick=\"openLightbox(this.src)\" title=\"Click to view full size\" />";
+ "onclick=\"openLightbox(this)\" title=\"Click to view full size\" />";
}

sb.append("<div class=\"step-item\">")
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/report/partials/lightbox.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<!-- Lightbox modal -->
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
<span class="lightbox-close" onclick="closeLightbox()">&times;</span>
<img id="lightbox-img" src="" alt="Screenshot">
<button type="button" class="lightbox-nav lightbox-prev" id="lightbox-prev" onclick="stepLightbox(-1, event)" aria-label="Previous screenshot">&#10094;</button>
<img id="lightbox-img" src="" alt="Screenshot" onclick="event.stopPropagation()">
<button type="button" class="lightbox-nav lightbox-next" id="lightbox-next" onclick="stepLightbox(1, event)" aria-label="Next screenshot">&#10095;</button>
<span class="lightbox-counter" id="lightbox-counter"></span>
</div>
6 changes: 6 additions & 0 deletions src/main/resources/report/report.css
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@
.lightbox img { max-width: 92vw; max-height: 92vh; border-radius: 6px; box-shadow: 0 8px 40px rgba(0,0,0,.6); }
.lightbox-close { position: fixed; top: 18px; right: 24px; color: #fff; font-size: 32px; cursor: pointer; line-height: 1; opacity: .8; }
.lightbox-close:hover { opacity: 1; }
.lightbox-nav { position: fixed; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,.15); color: #fff; border: 0; border-radius: 50%; width: 44px; height: 44px; font-size: 20px; cursor: pointer; }
.lightbox-nav:hover:not(:disabled) { background: rgba(255,255,255,.3); }
.lightbox-nav:disabled { opacity: .25; cursor: default; }
.lightbox-prev { left: 20px; }
.lightbox-next { right: 20px; }
.lightbox-counter { position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%); color: #fff; font-size: 14px; background: rgba(0,0,0,.5); padding: 3px 12px; border-radius: 12px; }

/* ── Filter + search bar ── */
.filter-bar { display: flex; align-items: center; gap: 10px; padding: 14px 24px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
Expand Down
44 changes: 41 additions & 3 deletions src/main/resources/report/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,56 @@ function showTab(tabId, el) {
if (el) el.classList.add('active');
}
// ── Lightbox ──
function openLightbox(src) {
document.getElementById('lightbox-img').src = src;
var lightboxImgs = [];
var lightboxIdx = 0;
// Screenshots of the same test: its row thumbnail plus the thumbnails in its detail row.
function collectTestScreenshots(img) {
var row = img.closest('tr');
if (!row) return [img];
var testRow = row.classList.contains('detail-row') ? row.previousElementSibling : row;
var detailRow = testRow && testRow.nextElementSibling;
var rows = [testRow];
if (detailRow && detailRow.classList.contains('detail-row')) rows.push(detailRow);
var imgs = [];
rows.forEach(function(r) {
if (r) r.querySelectorAll('img.screenshot-thumb, img.step-thumb').forEach(function(i) { imgs.push(i); });
});
return imgs.indexOf(img) >= 0 ? imgs : [img];
}
function showLightbox() {
document.getElementById('lightbox-img').src = lightboxImgs[lightboxIdx].src;
var multi = lightboxImgs.length > 1;
document.getElementById('lightbox-counter').textContent = multi ? (lightboxIdx + 1) + ' / ' + lightboxImgs.length : '';
document.getElementById('lightbox-prev').style.display = multi ? '' : 'none';
document.getElementById('lightbox-next').style.display = multi ? '' : 'none';
document.getElementById('lightbox-prev').disabled = lightboxIdx === 0;
document.getElementById('lightbox-next').disabled = lightboxIdx === lightboxImgs.length - 1;
}
function openLightbox(img) {
lightboxImgs = collectTestScreenshots(img);
lightboxIdx = lightboxImgs.indexOf(img);
showLightbox();
document.getElementById('lightbox').classList.add('open');
document.body.style.overflow = 'hidden';
}
function stepLightbox(delta, e) {
if (e) e.stopPropagation();
var next = lightboxIdx + delta;
if (next < 0 || next >= lightboxImgs.length) return;
lightboxIdx = next;
showLightbox();
}
function closeLightbox() {
document.getElementById('lightbox').classList.remove('open');
document.getElementById('lightbox-img').src = '';
document.body.style.overflow = '';
lightboxImgs = [];
}
document.addEventListener('keydown', function(e) {
if (!document.getElementById('lightbox').classList.contains('open')) return;
if (e.key === 'Escape') closeLightbox();
else if (e.key === 'ArrowLeft') stepLightbox(-1);
else if (e.key === 'ArrowRight') stepLightbox(1);
});

// ── Dark mode ──
Expand Down Expand Up @@ -481,4 +519,4 @@ window.addEventListener('afterprint', function () {
}

printState = null;
});
});
Loading