@test-station/render-html 0.2.15 → 0.2.16
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/package.json +1 -1
- package/src/index.js +120 -6
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -630,6 +630,15 @@ export function renderHtmlReport(report, options = {}) {
|
|
|
630
630
|
border-collapse: collapse;
|
|
631
631
|
margin-top: 12px;
|
|
632
632
|
font-size: 0.84rem;
|
|
633
|
+
table-layout: fixed;
|
|
634
|
+
}
|
|
635
|
+
.coverage-table col.coverage-table__fileCol { width: auto; }
|
|
636
|
+
.coverage-table col.coverage-table__metricCol { width: 170px; }
|
|
637
|
+
.coverage-table col.coverage-table__statementCol { width: 110px; }
|
|
638
|
+
.coverage-table col.coverage-table__attributionCol { width: 220px; }
|
|
639
|
+
.coverage-table td:first-child,
|
|
640
|
+
.coverage-table th:first-child {
|
|
641
|
+
width: auto;
|
|
633
642
|
}
|
|
634
643
|
.coverage-table th,
|
|
635
644
|
.coverage-table td {
|
|
@@ -644,7 +653,64 @@ export function renderHtmlReport(report, options = {}) {
|
|
|
644
653
|
text-transform: uppercase;
|
|
645
654
|
letter-spacing: 0.08em;
|
|
646
655
|
}
|
|
647
|
-
.coverage-table code {
|
|
656
|
+
.coverage-table code {
|
|
657
|
+
display: block;
|
|
658
|
+
font-size: 0.8rem;
|
|
659
|
+
color: #d7e5ff;
|
|
660
|
+
white-space: pre-wrap;
|
|
661
|
+
word-break: break-word;
|
|
662
|
+
}
|
|
663
|
+
.coverage-table__metric {
|
|
664
|
+
display: grid;
|
|
665
|
+
gap: 8px;
|
|
666
|
+
min-width: 0;
|
|
667
|
+
}
|
|
668
|
+
.coverage-table__metricValue {
|
|
669
|
+
font-size: 0.92rem;
|
|
670
|
+
font-weight: 700;
|
|
671
|
+
letter-spacing: -0.02em;
|
|
672
|
+
font-variant-numeric: tabular-nums;
|
|
673
|
+
}
|
|
674
|
+
.coverage-table__metricBar {
|
|
675
|
+
height: 10px;
|
|
676
|
+
border-radius: 999px;
|
|
677
|
+
overflow: hidden;
|
|
678
|
+
background: rgba(255, 111, 143, 0.14);
|
|
679
|
+
border: 1px solid rgba(124, 160, 224, 0.12);
|
|
680
|
+
}
|
|
681
|
+
.coverage-table__metricFill {
|
|
682
|
+
height: 100%;
|
|
683
|
+
border-radius: inherit;
|
|
684
|
+
transition: width 180ms ease-out;
|
|
685
|
+
}
|
|
686
|
+
.coverage-table__statementCell {
|
|
687
|
+
text-align: center;
|
|
688
|
+
vertical-align: middle;
|
|
689
|
+
}
|
|
690
|
+
.coverage-table__statementIcon {
|
|
691
|
+
display: inline-flex;
|
|
692
|
+
align-items: center;
|
|
693
|
+
justify-content: center;
|
|
694
|
+
width: 32px;
|
|
695
|
+
height: 32px;
|
|
696
|
+
border-radius: 999px;
|
|
697
|
+
border: 1px solid rgba(124, 160, 224, 0.16);
|
|
698
|
+
background: rgba(11, 20, 36, 0.78);
|
|
699
|
+
color: var(--muted);
|
|
700
|
+
font-size: 0.78rem;
|
|
701
|
+
font-weight: 700;
|
|
702
|
+
letter-spacing: 0.04em;
|
|
703
|
+
cursor: help;
|
|
704
|
+
user-select: none;
|
|
705
|
+
}
|
|
706
|
+
.coverage-table__statementIcon--active {
|
|
707
|
+
color: var(--text);
|
|
708
|
+
border-color: color-mix(in srgb, var(--accent) 30%, transparent);
|
|
709
|
+
background: color-mix(in srgb, var(--accent) 12%, rgba(11, 20, 36, 0.82));
|
|
710
|
+
}
|
|
711
|
+
.coverage-table__statementIcon--disabled {
|
|
712
|
+
opacity: 0.45;
|
|
713
|
+
}
|
|
648
714
|
.policy-block {
|
|
649
715
|
margin: 0 0 16px;
|
|
650
716
|
padding: 14px;
|
|
@@ -1402,10 +1468,10 @@ function renderCoverageBlock(coverage, rootDir) {
|
|
|
1402
1468
|
return `
|
|
1403
1469
|
<tr>
|
|
1404
1470
|
<td><code>${escapeHtml(displayPath)}</code></td>
|
|
1405
|
-
<td>${
|
|
1406
|
-
<td>${
|
|
1407
|
-
<td>${
|
|
1408
|
-
<td>${
|
|
1471
|
+
<td>${renderCoverageTableMetric(file.lines, 'Lines')}</td>
|
|
1472
|
+
<td>${renderCoverageTableMetric(file.branches, 'Branches')}</td>
|
|
1473
|
+
<td>${renderCoverageTableMetric(file.functions, 'Functions')}</td>
|
|
1474
|
+
<td class="coverage-table__statementCell">${renderStatementsIcon(file.statements)}</td>
|
|
1409
1475
|
${showAttribution ? `<td>${escapeHtml(formatCoverageAttribution(file))}</td>` : ''}
|
|
1410
1476
|
</tr>`;
|
|
1411
1477
|
})
|
|
@@ -1421,6 +1487,14 @@ function renderCoverageBlock(coverage, rootDir) {
|
|
|
1421
1487
|
<details>
|
|
1422
1488
|
<summary>Coverage by file (${files.length} files, lowest line coverage first)</summary>
|
|
1423
1489
|
<table class="coverage-table">
|
|
1490
|
+
<colgroup>
|
|
1491
|
+
<col class="coverage-table__fileCol" />
|
|
1492
|
+
<col class="coverage-table__metricCol" />
|
|
1493
|
+
<col class="coverage-table__metricCol" />
|
|
1494
|
+
<col class="coverage-table__metricCol" />
|
|
1495
|
+
<col class="coverage-table__statementCol" />
|
|
1496
|
+
${showAttribution ? '<col class="coverage-table__attributionCol" />' : ''}
|
|
1497
|
+
</colgroup>
|
|
1424
1498
|
<thead>
|
|
1425
1499
|
<tr>
|
|
1426
1500
|
<th>File</th>
|
|
@@ -1439,7 +1513,7 @@ function renderCoverageBlock(coverage, rootDir) {
|
|
|
1439
1513
|
}
|
|
1440
1514
|
|
|
1441
1515
|
function renderCoverageMetric(label, metric) {
|
|
1442
|
-
const pct = metric ? metric.pct
|
|
1516
|
+
const pct = metric ? formatCoveragePercent(metric.pct) : '0.0';
|
|
1443
1517
|
const counts = metric ? `${formatCoverageCount(metric.covered)}/${formatCoverageCount(metric.total)}` : 'n/a';
|
|
1444
1518
|
const fillStyle = metric
|
|
1445
1519
|
? `width:${metric.pct.toFixed(2)}%;background:hsl(${coverageHue(metric.pct)} 68% 48%);`
|
|
@@ -1458,6 +1532,42 @@ function renderCoverageMetric(label, metric) {
|
|
|
1458
1532
|
`;
|
|
1459
1533
|
}
|
|
1460
1534
|
|
|
1535
|
+
function renderCoverageTableMetric(metric, label) {
|
|
1536
|
+
if (!hasCoverageMetric(metric)) {
|
|
1537
|
+
return `<div class="coverage-table__metric" title="${escapeHtml(`No ${label.toLowerCase()} coverage recorded`)}"><strong class="coverage-table__metricValue">n/a</strong><div class="coverage-table__metricBar" aria-hidden="true"><div class="coverage-table__metricFill" style="width:0%;background:hsl(0 68% 48%);"></div></div></div>`;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const fillStyle = `width:${metric.pct.toFixed(2)}%;background:hsl(${coverageHue(metric.pct)} 68% 48%);`;
|
|
1541
|
+
return `
|
|
1542
|
+
<div class="coverage-table__metric" title="${escapeHtml(formatCoverageMetricTooltip(label, metric))}">
|
|
1543
|
+
<strong class="coverage-table__metricValue">${escapeHtml(formatCoveragePercent(metric.pct))}%</strong>
|
|
1544
|
+
<div class="coverage-table__metricBar" aria-hidden="true">
|
|
1545
|
+
<div class="coverage-table__metricFill" style="${fillStyle}"></div>
|
|
1546
|
+
</div>
|
|
1547
|
+
</div>
|
|
1548
|
+
`;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
function renderStatementsIcon(metric) {
|
|
1552
|
+
const active = hasCoverageMetric(metric);
|
|
1553
|
+
const title = active
|
|
1554
|
+
? formatCoverageMetricTooltip('Statements', metric)
|
|
1555
|
+
: 'No statement coverage recorded';
|
|
1556
|
+
return `<span class="coverage-table__statementIcon coverage-table__statementIcon--${active ? 'active' : 'disabled'}" title="${escapeHtml(title)}" aria-label="${escapeHtml(title)}">ST</span>`;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
function hasCoverageMetric(metric) {
|
|
1560
|
+
return Boolean(metric) && (
|
|
1561
|
+
Number.isFinite(metric.pct)
|
|
1562
|
+
|| Number.isFinite(metric.total) && metric.total > 0
|
|
1563
|
+
|| Number.isFinite(metric.covered) && metric.covered > 0
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
function formatCoverageMetricTooltip(label, metric) {
|
|
1568
|
+
return `${label}: ${formatCoveragePercent(metric?.pct)}% (${formatCoverageCount(metric?.covered)}/${formatCoverageCount(metric?.total)})`;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1461
1571
|
function formatCoverageAttribution(file) {
|
|
1462
1572
|
const parts = [];
|
|
1463
1573
|
if (file.module) {
|
|
@@ -1509,6 +1619,10 @@ function coverageHue(pct) {
|
|
|
1509
1619
|
return `${Math.round((normalized / 100) * 120)}deg`;
|
|
1510
1620
|
}
|
|
1511
1621
|
|
|
1622
|
+
function formatCoveragePercent(value) {
|
|
1623
|
+
return Number.isFinite(value) ? Number(value).toFixed(1) : '0.0';
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1512
1626
|
function formatCoverageCount(value) {
|
|
1513
1627
|
if (!Number.isFinite(value)) {
|
|
1514
1628
|
return '0';
|