@reportforge/playwright-pdf 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +4 -0
- package/dist/demo/cli.js +16 -2
- package/dist/index.js +71 -42
- package/dist/index.mjs +71 -42
- package/dist/templates/layouts/detailed.hbs +5 -4
- package/dist/templates/layouts/minimal.hbs +2 -0
- package/dist/templates/partials/charts.hbs +30 -16
- package/dist/templates/partials/cover-page.hbs +1 -1
- package/dist/templates/partials/executive-summary.hbs +5 -1
- package/dist/templates/partials/failure-deep-dive.hbs +2 -2
- package/dist/templates/partials/requirements-matrix.hbs +1 -1
- package/dist/templates/partials/slow-tests.hbs +2 -2
- package/dist/templates/partials/suite-breakdown.hbs +2 -1
- package/dist/templates/styles/base.css +244 -6
- package/dist/templates/styles/detailed.css +6 -159
- package/dist/templates/styles/executive.css +3 -76
- package/dist/templates/styles/minimal.css +3 -3
- package/package.json +1 -1
|
@@ -71,12 +71,14 @@
|
|
|
71
71
|
var remaining = 0;
|
|
72
72
|
function chartDone() { if (--remaining === 0) window.__chartsReady = true; }
|
|
73
73
|
|
|
74
|
-
var passed
|
|
75
|
-
var failed
|
|
76
|
-
var
|
|
77
|
-
var
|
|
78
|
-
var
|
|
79
|
-
|
|
74
|
+
var passed = {{charts.passRate.passed}};
|
|
75
|
+
var failed = {{charts.passRate.failed}};
|
|
76
|
+
var timedOut = {{charts.passRate.timedOut}};
|
|
77
|
+
var skipped = {{charts.passRate.skipped}};
|
|
78
|
+
var flaky = {{charts.passRate.flaky}};
|
|
79
|
+
// Pass rate is the canonical server-side stat (flaky counts as pass, denominator
|
|
80
|
+
// is every test) so the doughnut centre can never disagree with the KPI strip.
|
|
81
|
+
var passRate = {{stats.passRate}};
|
|
80
82
|
|
|
81
83
|
/* ── Centre-text plugin for the doughnut ─────────────────────────────── */
|
|
82
84
|
var centerTextPlugin = {
|
|
@@ -107,11 +109,11 @@
|
|
|
107
109
|
new Chart(document.getElementById('passRateChart'), {
|
|
108
110
|
type: 'doughnut',
|
|
109
111
|
data: {
|
|
110
|
-
labels: ['Passed', 'Failed', 'Skipped', 'Flaky'],
|
|
112
|
+
labels: ['Passed', 'Failed', 'Timed Out', 'Skipped', 'Flaky'],
|
|
111
113
|
datasets: [{
|
|
112
|
-
data: [passed, failed, skipped, flaky],
|
|
113
|
-
backgroundColor: ['#3F7D58', '#C3553C', '#7A786E', '#D49452'],
|
|
114
|
-
borderColor: ['#FAF9F5', '#FAF9F5', '#FAF9F5', '#FAF9F5'],
|
|
114
|
+
data: [passed, failed, timedOut, skipped, flaky],
|
|
115
|
+
backgroundColor: ['#3F7D58', '#C3553C', '#B5803A', '#7A786E', '#D49452'],
|
|
116
|
+
borderColor: ['#FAF9F5', '#FAF9F5', '#FAF9F5', '#FAF9F5', '#FAF9F5'],
|
|
115
117
|
borderWidth: 2,
|
|
116
118
|
hoverOffset: 4
|
|
117
119
|
}]
|
|
@@ -135,8 +137,9 @@
|
|
|
135
137
|
/* ── Horizontal suite results bar ────────────────────────────────────── */
|
|
136
138
|
var suiteLabels = [{{{jsonArray charts.suiteResults 'label'}}}];
|
|
137
139
|
var suitePassed = [{{{jsonArray charts.suiteResults 'passed'}}}];
|
|
138
|
-
var suiteFailed
|
|
139
|
-
var
|
|
140
|
+
var suiteFailed = [{{{jsonArray charts.suiteResults 'failed'}}}];
|
|
141
|
+
var suiteTimedOut = [{{{jsonArray charts.suiteResults 'timedOut'}}}];
|
|
142
|
+
var suiteSkipped = [{{{jsonArray charts.suiteResults 'skipped'}}}];
|
|
140
143
|
|
|
141
144
|
remaining += 1;
|
|
142
145
|
new Chart(document.getElementById('suitesBarChart'), {
|
|
@@ -144,9 +147,10 @@
|
|
|
144
147
|
data: {
|
|
145
148
|
labels: suiteLabels,
|
|
146
149
|
datasets: [
|
|
147
|
-
{ label: 'Passed',
|
|
148
|
-
{ label: 'Failed',
|
|
149
|
-
{ label: '
|
|
150
|
+
{ label: 'Passed', data: suitePassed, backgroundColor: '#3F7D58', borderRadius: 3 },
|
|
151
|
+
{ label: 'Failed', data: suiteFailed, backgroundColor: '#C3553C', borderRadius: 3 },
|
|
152
|
+
{ label: 'Timed Out', data: suiteTimedOut, backgroundColor: '#B5803A', borderRadius: 3 },
|
|
153
|
+
{ label: 'Skipped', data: suiteSkipped, backgroundColor: '#7A786E', borderRadius: 3 }
|
|
150
154
|
]
|
|
151
155
|
},
|
|
152
156
|
options: {
|
|
@@ -161,7 +165,17 @@
|
|
|
161
165
|
},
|
|
162
166
|
y: {
|
|
163
167
|
stacked: true,
|
|
164
|
-
|
|
168
|
+
// Widen the label gutter so the longest label isn't clipped at the canvas edge.
|
|
169
|
+
afterFit: function(scale) { if (!scale.$rfPadded) { scale.$rfPadded = true; scale.width += 8; } },
|
|
170
|
+
ticks: {
|
|
171
|
+
font: { size: 9, family: 'Inter, sans-serif' }, color: '#1F1F1C', autoSkip: false,
|
|
172
|
+
// Truncate long suite / describe labels so they fit the axis gutter
|
|
173
|
+
// instead of clipping at the canvas edge.
|
|
174
|
+
callback: function(value) {
|
|
175
|
+
var l = this.getLabelForValue(value);
|
|
176
|
+
return l.length > 24 ? l.slice(0, 23) + '…' : l;
|
|
177
|
+
}
|
|
178
|
+
},
|
|
165
179
|
grid: { display: false },
|
|
166
180
|
border: { color: '#D6D4C8' }
|
|
167
181
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="section">
|
|
2
2
|
<span class="verdict verdict--{{stats.verdict}}">
|
|
3
|
-
{{
|
|
3
|
+
{{verdictLabel stats.verdict}}
|
|
4
4
|
</span>
|
|
5
5
|
|
|
6
6
|
<div class="kpi-strip">
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
<div class="kpi-card__value">{{stats.failed}}</div>
|
|
20
20
|
<div class="kpi-card__label">Failed</div>
|
|
21
21
|
</div>
|
|
22
|
+
<div class="kpi-card kpi-card--timedout">
|
|
23
|
+
<div class="kpi-card__value">{{stats.timedOut}}</div>
|
|
24
|
+
<div class="kpi-card__label">Timed Out</div>
|
|
25
|
+
</div>
|
|
22
26
|
<div class="kpi-card kpi-card--skip">
|
|
23
27
|
<div class="kpi-card__value">{{stats.skipped}}</div>
|
|
24
28
|
<div class="kpi-card__label">Skipped</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{#if failures.length}}
|
|
2
|
-
<div class="section
|
|
2
|
+
<div class="section">
|
|
3
3
|
<h2>Failures ({{failures.length}}{{#if overflowFailureCount}} shown · {{overflowFailureCount}} more in sidecar{{/if}})</h2>
|
|
4
4
|
{{#if overflowFailureCount}}
|
|
5
5
|
<div class="overflow-banner">
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</div>
|
|
10
10
|
{{/if}}
|
|
11
11
|
{{#each failures}}
|
|
12
|
-
<div class="failure-item no-break">
|
|
12
|
+
<div class="failure-item failure-item--{{severity}} no-break">
|
|
13
13
|
{{#if ../options.showFullFailures}}
|
|
14
14
|
|
|
15
15
|
<div class="failure-item__header">
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<td style="color:{{#if failed}}var(--fail){{else}}var(--pass){{/if}};font-weight:600;">{{failed}}</td>
|
|
24
24
|
<td>
|
|
25
25
|
<div class="coverage-bar">
|
|
26
|
-
<div class="coverage-bar__fill" style="width:{{coveragePct}}%;"></div>
|
|
26
|
+
<div class="coverage-bar__fill coverage-bar__fill--{{coverageBand coveragePct}}" style="width:{{coveragePct}}%;"></div>
|
|
27
27
|
</div>
|
|
28
28
|
<span style="font-size:9.5px;color:var(--text-3);">{{coveragePct}}%</span>
|
|
29
29
|
</td>
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
<tr class="no-break">
|
|
17
17
|
<td style="font-weight:500;">{{title}}</td>
|
|
18
18
|
<td style="font-size:9px;color:var(--text-3);font-family:'JetBrains Mono',monospace;">{{suiteTitle}}</td>
|
|
19
|
-
{{#if ../showProject}}<td style="font-size:9px;color:var(--text-3);">{{project}}</td>{{/if}}
|
|
19
|
+
{{#if ../showProject}}<td style="font-size:9px;color:var(--text-3);white-space:nowrap;">{{project}}</td>{{/if}}
|
|
20
20
|
<td style="font-family:'JetBrains Mono',monospace;font-size:9.5px;color:var(--text-2);text-align:right;">{{durationFormatted}}</td>
|
|
21
|
-
<td
|
|
21
|
+
<td>{{statusBadge status}}</td>
|
|
22
22
|
</tr>
|
|
23
23
|
{{/each}}
|
|
24
24
|
</tbody>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<span class="suite-stats">
|
|
16
16
|
<span class="badge badge--passed">{{stats.passed}}</span>
|
|
17
17
|
{{#if stats.failed}}<span class="badge badge--failed">{{stats.failed}}</span>{{/if}}
|
|
18
|
+
{{#if stats.timedOut}}<span class="badge badge--timedout">{{stats.timedOut}}</span>{{/if}}
|
|
18
19
|
{{#if stats.skipped}}<span class="badge badge--skipped">{{stats.skipped}}</span>{{/if}}
|
|
19
20
|
{{#if stats.flaky}}<span class="badge badge--flaky">{{stats.flaky}}</span>{{/if}}
|
|
20
21
|
</span>
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
</div>
|
|
42
43
|
{{/if}}
|
|
43
44
|
</td>
|
|
44
|
-
<td
|
|
45
|
+
<td>{{statusBadge status}}</td>
|
|
45
46
|
<td style="color:var(--text-2);font-family:'JetBrains Mono',monospace;font-size:9.5px;">{{formatDuration duration}}</td>
|
|
46
47
|
{{#if ../../options.showRetries}}<td style="color:var(--text-3);text-align:center;">{{retryCount}}</td>{{/if}}
|
|
47
48
|
</tr>
|
|
@@ -149,14 +149,19 @@ tr:last-child td { border-bottom: none; }
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
/* ── Page breaks ────────────────────────────────────────────────────────── */
|
|
152
|
-
.
|
|
153
|
-
.no-break { page-break-inside: avoid; }
|
|
154
|
-
.page-group { page-break-before: always; page-break-inside: avoid; break-inside: avoid; }
|
|
155
|
-
.page-group > .section { page-break-inside: avoid; break-inside: avoid; }
|
|
156
|
-
.page-group > .section:last-child { margin-bottom: 0; }
|
|
152
|
+
.no-break { page-break-inside: avoid; }
|
|
157
153
|
|
|
158
154
|
/* ── Section wrapper ────────────────────────────────────────────────────── */
|
|
159
|
-
|
|
155
|
+
/* Large sections (suite breakdown, failures, tables) MAY flow across pages —
|
|
156
|
+
their rows carry .no-break so they never split mid-row. Only the small atomic
|
|
157
|
+
blocks below stay glued together, and headings stick to the content beneath. */
|
|
158
|
+
.section { margin-bottom: 24px; }
|
|
159
|
+
.kpi-strip, .summary-row, .charts-grid, .release-gate, .flaky-callout,
|
|
160
|
+
.trend-card, .flakiness-card, .cover-page__stats-strip, .env-grid {
|
|
161
|
+
page-break-inside: avoid; break-inside: avoid;
|
|
162
|
+
}
|
|
163
|
+
h2, h3 { page-break-after: avoid; break-after: avoid; }
|
|
164
|
+
.suite-group__header { page-break-after: avoid; break-after: avoid; }
|
|
160
165
|
|
|
161
166
|
/* ── Code / stack trace ─────────────────────────────────────────────────── */
|
|
162
167
|
pre, code {
|
|
@@ -235,3 +240,236 @@ code {
|
|
|
235
240
|
|
|
236
241
|
/* ── Failure-analysis one-liner — shared by detailed + executive templates ── */
|
|
237
242
|
.analysis-oneliner { font-size: 0.9em; opacity: 0.9; margin-top: 4px; }
|
|
243
|
+
|
|
244
|
+
/* ── Release gate — ship/hold banner, shared by all templates ────────────── */
|
|
245
|
+
.release-gate {
|
|
246
|
+
display: flex; align-items: center; gap: 16px;
|
|
247
|
+
padding: 16px 20px; border-radius: 6px; margin-bottom: 22px;
|
|
248
|
+
border: 1px solid;
|
|
249
|
+
position: relative; overflow: hidden;
|
|
250
|
+
-webkit-print-color-adjust: exact;
|
|
251
|
+
print-color-adjust: exact;
|
|
252
|
+
}
|
|
253
|
+
.release-gate--pass {
|
|
254
|
+
background: var(--pass-dim); border-color: #C5DBC9;
|
|
255
|
+
}
|
|
256
|
+
.release-gate--fail {
|
|
257
|
+
background: var(--fail-dim); border-color: #E5C9BD;
|
|
258
|
+
}
|
|
259
|
+
.release-gate__icon {
|
|
260
|
+
font-size: 22px; font-weight: 700; flex-shrink: 0; width: 32px;
|
|
261
|
+
text-align: center; line-height: 1;
|
|
262
|
+
}
|
|
263
|
+
.release-gate--pass .release-gate__icon { color: var(--pass); }
|
|
264
|
+
.release-gate--fail .release-gate__icon { color: var(--fail); }
|
|
265
|
+
.release-gate__body { flex: 1; }
|
|
266
|
+
.release-gate__label {
|
|
267
|
+
font-family: 'Inter', system-ui, sans-serif; font-size: 8px;
|
|
268
|
+
font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase;
|
|
269
|
+
color: var(--text-3); margin-bottom: 4px;
|
|
270
|
+
}
|
|
271
|
+
.release-gate__verdict {
|
|
272
|
+
font-family: 'Source Serif Pro', Georgia, serif;
|
|
273
|
+
font-size: 18px; font-weight: 700; letter-spacing: -0.2px; line-height: 1.1;
|
|
274
|
+
}
|
|
275
|
+
.release-gate__verdict--pass { color: var(--pass); }
|
|
276
|
+
.release-gate__verdict--fail { color: var(--fail); }
|
|
277
|
+
.release-gate__detail {
|
|
278
|
+
font-family: 'Inter', system-ui, sans-serif; font-size: 10px;
|
|
279
|
+
color: var(--text-2); margin-top: 4px;
|
|
280
|
+
}
|
|
281
|
+
.release-gate__meta {
|
|
282
|
+
display: flex; flex-direction: column; gap: 3px; align-items: flex-end;
|
|
283
|
+
font-family: 'JetBrains Mono', monospace; font-size: 9px; color: var(--text-3);
|
|
284
|
+
flex-shrink: 0; text-align: right;
|
|
285
|
+
}
|
|
286
|
+
.release-gate__sep { color: var(--border-3); }
|
|
287
|
+
.release-gate__flaky-warn { color: var(--flaky); font-weight: 600; }
|
|
288
|
+
|
|
289
|
+
/* ── Failure severity — colour the card's left edge by triage rank ───────── */
|
|
290
|
+
/* Two-class specificity beats the per-template `.failure-item` border shorthand. */
|
|
291
|
+
.failure-item.failure-item--critical { border-left-color: var(--fail); }
|
|
292
|
+
.failure-item.failure-item--high { border-left-color: #A35E1E; }
|
|
293
|
+
.failure-item.failure-item--medium { border-left-color: var(--timeout); }
|
|
294
|
+
.failure-item.failure-item--low { border-left-color: var(--text-3); }
|
|
295
|
+
|
|
296
|
+
/* ── History trend card — shared by detailed + executive (charts.hbs) ─────── */
|
|
297
|
+
.trend-card {
|
|
298
|
+
background: var(--surface);
|
|
299
|
+
border: 1px solid var(--border-2);
|
|
300
|
+
border-radius: 6px;
|
|
301
|
+
padding: 16px 18px 14px;
|
|
302
|
+
margin-bottom: 24px;
|
|
303
|
+
page-break-inside: avoid;
|
|
304
|
+
}
|
|
305
|
+
.trend-card__header {
|
|
306
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
307
|
+
margin-bottom: 14px;
|
|
308
|
+
}
|
|
309
|
+
.trend-card__title {
|
|
310
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
311
|
+
font-size: 9px; font-weight: 700;
|
|
312
|
+
letter-spacing: 1.2px; text-transform: uppercase;
|
|
313
|
+
color: var(--text-3);
|
|
314
|
+
}
|
|
315
|
+
.delta { font-size: 11px; font-weight: 700; }
|
|
316
|
+
.delta-up { color: var(--pass); }
|
|
317
|
+
.delta-down { color: var(--fail); }
|
|
318
|
+
|
|
319
|
+
/* ── Run history table (generated by JS) ─────────────────────────────────── */
|
|
320
|
+
.trend-run-history {
|
|
321
|
+
margin-top: 10px;
|
|
322
|
+
border-top: 1px solid var(--border);
|
|
323
|
+
padding-top: 8px;
|
|
324
|
+
}
|
|
325
|
+
.rh-item {
|
|
326
|
+
display: flex; align-items: center; gap: 12px;
|
|
327
|
+
padding: 5px 8px; border-radius: 4px;
|
|
328
|
+
font-size: 9px; font-family: 'Inter', system-ui, sans-serif;
|
|
329
|
+
}
|
|
330
|
+
.rh-item:nth-child(odd) {
|
|
331
|
+
background: var(--surface-2);
|
|
332
|
+
-webkit-print-color-adjust: exact; print-color-adjust: exact;
|
|
333
|
+
}
|
|
334
|
+
.rh-dot { flex-shrink: 0; }
|
|
335
|
+
.rh-date { color: var(--text-2); flex: 1; }
|
|
336
|
+
.rh-rate { font-weight: 700; min-width: 34px; color: var(--text-1); }
|
|
337
|
+
.rh-rate--pass { color: var(--pass); }
|
|
338
|
+
.rh-rate--fail { color: var(--fail); }
|
|
339
|
+
.rh-rate--partial { color: var(--flaky); }
|
|
340
|
+
.rh-tests { color: var(--text-3); min-width: 58px; }
|
|
341
|
+
.rh-dur { color: var(--text-3); }
|
|
342
|
+
|
|
343
|
+
/* ── Verdict dots (used in run history rh-dot) ───────────────────────────── */
|
|
344
|
+
.verdict-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
345
|
+
.verdict-passed { background: var(--pass); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
346
|
+
.verdict-partial { background: var(--flaky); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
347
|
+
.verdict-failed { background: var(--fail); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
348
|
+
.verdict-unknown { background: var(--skip); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
349
|
+
|
|
350
|
+
/* ── Flakiness trend table (R8) ─────────────────────────────────────────── */
|
|
351
|
+
.flakiness-card {
|
|
352
|
+
margin: 24px 0 0;
|
|
353
|
+
border: 1px solid var(--border);
|
|
354
|
+
border-radius: 8px;
|
|
355
|
+
overflow: hidden;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.flakiness-card__header {
|
|
359
|
+
display: flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
padding: 10px 16px;
|
|
362
|
+
background: var(--surface-2);
|
|
363
|
+
border-bottom: 1px solid var(--border);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.flakiness-card__title {
|
|
367
|
+
font-size: 11px;
|
|
368
|
+
font-weight: 600;
|
|
369
|
+
text-transform: uppercase;
|
|
370
|
+
letter-spacing: 0.04em;
|
|
371
|
+
color: var(--text-3);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.flakiness-table {
|
|
375
|
+
width: 100%;
|
|
376
|
+
border-collapse: collapse;
|
|
377
|
+
font-size: 11px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.flakiness-table thead th {
|
|
381
|
+
padding: 8px 12px;
|
|
382
|
+
text-align: left;
|
|
383
|
+
font-weight: 600;
|
|
384
|
+
font-size: 10px;
|
|
385
|
+
text-transform: uppercase;
|
|
386
|
+
letter-spacing: 0.04em;
|
|
387
|
+
color: var(--text-3);
|
|
388
|
+
border-bottom: 1px solid var(--border);
|
|
389
|
+
background: var(--surface-2);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.flakiness-table tbody tr {
|
|
393
|
+
border-bottom: 1px solid var(--border);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.flakiness-table tbody tr:last-child {
|
|
397
|
+
border-bottom: none;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.flakiness-table tbody td {
|
|
401
|
+
padding: 8px 12px;
|
|
402
|
+
vertical-align: middle;
|
|
403
|
+
color: var(--text-1);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.flakiness-table__test {
|
|
407
|
+
max-width: 300px;
|
|
408
|
+
overflow: hidden;
|
|
409
|
+
text-overflow: ellipsis;
|
|
410
|
+
white-space: nowrap;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.flakiness-table__rate {
|
|
414
|
+
width: 72px;
|
|
415
|
+
font-weight: 600;
|
|
416
|
+
color: var(--flaky);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.flakiness-table__count {
|
|
420
|
+
width: 72px;
|
|
421
|
+
color: var(--text-3);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.flakiness-table__spark {
|
|
425
|
+
width: 140px;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.flaky-spark {
|
|
429
|
+
display: flex;
|
|
430
|
+
gap: 3px;
|
|
431
|
+
align-items: center;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.flaky-dot {
|
|
435
|
+
flex-shrink: 0;
|
|
436
|
+
width: 7px;
|
|
437
|
+
height: 7px;
|
|
438
|
+
border-radius: 50%;
|
|
439
|
+
background: var(--border, #e5e3d8);
|
|
440
|
+
print-color-adjust: exact;
|
|
441
|
+
-webkit-print-color-adjust: exact;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.flaky-dot--hit {
|
|
445
|
+
background: #f59e0b;
|
|
446
|
+
print-color-adjust: exact;
|
|
447
|
+
-webkit-print-color-adjust: exact;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* ── Environment grid — compact ci-environment, shared by minimal + executive ─ */
|
|
451
|
+
.env-grid {
|
|
452
|
+
display: grid; grid-template-columns: 1fr 1fr;
|
|
453
|
+
gap: 0;
|
|
454
|
+
background: var(--surface);
|
|
455
|
+
border: 1px solid var(--border-2);
|
|
456
|
+
border-radius: 6px;
|
|
457
|
+
overflow: hidden;
|
|
458
|
+
}
|
|
459
|
+
.env-item {
|
|
460
|
+
display: flex; gap: 12px; font-size: 10.5px; align-items: baseline;
|
|
461
|
+
padding: 10px 18px;
|
|
462
|
+
border-bottom: 1px solid var(--border);
|
|
463
|
+
border-right: 1px solid var(--border);
|
|
464
|
+
}
|
|
465
|
+
.env-item:nth-child(even) { border-right: none; }
|
|
466
|
+
.env-item:nth-last-child(-n+2) { border-bottom: none; }
|
|
467
|
+
.env-item__key {
|
|
468
|
+
font-family: 'Inter', system-ui, sans-serif; font-weight: 600;
|
|
469
|
+
font-size: 8.5px; letter-spacing: 1px; text-transform: uppercase;
|
|
470
|
+
color: var(--text-2); min-width: 80px;
|
|
471
|
+
}
|
|
472
|
+
.env-item__val {
|
|
473
|
+
font-family: 'JetBrains Mono', monospace;
|
|
474
|
+
font-size: 9.5px; color: var(--text-1);
|
|
475
|
+
}
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
/* ── KPI strip ───────────────────────────────────────────────────────────── */
|
|
59
59
|
.kpi-strip {
|
|
60
|
-
display: grid; grid-template-columns: repeat(
|
|
60
|
+
display: grid; grid-template-columns: repeat(6, 1fr);
|
|
61
61
|
gap: 10px; margin-bottom: 22px;
|
|
62
62
|
}
|
|
63
63
|
.kpi-card {
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
.kpi-card--total::before { background: var(--accent); }
|
|
75
75
|
.kpi-card--pass::before { background: var(--pass); }
|
|
76
76
|
.kpi-card--fail::before { background: var(--fail); }
|
|
77
|
+
.kpi-card--timedout::before { background: var(--timeout); }
|
|
77
78
|
.kpi-card--skip::before { background: var(--skip); }
|
|
78
79
|
.kpi-card--flaky::before { background: var(--flaky); }
|
|
79
80
|
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
.kpi-card--total .kpi-card__value { color: var(--info); }
|
|
88
89
|
.kpi-card--pass .kpi-card__value { color: var(--pass); }
|
|
89
90
|
.kpi-card--fail .kpi-card__value { color: var(--fail); }
|
|
91
|
+
.kpi-card--timedout .kpi-card__value { color: var(--timeout); }
|
|
90
92
|
.kpi-card--skip .kpi-card__value { color: var(--skip); }
|
|
91
93
|
.kpi-card--flaky .kpi-card__value { color: var(--flaky); }
|
|
92
94
|
|
|
@@ -230,10 +232,12 @@
|
|
|
230
232
|
}
|
|
231
233
|
.coverage-bar__fill {
|
|
232
234
|
height: 100%; border-radius: 3px;
|
|
233
|
-
background: var(--pass);
|
|
234
235
|
-webkit-print-color-adjust: exact;
|
|
235
236
|
print-color-adjust: exact;
|
|
236
237
|
}
|
|
238
|
+
.coverage-bar__fill--low { background: var(--fail); }
|
|
239
|
+
.coverage-bar__fill--mid { background: var(--timeout); }
|
|
240
|
+
.coverage-bar__fill--high { background: var(--pass); }
|
|
237
241
|
|
|
238
242
|
/* ── Environment table ───────────────────────────────────────────────────── */
|
|
239
243
|
.env-table {
|
|
@@ -254,160 +258,6 @@
|
|
|
254
258
|
font-size: 9.5px; color: var(--text-1);
|
|
255
259
|
}
|
|
256
260
|
|
|
257
|
-
/* ── History trend card ──────────────────────────────────────────────────── */
|
|
258
|
-
.trend-card {
|
|
259
|
-
background: var(--surface);
|
|
260
|
-
border: 1px solid var(--border-2);
|
|
261
|
-
border-radius: 6px;
|
|
262
|
-
padding: 16px 18px 14px;
|
|
263
|
-
margin-bottom: 24px;
|
|
264
|
-
page-break-inside: avoid;
|
|
265
|
-
}
|
|
266
|
-
.trend-card__header {
|
|
267
|
-
display: flex; align-items: center; justify-content: space-between;
|
|
268
|
-
margin-bottom: 14px;
|
|
269
|
-
}
|
|
270
|
-
.trend-card__title {
|
|
271
|
-
font-family: 'Inter', system-ui, sans-serif;
|
|
272
|
-
font-size: 9px; font-weight: 700;
|
|
273
|
-
letter-spacing: 1.2px; text-transform: uppercase;
|
|
274
|
-
color: var(--text-3);
|
|
275
|
-
}
|
|
276
|
-
.delta { font-size: 11px; font-weight: 700; }
|
|
277
|
-
.delta-up { color: var(--pass); }
|
|
278
|
-
.delta-down { color: var(--fail); }
|
|
279
|
-
|
|
280
|
-
/* ── Run history table (generated by JS) ─────────────────────────────────── */
|
|
281
|
-
.trend-run-history {
|
|
282
|
-
margin-top: 10px;
|
|
283
|
-
border-top: 1px solid var(--border);
|
|
284
|
-
padding-top: 8px;
|
|
285
|
-
}
|
|
286
|
-
.rh-item {
|
|
287
|
-
display: flex; align-items: center; gap: 12px;
|
|
288
|
-
padding: 5px 8px; border-radius: 4px;
|
|
289
|
-
font-size: 9px; font-family: 'Inter', system-ui, sans-serif;
|
|
290
|
-
}
|
|
291
|
-
.rh-item:nth-child(odd) {
|
|
292
|
-
background: var(--surface-2);
|
|
293
|
-
-webkit-print-color-adjust: exact; print-color-adjust: exact;
|
|
294
|
-
}
|
|
295
|
-
.rh-dot { flex-shrink: 0; }
|
|
296
|
-
.rh-date { color: var(--text-2); flex: 1; }
|
|
297
|
-
.rh-rate { font-weight: 700; min-width: 34px; color: var(--text-1); }
|
|
298
|
-
.rh-rate--pass { color: var(--pass); }
|
|
299
|
-
.rh-rate--fail { color: var(--fail); }
|
|
300
|
-
.rh-rate--partial { color: var(--flaky); }
|
|
301
|
-
.rh-tests { color: var(--text-3); min-width: 58px; }
|
|
302
|
-
.rh-dur { color: var(--text-3); }
|
|
303
|
-
|
|
304
|
-
/* ── Verdict dots (used in run history rh-dot) ───────────────────────────── */
|
|
305
|
-
.verdict-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
306
|
-
.verdict-passed { background: var(--pass); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
307
|
-
.verdict-partial { background: var(--flaky); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
308
|
-
.verdict-failed { background: var(--fail); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
309
|
-
.verdict-unknown { background: var(--skip); -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
310
|
-
|
|
311
|
-
/* ── Flakiness trend table (R8) ─────────────────────────────────────────── */
|
|
312
|
-
.flakiness-card {
|
|
313
|
-
margin: 24px 0 0;
|
|
314
|
-
border: 1px solid var(--border);
|
|
315
|
-
border-radius: 8px;
|
|
316
|
-
overflow: hidden;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.flakiness-card__header {
|
|
320
|
-
display: flex;
|
|
321
|
-
align-items: center;
|
|
322
|
-
padding: 10px 16px;
|
|
323
|
-
background: var(--surface-2);
|
|
324
|
-
border-bottom: 1px solid var(--border);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
.flakiness-card__title {
|
|
328
|
-
font-size: 11px;
|
|
329
|
-
font-weight: 600;
|
|
330
|
-
text-transform: uppercase;
|
|
331
|
-
letter-spacing: 0.04em;
|
|
332
|
-
color: var(--text-3);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
.flakiness-table {
|
|
336
|
-
width: 100%;
|
|
337
|
-
border-collapse: collapse;
|
|
338
|
-
font-size: 11px;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.flakiness-table thead th {
|
|
342
|
-
padding: 8px 12px;
|
|
343
|
-
text-align: left;
|
|
344
|
-
font-weight: 600;
|
|
345
|
-
font-size: 10px;
|
|
346
|
-
text-transform: uppercase;
|
|
347
|
-
letter-spacing: 0.04em;
|
|
348
|
-
color: var(--text-3);
|
|
349
|
-
border-bottom: 1px solid var(--border);
|
|
350
|
-
background: var(--surface-2);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.flakiness-table tbody tr {
|
|
354
|
-
border-bottom: 1px solid var(--border);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
.flakiness-table tbody tr:last-child {
|
|
358
|
-
border-bottom: none;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.flakiness-table tbody td {
|
|
362
|
-
padding: 8px 12px;
|
|
363
|
-
vertical-align: middle;
|
|
364
|
-
color: var(--text-1);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.flakiness-table__test {
|
|
368
|
-
max-width: 300px;
|
|
369
|
-
overflow: hidden;
|
|
370
|
-
text-overflow: ellipsis;
|
|
371
|
-
white-space: nowrap;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
.flakiness-table__rate {
|
|
375
|
-
width: 72px;
|
|
376
|
-
font-weight: 600;
|
|
377
|
-
color: var(--flaky);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.flakiness-table__count {
|
|
381
|
-
width: 72px;
|
|
382
|
-
color: var(--text-3);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
.flakiness-table__spark {
|
|
386
|
-
width: 140px;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.flaky-spark {
|
|
390
|
-
display: flex;
|
|
391
|
-
gap: 3px;
|
|
392
|
-
align-items: center;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
.flaky-dot {
|
|
396
|
-
flex-shrink: 0;
|
|
397
|
-
width: 7px;
|
|
398
|
-
height: 7px;
|
|
399
|
-
border-radius: 50%;
|
|
400
|
-
background: var(--border, #e5e3d8);
|
|
401
|
-
print-color-adjust: exact;
|
|
402
|
-
-webkit-print-color-adjust: exact;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
.flaky-dot--hit {
|
|
406
|
-
background: #f59e0b;
|
|
407
|
-
print-color-adjust: exact;
|
|
408
|
-
-webkit-print-color-adjust: exact;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
261
|
/* ── Failure Analysis ───────────────────────────────────────────────────── */
|
|
412
262
|
/* NOTE: .analysis-oneliner lives in base.css — the executive template renders it too. */
|
|
413
263
|
.failure-analysis { margin-top: 24px; }
|
|
@@ -428,6 +278,3 @@
|
|
|
428
278
|
.cluster--env-config { border-color: oklch(55% 0.15 145); }
|
|
429
279
|
.cluster--unknown { border-color: oklch(60% 0.00 0); }
|
|
430
280
|
.confidence-warning { font-size: 0.8em; opacity: 0.75; }
|
|
431
|
-
|
|
432
|
-
</content>
|
|
433
|
-
</invoke>
|