executable-stories-formatters 0.1.0 → 0.3.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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/cli.js +347 -28
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +377 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +374 -28
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/schemas/README.md +7 -12
package/dist/index.js
CHANGED
|
@@ -693,50 +693,162 @@ function initTheme() {
|
|
|
693
693
|
}
|
|
694
694
|
`;
|
|
695
695
|
var JS_CORE = `
|
|
696
|
+
// Filter state
|
|
697
|
+
var activeTags = new Set();
|
|
698
|
+
var activeStatus = null;
|
|
699
|
+
|
|
696
700
|
// Search functionality
|
|
697
701
|
function initSearch() {
|
|
698
|
-
|
|
702
|
+
var input = document.querySelector('.search-input');
|
|
699
703
|
if (!input) return;
|
|
700
704
|
|
|
701
|
-
|
|
702
|
-
input.addEventListener('input', (
|
|
705
|
+
var debounceTimer;
|
|
706
|
+
input.addEventListener('input', function() {
|
|
703
707
|
clearTimeout(debounceTimer);
|
|
704
|
-
debounceTimer = setTimeout(()
|
|
705
|
-
|
|
708
|
+
debounceTimer = setTimeout(function() {
|
|
709
|
+
applyAllFilters();
|
|
706
710
|
}, 150);
|
|
707
711
|
});
|
|
708
712
|
|
|
709
713
|
// Clear search on Escape
|
|
710
|
-
input.addEventListener('keydown', (e)
|
|
714
|
+
input.addEventListener('keydown', function(e) {
|
|
711
715
|
if (e.key === 'Escape') {
|
|
712
716
|
e.target.value = '';
|
|
713
|
-
|
|
717
|
+
applyAllFilters();
|
|
714
718
|
}
|
|
715
719
|
});
|
|
716
720
|
}
|
|
717
721
|
|
|
718
|
-
|
|
719
|
-
|
|
722
|
+
// Tag filter
|
|
723
|
+
function initTagFilter() {
|
|
724
|
+
document.querySelectorAll('.tag-pill').forEach(function(pill) {
|
|
725
|
+
pill.addEventListener('click', function() {
|
|
726
|
+
var tag = pill.dataset.tag;
|
|
727
|
+
if (activeTags.has(tag)) {
|
|
728
|
+
activeTags.delete(tag);
|
|
729
|
+
pill.classList.remove('active');
|
|
730
|
+
} else {
|
|
731
|
+
activeTags.add(tag);
|
|
732
|
+
pill.classList.add('active');
|
|
733
|
+
}
|
|
734
|
+
updateClearButton();
|
|
735
|
+
applyAllFilters();
|
|
736
|
+
});
|
|
737
|
+
});
|
|
720
738
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
739
|
+
var clearBtn = document.querySelector('.tag-bar-clear');
|
|
740
|
+
if (clearBtn) {
|
|
741
|
+
clearBtn.addEventListener('click', function() {
|
|
742
|
+
activeTags.clear();
|
|
743
|
+
document.querySelectorAll('.tag-pill.active').forEach(function(p) { p.classList.remove('active'); });
|
|
744
|
+
updateClearButton();
|
|
745
|
+
applyAllFilters();
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
724
749
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
750
|
+
function updateClearButton() {
|
|
751
|
+
var clearBtn = document.querySelector('.tag-bar-clear');
|
|
752
|
+
if (clearBtn) {
|
|
753
|
+
clearBtn.style.display = activeTags.size > 0 ? '' : 'none';
|
|
754
|
+
}
|
|
755
|
+
}
|
|
729
756
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
757
|
+
// Status filter (clickable summary cards)
|
|
758
|
+
function initStatusFilter() {
|
|
759
|
+
document.querySelectorAll('.summary-card').forEach(function(card) {
|
|
760
|
+
card.style.cursor = 'pointer';
|
|
761
|
+
if (!card.classList.contains('passed') && !card.classList.contains('failed') && !card.classList.contains('skipped')) {
|
|
762
|
+
card.addEventListener('click', function() {
|
|
763
|
+
activeStatus = null;
|
|
764
|
+
document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });
|
|
765
|
+
applyAllFilters();
|
|
766
|
+
});
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
card.addEventListener('click', function() {
|
|
770
|
+
var status = card.classList.contains('passed') ? 'passed' :
|
|
771
|
+
card.classList.contains('failed') ? 'failed' : 'skipped';
|
|
772
|
+
if (activeStatus === status) {
|
|
773
|
+
activeStatus = null;
|
|
774
|
+
card.classList.remove('status-active');
|
|
775
|
+
} else {
|
|
776
|
+
activeStatus = status;
|
|
777
|
+
document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });
|
|
778
|
+
card.classList.add('status-active');
|
|
779
|
+
}
|
|
780
|
+
applyAllFilters();
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
}
|
|
734
784
|
|
|
735
|
-
|
|
736
|
-
|
|
785
|
+
// Unified filter: composes search + tags + status
|
|
786
|
+
function applyAllFilters() {
|
|
787
|
+
var searchInput = document.querySelector('.search-input');
|
|
788
|
+
var searchQuery = searchInput ? searchInput.value.toLowerCase().trim() : '';
|
|
789
|
+
var features = document.querySelectorAll('.feature');
|
|
790
|
+
var visibleCount = 0;
|
|
791
|
+
var totalCount = 0;
|
|
792
|
+
|
|
793
|
+
features.forEach(function(feature) {
|
|
794
|
+
var scenarios = feature.querySelectorAll('.scenario');
|
|
795
|
+
var featureVisible = 0;
|
|
796
|
+
|
|
797
|
+
scenarios.forEach(function(scenario) {
|
|
798
|
+
totalCount++;
|
|
799
|
+
var title = (scenario.querySelector('.scenario-title') || {}).textContent || '';
|
|
800
|
+
title = title.toLowerCase();
|
|
801
|
+
var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.toLowerCase(); });
|
|
802
|
+
var steps = Array.from(scenario.querySelectorAll('.step-text')).map(function(s) { return s.textContent.toLowerCase(); });
|
|
803
|
+
var statusEl = scenario.querySelector('.status-icon');
|
|
804
|
+
var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
|
|
805
|
+
statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
|
|
806
|
+
statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
|
|
807
|
+
|
|
808
|
+
var matchesSearch = !searchQuery ||
|
|
809
|
+
title.includes(searchQuery) ||
|
|
810
|
+
tags.some(function(t) { return t.includes(searchQuery); }) ||
|
|
811
|
+
steps.some(function(s) { return s.includes(searchQuery); });
|
|
812
|
+
|
|
813
|
+
var matchesTags = activeTags.size === 0 ||
|
|
814
|
+
tags.some(function(t) { return activeTags.has(t); });
|
|
815
|
+
|
|
816
|
+
var matchesStatus = !activeStatus ||
|
|
817
|
+
status === activeStatus ||
|
|
818
|
+
(activeStatus === 'skipped' && status === 'pending');
|
|
819
|
+
|
|
820
|
+
var visible = matchesSearch && matchesTags && matchesStatus;
|
|
821
|
+
scenario.style.display = visible ? '' : 'none';
|
|
822
|
+
if (visible) { visibleCount++; featureVisible++; }
|
|
737
823
|
});
|
|
738
824
|
|
|
739
|
-
feature.style.display =
|
|
825
|
+
feature.style.display = featureVisible > 0 ? '' : 'none';
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
updateFilterResults(visibleCount, totalCount);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function updateFilterResults(visible, total) {
|
|
832
|
+
var el = document.querySelector('.filter-results');
|
|
833
|
+
if (!el) return;
|
|
834
|
+
var searchInput = document.querySelector('.search-input');
|
|
835
|
+
var isFiltering = activeTags.size > 0 || activeStatus ||
|
|
836
|
+
(searchInput && searchInput.value.trim().length > 0);
|
|
837
|
+
el.style.display = isFiltering ? '' : 'none';
|
|
838
|
+
var vc = el.querySelector('.visible-count');
|
|
839
|
+
var tc = el.querySelector('.total-count');
|
|
840
|
+
if (vc) vc.textContent = visible;
|
|
841
|
+
if (tc) tc.textContent = total;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Keyboard shortcuts
|
|
845
|
+
function initKeyboardShortcuts() {
|
|
846
|
+
document.addEventListener('keydown', function(e) {
|
|
847
|
+
if (e.key === '/' && !e.ctrlKey && !e.metaKey && e.target.tagName !== 'INPUT') {
|
|
848
|
+
e.preventDefault();
|
|
849
|
+
var input = document.querySelector('.search-input');
|
|
850
|
+
if (input) input.focus();
|
|
851
|
+
}
|
|
740
852
|
});
|
|
741
853
|
}
|
|
742
854
|
|
|
@@ -771,6 +883,18 @@ function initCollapse() {
|
|
|
771
883
|
}
|
|
772
884
|
});
|
|
773
885
|
});
|
|
886
|
+
|
|
887
|
+
document.querySelectorAll('.trace-view-header').forEach(header => {
|
|
888
|
+
header.addEventListener('click', () => {
|
|
889
|
+
toggleCollapse(header, header.closest('.trace-view'));
|
|
890
|
+
});
|
|
891
|
+
header.addEventListener('keydown', (e) => {
|
|
892
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
893
|
+
e.preventDefault();
|
|
894
|
+
toggleCollapse(header, header.closest('.trace-view'));
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
});
|
|
774
898
|
}
|
|
775
899
|
|
|
776
900
|
function expandAll() {
|
|
@@ -821,6 +945,9 @@ function generateScript(options) {
|
|
|
821
945
|
initCalls.push("initTheme();");
|
|
822
946
|
}
|
|
823
947
|
initCalls.push("initSearch();");
|
|
948
|
+
initCalls.push("initTagFilter();");
|
|
949
|
+
initCalls.push("initStatusFilter();");
|
|
950
|
+
initCalls.push("initKeyboardShortcuts();");
|
|
824
951
|
initCalls.push("initCollapse();");
|
|
825
952
|
const initScript = `
|
|
826
953
|
// Initialize on load
|
|
@@ -971,6 +1098,7 @@ var CSS_STYLES = `
|
|
|
971
1098
|
--tag-bg: hsl(145 55% 95%);
|
|
972
1099
|
--tag-color: hsl(145 63% 30%);
|
|
973
1100
|
--tag-border: hsl(145 55% 85%);
|
|
1101
|
+
--step-param-color: hsl(220 70% 50%);
|
|
974
1102
|
|
|
975
1103
|
/* Accordion/Collapsible styling */
|
|
976
1104
|
--accordion-header-hover: hsl(0 0% 98%);
|
|
@@ -1029,6 +1157,7 @@ var CSS_STYLES = `
|
|
|
1029
1157
|
--tag-bg: hsl(145 35% 14%);
|
|
1030
1158
|
--tag-color: hsl(145 63% 60%);
|
|
1031
1159
|
--tag-border: hsl(145 35% 22%);
|
|
1160
|
+
--step-param-color: hsl(220 70% 70%);
|
|
1032
1161
|
|
|
1033
1162
|
/* Accordion/Collapsible styling */
|
|
1034
1163
|
--accordion-header-hover: hsl(0 0% 11%);
|
|
@@ -1077,6 +1206,7 @@ var CSS_STYLES = `
|
|
|
1077
1206
|
--tag-bg: hsl(145 35% 14%);
|
|
1078
1207
|
--tag-color: hsl(145 63% 60%);
|
|
1079
1208
|
--tag-border: hsl(145 35% 22%);
|
|
1209
|
+
--step-param-color: hsl(220 70% 70%);
|
|
1080
1210
|
--accordion-header-hover: hsl(0 0% 11%);
|
|
1081
1211
|
--accordion-content-bg: hsl(0 0% 7%);
|
|
1082
1212
|
}
|
|
@@ -1304,6 +1434,98 @@ body {
|
|
|
1304
1434
|
}
|
|
1305
1435
|
.summary-card.pending .value { color: var(--pending); }
|
|
1306
1436
|
|
|
1437
|
+
/* ============================================================================
|
|
1438
|
+
Tag Filter Bar
|
|
1439
|
+
============================================================================ */
|
|
1440
|
+
.tag-bar {
|
|
1441
|
+
margin-bottom: 1rem;
|
|
1442
|
+
padding: 0.75rem 1rem;
|
|
1443
|
+
background: var(--card);
|
|
1444
|
+
border: 1px solid var(--border);
|
|
1445
|
+
border-radius: var(--radius);
|
|
1446
|
+
position: sticky;
|
|
1447
|
+
top: 0;
|
|
1448
|
+
z-index: 10;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
.tag-bar-header {
|
|
1452
|
+
display: flex;
|
|
1453
|
+
justify-content: space-between;
|
|
1454
|
+
align-items: center;
|
|
1455
|
+
margin-bottom: 0.5rem;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
.tag-bar-label {
|
|
1459
|
+
font-size: 0.6875rem;
|
|
1460
|
+
text-transform: uppercase;
|
|
1461
|
+
letter-spacing: 0.05em;
|
|
1462
|
+
color: var(--muted-foreground);
|
|
1463
|
+
font-weight: 500;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
.tag-bar-clear {
|
|
1467
|
+
font-size: 0.75rem;
|
|
1468
|
+
font-weight: 500;
|
|
1469
|
+
color: var(--primary);
|
|
1470
|
+
background: none;
|
|
1471
|
+
border: none;
|
|
1472
|
+
cursor: pointer;
|
|
1473
|
+
padding: 0.125rem 0.5rem;
|
|
1474
|
+
border-radius: var(--radius);
|
|
1475
|
+
transition: all 0.15s ease;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
.tag-bar-clear:hover {
|
|
1479
|
+
background: var(--muted);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.tag-bar-pills {
|
|
1483
|
+
display: flex;
|
|
1484
|
+
flex-wrap: wrap;
|
|
1485
|
+
gap: 0.375rem;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
.tag-pill {
|
|
1489
|
+
font-size: 0.75rem;
|
|
1490
|
+
font-weight: 500;
|
|
1491
|
+
padding: 0.25rem 0.625rem;
|
|
1492
|
+
background: var(--tag-bg);
|
|
1493
|
+
color: var(--tag-color);
|
|
1494
|
+
border: 1px solid var(--tag-border);
|
|
1495
|
+
border-radius: 9999px;
|
|
1496
|
+
font-family: var(--font-mono);
|
|
1497
|
+
cursor: pointer;
|
|
1498
|
+
transition: all 0.15s ease;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
.tag-pill:hover {
|
|
1502
|
+
background: var(--success-border);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
.tag-pill.active {
|
|
1506
|
+
background: var(--primary);
|
|
1507
|
+
color: var(--primary-foreground);
|
|
1508
|
+
border-color: var(--primary);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/* ============================================================================
|
|
1512
|
+
Summary Card Status Filter
|
|
1513
|
+
============================================================================ */
|
|
1514
|
+
.summary-card.status-active {
|
|
1515
|
+
box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/* ============================================================================
|
|
1519
|
+
Filter Results Counter
|
|
1520
|
+
============================================================================ */
|
|
1521
|
+
.filter-results {
|
|
1522
|
+
text-align: center;
|
|
1523
|
+
font-size: 0.8125rem;
|
|
1524
|
+
color: var(--muted-foreground);
|
|
1525
|
+
margin-bottom: 1rem;
|
|
1526
|
+
font-weight: 500;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1307
1529
|
/* ============================================================================
|
|
1308
1530
|
Feature Sections - shadcn accordion style
|
|
1309
1531
|
============================================================================ */
|
|
@@ -1530,6 +1752,12 @@ body {
|
|
|
1530
1752
|
color: var(--foreground);
|
|
1531
1753
|
}
|
|
1532
1754
|
|
|
1755
|
+
.step-param {
|
|
1756
|
+
font-style: italic;
|
|
1757
|
+
font-weight: 500;
|
|
1758
|
+
color: var(--step-param-color);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1533
1761
|
.step-duration {
|
|
1534
1762
|
color: var(--muted-foreground);
|
|
1535
1763
|
font-size: 0.6875rem;
|
|
@@ -1691,8 +1919,10 @@ body {
|
|
|
1691
1919
|
padding: 0;
|
|
1692
1920
|
}
|
|
1693
1921
|
|
|
1694
|
-
.header-actions
|
|
1695
|
-
|
|
1922
|
+
.header-actions,
|
|
1923
|
+
.tag-bar,
|
|
1924
|
+
.filter-results {
|
|
1925
|
+
display: none !important;
|
|
1696
1926
|
}
|
|
1697
1927
|
|
|
1698
1928
|
.feature,
|
|
@@ -2204,6 +2434,7 @@ body {
|
|
|
2204
2434
|
font-family: inherit;
|
|
2205
2435
|
background: none;
|
|
2206
2436
|
}
|
|
2437
|
+
|
|
2207
2438
|
`;
|
|
2208
2439
|
|
|
2209
2440
|
// src/formatters/html/renderers/status.ts
|
|
@@ -2266,6 +2497,28 @@ function renderSummary(args, _deps) {
|
|
|
2266
2497
|
</div>`;
|
|
2267
2498
|
}
|
|
2268
2499
|
|
|
2500
|
+
// src/formatters/html/renderers/tag-bar.ts
|
|
2501
|
+
function renderTagBar(args, deps) {
|
|
2502
|
+
const { tags, totalScenarios } = args;
|
|
2503
|
+
if (tags.length === 0) return "";
|
|
2504
|
+
const pills = tags.map(
|
|
2505
|
+
(tag) => `<button type="button" class="tag-pill" data-tag="${deps.escapeHtml(tag)}">${deps.escapeHtml(tag)}</button>`
|
|
2506
|
+
).join("\n ");
|
|
2507
|
+
return `
|
|
2508
|
+
<div class="tag-bar">
|
|
2509
|
+
<div class="tag-bar-header">
|
|
2510
|
+
<span class="tag-bar-label">Filter by tag</span>
|
|
2511
|
+
<button type="button" class="tag-bar-clear" style="display:none">Clear</button>
|
|
2512
|
+
</div>
|
|
2513
|
+
<div class="tag-bar-pills">
|
|
2514
|
+
${pills}
|
|
2515
|
+
</div>
|
|
2516
|
+
</div>
|
|
2517
|
+
<div class="filter-results" style="display:none">
|
|
2518
|
+
Showing <span class="visible-count">0</span> of <span class="total-count">${totalScenarios}</span> scenarios
|
|
2519
|
+
</div>`;
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2269
2522
|
// src/formatters/html/renderers/error-box.ts
|
|
2270
2523
|
function renderErrorBox(args, deps) {
|
|
2271
2524
|
const body = args.stack != null ? `${deps.escapeHtml(args.message)}
|
|
@@ -2426,10 +2679,11 @@ function renderStep(step, stepResult, index, deps) {
|
|
|
2426
2679
|
const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);
|
|
2427
2680
|
const stepClass = isContinuation ? "step continuation" : "step";
|
|
2428
2681
|
const stepDocs = deps.renderDocs(step.docs, "step-docs");
|
|
2682
|
+
const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
|
|
2429
2683
|
return `<div class="${stepClass}">
|
|
2430
2684
|
<span class="step-status ${statusClass}">${statusIcon}</span>
|
|
2431
2685
|
<span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
|
|
2432
|
-
<span class="step-text">${
|
|
2686
|
+
<span class="step-text">${textHtml}</span>
|
|
2433
2687
|
<span class="step-duration">${duration}</span>
|
|
2434
2688
|
</div>${stepDocs}`;
|
|
2435
2689
|
}
|
|
@@ -2441,6 +2695,30 @@ function renderSteps(args, deps) {
|
|
|
2441
2695
|
return `<div class="steps">${stepsHtml}</div>`;
|
|
2442
2696
|
}
|
|
2443
2697
|
|
|
2698
|
+
// src/formatters/html/renderers/step-params.ts
|
|
2699
|
+
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.\-])\d+(?:\.\d+)?(?![\w.\-])/g;
|
|
2700
|
+
function highlightStepParams(text, deps) {
|
|
2701
|
+
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
2702
|
+
if (matches.length === 0) {
|
|
2703
|
+
return deps.escapeHtml(text);
|
|
2704
|
+
}
|
|
2705
|
+
let result = "";
|
|
2706
|
+
let lastIndex = 0;
|
|
2707
|
+
for (const match of matches) {
|
|
2708
|
+
const matchStart = match.index;
|
|
2709
|
+
const matchEnd = matchStart + match[0].length;
|
|
2710
|
+
if (matchStart > lastIndex) {
|
|
2711
|
+
result += deps.escapeHtml(text.slice(lastIndex, matchStart));
|
|
2712
|
+
}
|
|
2713
|
+
result += `<span class="step-param">${deps.escapeHtml(match[0])}</span>`;
|
|
2714
|
+
lastIndex = matchEnd;
|
|
2715
|
+
}
|
|
2716
|
+
if (lastIndex < text.length) {
|
|
2717
|
+
result += deps.escapeHtml(text.slice(lastIndex));
|
|
2718
|
+
}
|
|
2719
|
+
return result;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2444
2722
|
// src/formatters/html/renderers/scenario.ts
|
|
2445
2723
|
function renderScenario(args, deps) {
|
|
2446
2724
|
const { tc } = args;
|
|
@@ -2448,6 +2726,19 @@ function renderScenario(args, deps) {
|
|
|
2448
2726
|
const statusClass = `status-${tc.status}`;
|
|
2449
2727
|
const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
|
|
2450
2728
|
const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
|
|
2729
|
+
const otelMeta = tc.story.meta?.otel;
|
|
2730
|
+
let traceBadge = "";
|
|
2731
|
+
if (otelMeta?.traceId) {
|
|
2732
|
+
const shortId = otelMeta.traceId.slice(0, 16);
|
|
2733
|
+
const traceLink = tc.story.docs?.find(
|
|
2734
|
+
(d) => d.kind === "link" && d.label === "View Trace"
|
|
2735
|
+
);
|
|
2736
|
+
if (traceLink) {
|
|
2737
|
+
traceBadge = `<a class="tag trace-tag" href="${deps.escapeHtml(traceLink.url)}" title="${deps.escapeHtml(otelMeta.traceId)}" target="_blank" rel="noopener">${deps.escapeHtml(shortId)}\u2026</a>`;
|
|
2738
|
+
} else {
|
|
2739
|
+
traceBadge = `<span class="tag trace-tag" title="${deps.escapeHtml(otelMeta.traceId)}">${deps.escapeHtml(shortId)}\u2026</span>`;
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2451
2742
|
const storyDocs = deps.renderDocs(tc.story.docs, "story-docs");
|
|
2452
2743
|
const steps = deps.renderSteps(
|
|
2453
2744
|
{ steps: tc.story.steps, stepResults: tc.stepResults },
|
|
@@ -2478,7 +2769,7 @@ function renderScenario(args, deps) {
|
|
|
2478
2769
|
<span class="status-icon ${statusClass}">${statusIcon}</span>
|
|
2479
2770
|
<span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
|
|
2480
2771
|
</div>
|
|
2481
|
-
<div class="scenario-meta">${tags}</div>
|
|
2772
|
+
<div class="scenario-meta">${tags}${traceBadge}</div>
|
|
2482
2773
|
</div>
|
|
2483
2774
|
<span class="scenario-duration">${duration}</span>
|
|
2484
2775
|
</div>
|
|
@@ -2565,6 +2856,15 @@ function buildBody(args, deps) {
|
|
|
2565
2856
|
deps.summaryDeps
|
|
2566
2857
|
)
|
|
2567
2858
|
);
|
|
2859
|
+
const allTags = [
|
|
2860
|
+
...new Set(run.testCases.flatMap((tc) => tc.tags))
|
|
2861
|
+
].sort();
|
|
2862
|
+
parts.push(
|
|
2863
|
+
deps.renderTagBar(
|
|
2864
|
+
{ tags: allTags, totalScenarios: total },
|
|
2865
|
+
deps.tagBarDeps
|
|
2866
|
+
)
|
|
2867
|
+
);
|
|
2568
2868
|
const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);
|
|
2569
2869
|
for (const [file, testCases] of byFile) {
|
|
2570
2870
|
parts.push(
|
|
@@ -2603,7 +2903,8 @@ function createHtmlFormatter(options = {}) {
|
|
|
2603
2903
|
const stepsDeps = {
|
|
2604
2904
|
escapeHtml,
|
|
2605
2905
|
getStatusIcon,
|
|
2606
|
-
renderDocs
|
|
2906
|
+
renderDocs,
|
|
2907
|
+
highlightStepParams: (text) => highlightStepParams(text, { escapeHtml })
|
|
2607
2908
|
};
|
|
2608
2909
|
const scenarioDeps = {
|
|
2609
2910
|
escapeHtml,
|
|
@@ -2621,12 +2922,15 @@ function createHtmlFormatter(options = {}) {
|
|
|
2621
2922
|
renderScenario: (args) => renderScenario(args, scenarioDeps),
|
|
2622
2923
|
scenarioDeps
|
|
2623
2924
|
};
|
|
2925
|
+
const tagBarDeps = { escapeHtml };
|
|
2624
2926
|
const bodyDeps = {
|
|
2625
2927
|
renderMetaInfo,
|
|
2626
2928
|
renderSummary,
|
|
2929
|
+
renderTagBar,
|
|
2627
2930
|
renderFeature,
|
|
2628
2931
|
metaDeps: { escapeHtml },
|
|
2629
2932
|
summaryDeps: {},
|
|
2933
|
+
tagBarDeps,
|
|
2630
2934
|
featureDeps
|
|
2631
2935
|
};
|
|
2632
2936
|
return {
|
|
@@ -2902,6 +3206,7 @@ var MarkdownFormatter = class {
|
|
|
2902
3206
|
includeSummaryTable: options.includeSummaryTable ?? false,
|
|
2903
3207
|
permalinkBaseUrl: options.permalinkBaseUrl,
|
|
2904
3208
|
ticketUrlTemplate: options.ticketUrlTemplate,
|
|
3209
|
+
traceUrlTemplate: options.traceUrlTemplate,
|
|
2905
3210
|
includeSourceLinks: options.includeSourceLinks ?? true,
|
|
2906
3211
|
customRenderers: options.customRenderers
|
|
2907
3212
|
};
|
|
@@ -3124,6 +3429,18 @@ var MarkdownFormatter = class {
|
|
|
3124
3429
|
meta.push(`Tickets: ${tc.story.tickets.map((t) => `\`${t}\``).join(", ")}`);
|
|
3125
3430
|
}
|
|
3126
3431
|
}
|
|
3432
|
+
const otelMeta = tc.story.meta?.otel;
|
|
3433
|
+
if (otelMeta?.traceId) {
|
|
3434
|
+
const traceTemplate = this.options.traceUrlTemplate;
|
|
3435
|
+
if (traceTemplate) {
|
|
3436
|
+
const url = traceTemplate.replace(/\{traceId\}/g, otelMeta.traceId);
|
|
3437
|
+
meta.push(
|
|
3438
|
+
`Trace: [${otelMeta.traceId.slice(0, 16)}\u2026](${url})`
|
|
3439
|
+
);
|
|
3440
|
+
} else {
|
|
3441
|
+
meta.push(`Trace: \`${otelMeta.traceId}\``);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3127
3444
|
if (meta.length > 0) {
|
|
3128
3445
|
lines.push(meta.join(" | "));
|
|
3129
3446
|
}
|
|
@@ -5066,6 +5383,31 @@ function detectCI4(env = process.env) {
|
|
|
5066
5383
|
return void 0;
|
|
5067
5384
|
}
|
|
5068
5385
|
|
|
5386
|
+
// src/utils/otel-detect.ts
|
|
5387
|
+
import { createRequire } from "module";
|
|
5388
|
+
function getRequire() {
|
|
5389
|
+
const url = import.meta.url ?? (typeof __filename !== "undefined" ? `file://${__filename}` : void 0);
|
|
5390
|
+
if (!url) throw new Error("Cannot determine module URL");
|
|
5391
|
+
return createRequire(url);
|
|
5392
|
+
}
|
|
5393
|
+
function tryGetActiveOtelContext() {
|
|
5394
|
+
try {
|
|
5395
|
+
const api = getRequire()("@opentelemetry/api");
|
|
5396
|
+
const span = api.trace?.getActiveSpan?.();
|
|
5397
|
+
if (!span) return void 0;
|
|
5398
|
+
const ctx = span.spanContext?.();
|
|
5399
|
+
if (!ctx?.traceId || ctx.traceId === "00000000000000000000000000000000")
|
|
5400
|
+
return void 0;
|
|
5401
|
+
return { traceId: ctx.traceId, spanId: ctx.spanId };
|
|
5402
|
+
} catch {
|
|
5403
|
+
return void 0;
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
function resolveTraceUrl(template, traceId) {
|
|
5407
|
+
if (!template) return void 0;
|
|
5408
|
+
return template.replace(/\{traceId\}/g, traceId);
|
|
5409
|
+
}
|
|
5410
|
+
|
|
5069
5411
|
// src/index.ts
|
|
5070
5412
|
var FORMAT_EXTENSIONS = {
|
|
5071
5413
|
markdown: ".md",
|
|
@@ -5256,6 +5598,7 @@ var ReportGenerator = class {
|
|
|
5256
5598
|
includeSummaryTable: options.markdown?.includeSummaryTable ?? false,
|
|
5257
5599
|
permalinkBaseUrl: options.markdown?.permalinkBaseUrl,
|
|
5258
5600
|
ticketUrlTemplate: options.markdown?.ticketUrlTemplate,
|
|
5601
|
+
traceUrlTemplate: options.markdown?.traceUrlTemplate,
|
|
5259
5602
|
includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
|
|
5260
5603
|
customRenderers: options.markdown?.customRenderers
|
|
5261
5604
|
}
|
|
@@ -5381,6 +5724,7 @@ var ReportGenerator = class {
|
|
|
5381
5724
|
includeSummaryTable: this.options.markdown.includeSummaryTable,
|
|
5382
5725
|
permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,
|
|
5383
5726
|
ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,
|
|
5727
|
+
traceUrlTemplate: this.options.markdown.traceUrlTemplate,
|
|
5384
5728
|
includeSourceLinks: this.options.markdown.includeSourceLinks,
|
|
5385
5729
|
customRenderers: this.options.markdown.customRenderers
|
|
5386
5730
|
});
|
|
@@ -5442,7 +5786,9 @@ export {
|
|
|
5442
5786
|
readPackageVersion,
|
|
5443
5787
|
resolveAttachment,
|
|
5444
5788
|
resolveAttachments,
|
|
5789
|
+
resolveTraceUrl,
|
|
5445
5790
|
slugify,
|
|
5791
|
+
tryGetActiveOtelContext,
|
|
5446
5792
|
validateCanonicalRun
|
|
5447
5793
|
};
|
|
5448
5794
|
//# sourceMappingURL=index.js.map
|