diff --git a/src/main/resources/report/partials/tests.html b/src/main/resources/report/partials/tests.html
index f34b747..6377659 100644
--- a/src/main/resources/report/partials/tests.html
+++ b/src/main/resources/report/partials/tests.html
@@ -11,6 +11,10 @@
+
+
+
+
🔍
diff --git a/src/main/resources/report/report.css b/src/main/resources/report/report.css
index a53fccb..6de8919 100644
--- a/src/main/resources/report/report.css
+++ b/src/main/resources/report/report.css
@@ -233,6 +233,7 @@
.filter-bar { display: flex; align-items: center; gap: 10px; padding: 14px 24px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.filter-btn { border: 1px solid var(--border); background: var(--surface); color: var(--text-secondary); border-radius: 16px; padding: 4px 14px; font-size: 13px; cursor: pointer; transition: all .15s; }
.filter-btn:hover, .filter-btn.active { background: var(--primary); color: #fff; border-color: var(--primary); }
+ .expand-controls { display: inline-flex; gap: 8px; margin-left: 8px; }
.search-wrap { margin-left: auto; display: flex; align-items: center; border: 1px solid var(--border); border-radius: 20px; padding: 4px 14px; background: var(--surface2); }
.search-wrap input { border: none; background: transparent; outline: none; font-size: 13px; color: var(--text); width: 200px; }
.search-wrap input::placeholder { color: var(--text-secondary); }
@@ -474,6 +475,7 @@
.sidebar,
.filter-bar,
.filter-btn,
+ .expand-controls,
.search-wrap,
.dark-toggle,
.copy-failure-btn,
diff --git a/src/main/resources/report/report.js b/src/main/resources/report/report.js
index 41eb396..5e08d00 100644
--- a/src/main/resources/report/report.js
+++ b/src/main/resources/report/report.js
@@ -102,6 +102,27 @@ function toggleDetail(idx) {
if (icon) icon.classList.toggle('open', !isOpen);
}
+// ── Expand all / collapse all (tests matching the active filter and search) ──
+function setAllDetails(open) {
+ const q = document.getElementById('searchInput').value.toLowerCase();
+ if (open) {
+ document.querySelectorAll('#testTable tbody tr.group-header').forEach(hdr => {
+ if (hdr.style.display === 'none') return;
+ const icon = document.getElementById('gicon-' + hdr.getAttribute('data-group'));
+ if (icon) icon.classList.remove('closed');
+ });
+ }
+ document.querySelectorAll('#testTable tbody tr.group-member.expandable').forEach(row => {
+ if (!rowMatches(row, q) || !groupIsOpen(row.getAttribute('data-group'))) return;
+ const detail = row.nextElementSibling;
+ if (!detail || !detail.classList.contains('detail-row')) return;
+ row.style.display = '';
+ detail.style.display = open ? '' : 'none';
+ const icon = row.querySelector('.expand-icon');
+ if (icon) icon.classList.toggle('open', open);
+ });
+}
+
// ── Tag filtering ──
let activeTagFilter = '';
function filterByTag(tagName, event) {
@@ -152,27 +173,36 @@ function setFilter(status, btn) {
} catch (e) {}
applySearch();
}
+function rowMatches(row, q) {
+ const rowStatus = row.getAttribute('data-status') || '';
+ const rowTest = row.getAttribute('data-test') || '';
+ const groupId = row.getAttribute('data-group') || '';
+ const rowTags = (row.getAttribute('data-tags') || '').split(',').map(t => t.trim().toLowerCase()).filter(Boolean);
+ const hasRetry = row.querySelector('.retry-badge') !== null;
+ const matchFilter = activeFilter === 'ALL'
+ || activeFilter === rowStatus
+ || (activeFilter === 'FLAKY' && hasRetry);
+ const matchTag = !activeTagFilter || rowTags.includes(activeTagFilter);
+ const matchSearch = !q
+ || rowTest.includes(q)
+ || groupId.toLowerCase().includes(q)
+ || rowTags.some(t => t.includes(q));
+ return matchFilter && matchTag && matchSearch;
+}
+function groupIsOpen(groupId) {
+ const icon = document.getElementById('gicon-' + groupId);
+ return !icon || !icon.classList.contains('closed');
+}
function applySearch() {
const q = document.getElementById('searchInput').value.toLowerCase();
const members = document.querySelectorAll('#testTable tbody tr.group-member:not(.detail-row)');
const visibleGroups = new Set();
members.forEach(row => {
- const rowStatus = row.getAttribute('data-status') || '';
- const rowTest = row.getAttribute('data-test') || '';
- const groupId = row.getAttribute('data-group') || '';
- const rowTags = (row.getAttribute('data-tags') || '').split(',').map(t => t.trim().toLowerCase()).filter(Boolean);
- const hasRetry = row.querySelector('.retry-badge') !== null;
- const matchFilter = activeFilter === 'ALL'
- || activeFilter === rowStatus
- || (activeFilter === 'FLAKY' && hasRetry);
- const matchTag = !activeTagFilter || rowTags.includes(activeTagFilter);
- const matchSearch = !q
- || rowTest.includes(q)
- || groupId.toLowerCase().includes(q)
- || rowTags.some(t => t.includes(q));
- const show = matchFilter && matchTag && matchSearch;
+ const groupId = row.getAttribute('data-group') || '';
+ const match = rowMatches(row, q);
+ const show = match && groupIsOpen(groupId);
row.style.display = show ? '' : 'none';
- if (show) visibleGroups.add(groupId);
+ if (match) visibleGroups.add(groupId);
const next = row.nextElementSibling;
if (next && next.classList.contains('detail-row') && !show) next.style.display = 'none';
});