'
+ html += ''
+ esc(t('Run log')) + (m.log_truncated ? ' (' + esc(t('truncated')) + ')' : '') + '
'
+ ''
+ esc(m.log.join('\n')) + ' ';
diff --git a/frontend/style.css b/frontend/style.css
index 4198c55..c647f01 100644
--- a/frontend/style.css
+++ b/frontend/style.css
@@ -620,6 +620,10 @@ video {
visibility: visible;
}
+.collapse {
+ visibility: collapse;
+}
+
.static {
position: static;
}
@@ -976,12 +980,12 @@ video {
min-width: 0px;
}
-.min-w-\[14rem\] {
- min-width: 14rem;
+.min-w-\[12rem\] {
+ min-width: 12rem;
}
-.min-w-\[16rem\] {
- min-width: 16rem;
+.min-w-\[14rem\] {
+ min-width: 14rem;
}
.min-w-\[180px\] {
@@ -1357,6 +1361,11 @@ video {
border-top-color: transparent;
}
+.bg-amber-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(254 243 199 / var(--tw-bg-opacity, 1));
+}
+
.bg-amber-50 {
--tw-bg-opacity: 1;
background-color: rgb(255 251 235 / var(--tw-bg-opacity, 1));
@@ -1439,6 +1448,11 @@ video {
background-color: rgb(250 250 250 / var(--tw-bg-opacity, 1));
}
+.bg-purple-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 232 255 / var(--tw-bg-opacity, 1));
+}
+
.bg-red-100 {
--tw-bg-opacity: 1;
background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1));
@@ -1571,6 +1585,10 @@ video {
padding-bottom: 0.25rem;
}
+.pb-1\.5 {
+ padding-bottom: 0.375rem;
+}
+
.pb-3 {
padding-bottom: 0.75rem;
}
@@ -1591,6 +1609,10 @@ video {
padding-left: 1.5rem;
}
+.pl-7 {
+ padding-left: 1.75rem;
+}
+
.pr-2 {
padding-right: 0.5rem;
}
@@ -1784,6 +1806,11 @@ video {
color: rgb(147 51 234 / var(--tw-text-opacity, 1));
}
+.text-purple-700 {
+ --tw-text-opacity: 1;
+ color: rgb(126 34 206 / var(--tw-text-opacity, 1));
+}
+
.text-red-400 {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity, 1));
@@ -2028,6 +2055,10 @@ video {
background-color: rgb(120 53 15 / 0.2);
}
+.dark\:bg-amber-900\/40:is(.dark *) {
+ background-color: rgb(120 53 15 / 0.4);
+}
+
.dark\:bg-blue-500:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));
@@ -2049,6 +2080,10 @@ video {
background-color: rgb(6 78 59 / 0.4);
}
+.dark\:bg-purple-900\/40:is(.dark *) {
+ background-color: rgb(88 28 135 / 0.4);
+}
+
.dark\:bg-red-900\/30:is(.dark *) {
background-color: rgb(127 29 29 / 0.3);
}
@@ -2067,6 +2102,10 @@ video {
background-color: rgb(30 41 59 / 0.4);
}
+.dark\:bg-slate-800\/50:is(.dark *) {
+ background-color: rgb(30 41 59 / 0.5);
+}
+
.dark\:bg-slate-800\/60:is(.dark *) {
background-color: rgb(30 41 59 / 0.6);
}
@@ -2126,6 +2165,11 @@ video {
color: rgb(129 140 248 / var(--tw-text-opacity, 1));
}
+.dark\:text-purple-300:is(.dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(216 180 254 / var(--tw-text-opacity, 1));
+}
+
.dark\:text-purple-400:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(192 132 252 / var(--tw-text-opacity, 1));
diff --git a/frontend/sw.js b/frontend/sw.js
index 35019e1..376e187 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simple-v398';
+const CACHE = 'simple-v400';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/testsuites.test.js b/frontend/tests/testsuites.test.js
index d4912ef..b129b56 100644
--- a/frontend/tests/testsuites.test.js
+++ b/frontend/tests/testsuites.test.js
@@ -184,3 +184,30 @@ test('role rows cannot be moved and the i18n refresh redraws the test views', ()
const i18n = extractFunc(html, 'refreshDynamicI18n');
assert.match(i18n, /isViewVisible\('phone-sub-test'\)\) testRender\(\)/);
});
+
+test('the report keeps an expanded run log open across the live polls', () => {
+ // A live suite run re-renders the member blocks on every poll; replacing
+ // innerHTML creates fresh , so the open state is carried over
+ // by the stable member index.
+ const member = extractFunc(html, 'testReportMemberHtml');
+ assert.match(member, /data-member="/);
+ const report = extractFunc(html, 'testRenderReport');
+ assert.match(report, /details\[data-member\]/);
+ assert.match(report, /openLogs\[d\.dataset\.member\]/);
+ assert.match(report, /d\.open = true/);
+});
+
+test('the report header classes are generated in the prebuilt stylesheet', () => {
+ // The stale-style trap (v3.22.0 review): style.css is a prebuilt Tailwind
+ // output - a class used only in the PWA source has no rule until
+ // `npm run build` runs in frontend/. The report's member header is the
+ // regression case (a near-white bg + dark-theme text made titles vanish).
+ const css = fs.readFileSync(path.join(__dirname, '..', 'style.css'), 'utf8');
+ for (const sel of ['.bg-gray-50', '.dark\\:bg-slate-800\\/50',
+ '.dark\\:text-slate-200', '.bg-purple-100', '.dark\\:bg-purple-900\\/40',
+ '.min-w-\\[12rem\\]']) {
+ assert.ok(css.includes(sel), 'missing rule for ' + sel);
+ }
+ assert.match(extractFunc(html, 'testReportMemberHtml'),
+ /bg-gray-50 dark:bg-slate-800\/50/);
+});