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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Jag Reehal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -119,7 +119,7 @@ const generator = new ReportGenerator({
|
|
|
119
119
|
|
|
120
120
|
| Format | Description | File Extension |
|
|
121
121
|
| --- | --- | --- |
|
|
122
|
-
| `html` | Interactive HTML report with search and
|
|
122
|
+
| `html` | Interactive HTML report with search, screenshots, step parameter highlighting (quoted strings and numbers), syntax-highlighted code blocks, Mermaid diagrams, and Markdown in doc sections | `.html` |
|
|
123
123
|
| `markdown` | Markdown user-story documentation | `.md` |
|
|
124
124
|
| `junit` | JUnit XML for CI integration | `.junit.xml` |
|
|
125
125
|
| `cucumber-json` | Cucumber JSON for tooling compatibility | `.cucumber.json` |
|
package/dist/cli.js
CHANGED
|
@@ -896,50 +896,162 @@ function initTheme() {
|
|
|
896
896
|
}
|
|
897
897
|
`;
|
|
898
898
|
var JS_CORE = `
|
|
899
|
+
// Filter state
|
|
900
|
+
var activeTags = new Set();
|
|
901
|
+
var activeStatus = null;
|
|
902
|
+
|
|
899
903
|
// Search functionality
|
|
900
904
|
function initSearch() {
|
|
901
|
-
|
|
905
|
+
var input = document.querySelector('.search-input');
|
|
902
906
|
if (!input) return;
|
|
903
907
|
|
|
904
|
-
|
|
905
|
-
input.addEventListener('input', (
|
|
908
|
+
var debounceTimer;
|
|
909
|
+
input.addEventListener('input', function() {
|
|
906
910
|
clearTimeout(debounceTimer);
|
|
907
|
-
debounceTimer = setTimeout(()
|
|
908
|
-
|
|
911
|
+
debounceTimer = setTimeout(function() {
|
|
912
|
+
applyAllFilters();
|
|
909
913
|
}, 150);
|
|
910
914
|
});
|
|
911
915
|
|
|
912
916
|
// Clear search on Escape
|
|
913
|
-
input.addEventListener('keydown', (e)
|
|
917
|
+
input.addEventListener('keydown', function(e) {
|
|
914
918
|
if (e.key === 'Escape') {
|
|
915
919
|
e.target.value = '';
|
|
916
|
-
|
|
920
|
+
applyAllFilters();
|
|
917
921
|
}
|
|
918
922
|
});
|
|
919
923
|
}
|
|
920
924
|
|
|
921
|
-
|
|
922
|
-
|
|
925
|
+
// Tag filter
|
|
926
|
+
function initTagFilter() {
|
|
927
|
+
document.querySelectorAll('.tag-pill').forEach(function(pill) {
|
|
928
|
+
pill.addEventListener('click', function() {
|
|
929
|
+
var tag = pill.dataset.tag;
|
|
930
|
+
if (activeTags.has(tag)) {
|
|
931
|
+
activeTags.delete(tag);
|
|
932
|
+
pill.classList.remove('active');
|
|
933
|
+
} else {
|
|
934
|
+
activeTags.add(tag);
|
|
935
|
+
pill.classList.add('active');
|
|
936
|
+
}
|
|
937
|
+
updateClearButton();
|
|
938
|
+
applyAllFilters();
|
|
939
|
+
});
|
|
940
|
+
});
|
|
923
941
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
942
|
+
var clearBtn = document.querySelector('.tag-bar-clear');
|
|
943
|
+
if (clearBtn) {
|
|
944
|
+
clearBtn.addEventListener('click', function() {
|
|
945
|
+
activeTags.clear();
|
|
946
|
+
document.querySelectorAll('.tag-pill.active').forEach(function(p) { p.classList.remove('active'); });
|
|
947
|
+
updateClearButton();
|
|
948
|
+
applyAllFilters();
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
}
|
|
927
952
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
953
|
+
function updateClearButton() {
|
|
954
|
+
var clearBtn = document.querySelector('.tag-bar-clear');
|
|
955
|
+
if (clearBtn) {
|
|
956
|
+
clearBtn.style.display = activeTags.size > 0 ? '' : 'none';
|
|
957
|
+
}
|
|
958
|
+
}
|
|
932
959
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
960
|
+
// Status filter (clickable summary cards)
|
|
961
|
+
function initStatusFilter() {
|
|
962
|
+
document.querySelectorAll('.summary-card').forEach(function(card) {
|
|
963
|
+
card.style.cursor = 'pointer';
|
|
964
|
+
if (!card.classList.contains('passed') && !card.classList.contains('failed') && !card.classList.contains('skipped')) {
|
|
965
|
+
card.addEventListener('click', function() {
|
|
966
|
+
activeStatus = null;
|
|
967
|
+
document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });
|
|
968
|
+
applyAllFilters();
|
|
969
|
+
});
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
card.addEventListener('click', function() {
|
|
973
|
+
var status = card.classList.contains('passed') ? 'passed' :
|
|
974
|
+
card.classList.contains('failed') ? 'failed' : 'skipped';
|
|
975
|
+
if (activeStatus === status) {
|
|
976
|
+
activeStatus = null;
|
|
977
|
+
card.classList.remove('status-active');
|
|
978
|
+
} else {
|
|
979
|
+
activeStatus = status;
|
|
980
|
+
document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });
|
|
981
|
+
card.classList.add('status-active');
|
|
982
|
+
}
|
|
983
|
+
applyAllFilters();
|
|
984
|
+
});
|
|
985
|
+
});
|
|
986
|
+
}
|
|
937
987
|
|
|
938
|
-
|
|
939
|
-
|
|
988
|
+
// Unified filter: composes search + tags + status
|
|
989
|
+
function applyAllFilters() {
|
|
990
|
+
var searchInput = document.querySelector('.search-input');
|
|
991
|
+
var searchQuery = searchInput ? searchInput.value.toLowerCase().trim() : '';
|
|
992
|
+
var features = document.querySelectorAll('.feature');
|
|
993
|
+
var visibleCount = 0;
|
|
994
|
+
var totalCount = 0;
|
|
995
|
+
|
|
996
|
+
features.forEach(function(feature) {
|
|
997
|
+
var scenarios = feature.querySelectorAll('.scenario');
|
|
998
|
+
var featureVisible = 0;
|
|
999
|
+
|
|
1000
|
+
scenarios.forEach(function(scenario) {
|
|
1001
|
+
totalCount++;
|
|
1002
|
+
var title = (scenario.querySelector('.scenario-title') || {}).textContent || '';
|
|
1003
|
+
title = title.toLowerCase();
|
|
1004
|
+
var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.toLowerCase(); });
|
|
1005
|
+
var steps = Array.from(scenario.querySelectorAll('.step-text')).map(function(s) { return s.textContent.toLowerCase(); });
|
|
1006
|
+
var statusEl = scenario.querySelector('.status-icon');
|
|
1007
|
+
var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
|
|
1008
|
+
statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
|
|
1009
|
+
statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
|
|
1010
|
+
|
|
1011
|
+
var matchesSearch = !searchQuery ||
|
|
1012
|
+
title.includes(searchQuery) ||
|
|
1013
|
+
tags.some(function(t) { return t.includes(searchQuery); }) ||
|
|
1014
|
+
steps.some(function(s) { return s.includes(searchQuery); });
|
|
1015
|
+
|
|
1016
|
+
var matchesTags = activeTags.size === 0 ||
|
|
1017
|
+
tags.some(function(t) { return activeTags.has(t); });
|
|
1018
|
+
|
|
1019
|
+
var matchesStatus = !activeStatus ||
|
|
1020
|
+
status === activeStatus ||
|
|
1021
|
+
(activeStatus === 'skipped' && status === 'pending');
|
|
1022
|
+
|
|
1023
|
+
var visible = matchesSearch && matchesTags && matchesStatus;
|
|
1024
|
+
scenario.style.display = visible ? '' : 'none';
|
|
1025
|
+
if (visible) { visibleCount++; featureVisible++; }
|
|
940
1026
|
});
|
|
941
1027
|
|
|
942
|
-
feature.style.display =
|
|
1028
|
+
feature.style.display = featureVisible > 0 ? '' : 'none';
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
updateFilterResults(visibleCount, totalCount);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function updateFilterResults(visible, total) {
|
|
1035
|
+
var el = document.querySelector('.filter-results');
|
|
1036
|
+
if (!el) return;
|
|
1037
|
+
var searchInput = document.querySelector('.search-input');
|
|
1038
|
+
var isFiltering = activeTags.size > 0 || activeStatus ||
|
|
1039
|
+
(searchInput && searchInput.value.trim().length > 0);
|
|
1040
|
+
el.style.display = isFiltering ? '' : 'none';
|
|
1041
|
+
var vc = el.querySelector('.visible-count');
|
|
1042
|
+
var tc = el.querySelector('.total-count');
|
|
1043
|
+
if (vc) vc.textContent = visible;
|
|
1044
|
+
if (tc) tc.textContent = total;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// Keyboard shortcuts
|
|
1048
|
+
function initKeyboardShortcuts() {
|
|
1049
|
+
document.addEventListener('keydown', function(e) {
|
|
1050
|
+
if (e.key === '/' && !e.ctrlKey && !e.metaKey && e.target.tagName !== 'INPUT') {
|
|
1051
|
+
e.preventDefault();
|
|
1052
|
+
var input = document.querySelector('.search-input');
|
|
1053
|
+
if (input) input.focus();
|
|
1054
|
+
}
|
|
943
1055
|
});
|
|
944
1056
|
}
|
|
945
1057
|
|
|
@@ -974,6 +1086,18 @@ function initCollapse() {
|
|
|
974
1086
|
}
|
|
975
1087
|
});
|
|
976
1088
|
});
|
|
1089
|
+
|
|
1090
|
+
document.querySelectorAll('.trace-view-header').forEach(header => {
|
|
1091
|
+
header.addEventListener('click', () => {
|
|
1092
|
+
toggleCollapse(header, header.closest('.trace-view'));
|
|
1093
|
+
});
|
|
1094
|
+
header.addEventListener('keydown', (e) => {
|
|
1095
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
1096
|
+
e.preventDefault();
|
|
1097
|
+
toggleCollapse(header, header.closest('.trace-view'));
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
});
|
|
977
1101
|
}
|
|
978
1102
|
|
|
979
1103
|
function expandAll() {
|
|
@@ -1024,6 +1148,9 @@ function generateScript(options) {
|
|
|
1024
1148
|
initCalls.push("initTheme();");
|
|
1025
1149
|
}
|
|
1026
1150
|
initCalls.push("initSearch();");
|
|
1151
|
+
initCalls.push("initTagFilter();");
|
|
1152
|
+
initCalls.push("initStatusFilter();");
|
|
1153
|
+
initCalls.push("initKeyboardShortcuts();");
|
|
1027
1154
|
initCalls.push("initCollapse();");
|
|
1028
1155
|
const initScript = `
|
|
1029
1156
|
// Initialize on load
|
|
@@ -1174,6 +1301,7 @@ var CSS_STYLES = `
|
|
|
1174
1301
|
--tag-bg: hsl(145 55% 95%);
|
|
1175
1302
|
--tag-color: hsl(145 63% 30%);
|
|
1176
1303
|
--tag-border: hsl(145 55% 85%);
|
|
1304
|
+
--step-param-color: hsl(220 70% 50%);
|
|
1177
1305
|
|
|
1178
1306
|
/* Accordion/Collapsible styling */
|
|
1179
1307
|
--accordion-header-hover: hsl(0 0% 98%);
|
|
@@ -1232,6 +1360,7 @@ var CSS_STYLES = `
|
|
|
1232
1360
|
--tag-bg: hsl(145 35% 14%);
|
|
1233
1361
|
--tag-color: hsl(145 63% 60%);
|
|
1234
1362
|
--tag-border: hsl(145 35% 22%);
|
|
1363
|
+
--step-param-color: hsl(220 70% 70%);
|
|
1235
1364
|
|
|
1236
1365
|
/* Accordion/Collapsible styling */
|
|
1237
1366
|
--accordion-header-hover: hsl(0 0% 11%);
|
|
@@ -1280,6 +1409,7 @@ var CSS_STYLES = `
|
|
|
1280
1409
|
--tag-bg: hsl(145 35% 14%);
|
|
1281
1410
|
--tag-color: hsl(145 63% 60%);
|
|
1282
1411
|
--tag-border: hsl(145 35% 22%);
|
|
1412
|
+
--step-param-color: hsl(220 70% 70%);
|
|
1283
1413
|
--accordion-header-hover: hsl(0 0% 11%);
|
|
1284
1414
|
--accordion-content-bg: hsl(0 0% 7%);
|
|
1285
1415
|
}
|
|
@@ -1507,6 +1637,98 @@ body {
|
|
|
1507
1637
|
}
|
|
1508
1638
|
.summary-card.pending .value { color: var(--pending); }
|
|
1509
1639
|
|
|
1640
|
+
/* ============================================================================
|
|
1641
|
+
Tag Filter Bar
|
|
1642
|
+
============================================================================ */
|
|
1643
|
+
.tag-bar {
|
|
1644
|
+
margin-bottom: 1rem;
|
|
1645
|
+
padding: 0.75rem 1rem;
|
|
1646
|
+
background: var(--card);
|
|
1647
|
+
border: 1px solid var(--border);
|
|
1648
|
+
border-radius: var(--radius);
|
|
1649
|
+
position: sticky;
|
|
1650
|
+
top: 0;
|
|
1651
|
+
z-index: 10;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
.tag-bar-header {
|
|
1655
|
+
display: flex;
|
|
1656
|
+
justify-content: space-between;
|
|
1657
|
+
align-items: center;
|
|
1658
|
+
margin-bottom: 0.5rem;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
.tag-bar-label {
|
|
1662
|
+
font-size: 0.6875rem;
|
|
1663
|
+
text-transform: uppercase;
|
|
1664
|
+
letter-spacing: 0.05em;
|
|
1665
|
+
color: var(--muted-foreground);
|
|
1666
|
+
font-weight: 500;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
.tag-bar-clear {
|
|
1670
|
+
font-size: 0.75rem;
|
|
1671
|
+
font-weight: 500;
|
|
1672
|
+
color: var(--primary);
|
|
1673
|
+
background: none;
|
|
1674
|
+
border: none;
|
|
1675
|
+
cursor: pointer;
|
|
1676
|
+
padding: 0.125rem 0.5rem;
|
|
1677
|
+
border-radius: var(--radius);
|
|
1678
|
+
transition: all 0.15s ease;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
.tag-bar-clear:hover {
|
|
1682
|
+
background: var(--muted);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
.tag-bar-pills {
|
|
1686
|
+
display: flex;
|
|
1687
|
+
flex-wrap: wrap;
|
|
1688
|
+
gap: 0.375rem;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
.tag-pill {
|
|
1692
|
+
font-size: 0.75rem;
|
|
1693
|
+
font-weight: 500;
|
|
1694
|
+
padding: 0.25rem 0.625rem;
|
|
1695
|
+
background: var(--tag-bg);
|
|
1696
|
+
color: var(--tag-color);
|
|
1697
|
+
border: 1px solid var(--tag-border);
|
|
1698
|
+
border-radius: 9999px;
|
|
1699
|
+
font-family: var(--font-mono);
|
|
1700
|
+
cursor: pointer;
|
|
1701
|
+
transition: all 0.15s ease;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.tag-pill:hover {
|
|
1705
|
+
background: var(--success-border);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.tag-pill.active {
|
|
1709
|
+
background: var(--primary);
|
|
1710
|
+
color: var(--primary-foreground);
|
|
1711
|
+
border-color: var(--primary);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
/* ============================================================================
|
|
1715
|
+
Summary Card Status Filter
|
|
1716
|
+
============================================================================ */
|
|
1717
|
+
.summary-card.status-active {
|
|
1718
|
+
box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
/* ============================================================================
|
|
1722
|
+
Filter Results Counter
|
|
1723
|
+
============================================================================ */
|
|
1724
|
+
.filter-results {
|
|
1725
|
+
text-align: center;
|
|
1726
|
+
font-size: 0.8125rem;
|
|
1727
|
+
color: var(--muted-foreground);
|
|
1728
|
+
margin-bottom: 1rem;
|
|
1729
|
+
font-weight: 500;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1510
1732
|
/* ============================================================================
|
|
1511
1733
|
Feature Sections - shadcn accordion style
|
|
1512
1734
|
============================================================================ */
|
|
@@ -1733,6 +1955,12 @@ body {
|
|
|
1733
1955
|
color: var(--foreground);
|
|
1734
1956
|
}
|
|
1735
1957
|
|
|
1958
|
+
.step-param {
|
|
1959
|
+
font-style: italic;
|
|
1960
|
+
font-weight: 500;
|
|
1961
|
+
color: var(--step-param-color);
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1736
1964
|
.step-duration {
|
|
1737
1965
|
color: var(--muted-foreground);
|
|
1738
1966
|
font-size: 0.6875rem;
|
|
@@ -1894,8 +2122,10 @@ body {
|
|
|
1894
2122
|
padding: 0;
|
|
1895
2123
|
}
|
|
1896
2124
|
|
|
1897
|
-
.header-actions
|
|
1898
|
-
|
|
2125
|
+
.header-actions,
|
|
2126
|
+
.tag-bar,
|
|
2127
|
+
.filter-results {
|
|
2128
|
+
display: none !important;
|
|
1899
2129
|
}
|
|
1900
2130
|
|
|
1901
2131
|
.feature,
|
|
@@ -2407,6 +2637,7 @@ body {
|
|
|
2407
2637
|
font-family: inherit;
|
|
2408
2638
|
background: none;
|
|
2409
2639
|
}
|
|
2640
|
+
|
|
2410
2641
|
`;
|
|
2411
2642
|
|
|
2412
2643
|
// src/formatters/html/renderers/status.ts
|
|
@@ -2469,6 +2700,28 @@ function renderSummary(args, _deps) {
|
|
|
2469
2700
|
</div>`;
|
|
2470
2701
|
}
|
|
2471
2702
|
|
|
2703
|
+
// src/formatters/html/renderers/tag-bar.ts
|
|
2704
|
+
function renderTagBar(args, deps) {
|
|
2705
|
+
const { tags, totalScenarios } = args;
|
|
2706
|
+
if (tags.length === 0) return "";
|
|
2707
|
+
const pills = tags.map(
|
|
2708
|
+
(tag) => `<button type="button" class="tag-pill" data-tag="${deps.escapeHtml(tag)}">${deps.escapeHtml(tag)}</button>`
|
|
2709
|
+
).join("\n ");
|
|
2710
|
+
return `
|
|
2711
|
+
<div class="tag-bar">
|
|
2712
|
+
<div class="tag-bar-header">
|
|
2713
|
+
<span class="tag-bar-label">Filter by tag</span>
|
|
2714
|
+
<button type="button" class="tag-bar-clear" style="display:none">Clear</button>
|
|
2715
|
+
</div>
|
|
2716
|
+
<div class="tag-bar-pills">
|
|
2717
|
+
${pills}
|
|
2718
|
+
</div>
|
|
2719
|
+
</div>
|
|
2720
|
+
<div class="filter-results" style="display:none">
|
|
2721
|
+
Showing <span class="visible-count">0</span> of <span class="total-count">${totalScenarios}</span> scenarios
|
|
2722
|
+
</div>`;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2472
2725
|
// src/formatters/html/renderers/error-box.ts
|
|
2473
2726
|
function renderErrorBox(args, deps) {
|
|
2474
2727
|
const body = args.stack != null ? `${deps.escapeHtml(args.message)}
|
|
@@ -2629,10 +2882,11 @@ function renderStep(step, stepResult, index, deps) {
|
|
|
2629
2882
|
const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);
|
|
2630
2883
|
const stepClass = isContinuation ? "step continuation" : "step";
|
|
2631
2884
|
const stepDocs = deps.renderDocs(step.docs, "step-docs");
|
|
2885
|
+
const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
|
|
2632
2886
|
return `<div class="${stepClass}">
|
|
2633
2887
|
<span class="step-status ${statusClass}">${statusIcon}</span>
|
|
2634
2888
|
<span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
|
|
2635
|
-
<span class="step-text">${
|
|
2889
|
+
<span class="step-text">${textHtml}</span>
|
|
2636
2890
|
<span class="step-duration">${duration}</span>
|
|
2637
2891
|
</div>${stepDocs}`;
|
|
2638
2892
|
}
|
|
@@ -2644,6 +2898,30 @@ function renderSteps(args, deps) {
|
|
|
2644
2898
|
return `<div class="steps">${stepsHtml}</div>`;
|
|
2645
2899
|
}
|
|
2646
2900
|
|
|
2901
|
+
// src/formatters/html/renderers/step-params.ts
|
|
2902
|
+
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.\-])\d+(?:\.\d+)?(?![\w.\-])/g;
|
|
2903
|
+
function highlightStepParams(text, deps) {
|
|
2904
|
+
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
2905
|
+
if (matches.length === 0) {
|
|
2906
|
+
return deps.escapeHtml(text);
|
|
2907
|
+
}
|
|
2908
|
+
let result = "";
|
|
2909
|
+
let lastIndex = 0;
|
|
2910
|
+
for (const match of matches) {
|
|
2911
|
+
const matchStart = match.index;
|
|
2912
|
+
const matchEnd = matchStart + match[0].length;
|
|
2913
|
+
if (matchStart > lastIndex) {
|
|
2914
|
+
result += deps.escapeHtml(text.slice(lastIndex, matchStart));
|
|
2915
|
+
}
|
|
2916
|
+
result += `<span class="step-param">${deps.escapeHtml(match[0])}</span>`;
|
|
2917
|
+
lastIndex = matchEnd;
|
|
2918
|
+
}
|
|
2919
|
+
if (lastIndex < text.length) {
|
|
2920
|
+
result += deps.escapeHtml(text.slice(lastIndex));
|
|
2921
|
+
}
|
|
2922
|
+
return result;
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2647
2925
|
// src/formatters/html/renderers/scenario.ts
|
|
2648
2926
|
function renderScenario(args, deps) {
|
|
2649
2927
|
const { tc } = args;
|
|
@@ -2651,6 +2929,19 @@ function renderScenario(args, deps) {
|
|
|
2651
2929
|
const statusClass = `status-${tc.status}`;
|
|
2652
2930
|
const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
|
|
2653
2931
|
const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
|
|
2932
|
+
const otelMeta = tc.story.meta?.otel;
|
|
2933
|
+
let traceBadge = "";
|
|
2934
|
+
if (otelMeta?.traceId) {
|
|
2935
|
+
const shortId = otelMeta.traceId.slice(0, 16);
|
|
2936
|
+
const traceLink = tc.story.docs?.find(
|
|
2937
|
+
(d) => d.kind === "link" && d.label === "View Trace"
|
|
2938
|
+
);
|
|
2939
|
+
if (traceLink) {
|
|
2940
|
+
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>`;
|
|
2941
|
+
} else {
|
|
2942
|
+
traceBadge = `<span class="tag trace-tag" title="${deps.escapeHtml(otelMeta.traceId)}">${deps.escapeHtml(shortId)}\u2026</span>`;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2654
2945
|
const storyDocs = deps.renderDocs(tc.story.docs, "story-docs");
|
|
2655
2946
|
const steps = deps.renderSteps(
|
|
2656
2947
|
{ steps: tc.story.steps, stepResults: tc.stepResults },
|
|
@@ -2681,7 +2972,7 @@ function renderScenario(args, deps) {
|
|
|
2681
2972
|
<span class="status-icon ${statusClass}">${statusIcon}</span>
|
|
2682
2973
|
<span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
|
|
2683
2974
|
</div>
|
|
2684
|
-
<div class="scenario-meta">${tags}</div>
|
|
2975
|
+
<div class="scenario-meta">${tags}${traceBadge}</div>
|
|
2685
2976
|
</div>
|
|
2686
2977
|
<span class="scenario-duration">${duration}</span>
|
|
2687
2978
|
</div>
|
|
@@ -2768,6 +3059,15 @@ function buildBody(args, deps) {
|
|
|
2768
3059
|
deps.summaryDeps
|
|
2769
3060
|
)
|
|
2770
3061
|
);
|
|
3062
|
+
const allTags = [
|
|
3063
|
+
...new Set(run.testCases.flatMap((tc) => tc.tags))
|
|
3064
|
+
].sort();
|
|
3065
|
+
parts.push(
|
|
3066
|
+
deps.renderTagBar(
|
|
3067
|
+
{ tags: allTags, totalScenarios: total },
|
|
3068
|
+
deps.tagBarDeps
|
|
3069
|
+
)
|
|
3070
|
+
);
|
|
2771
3071
|
const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);
|
|
2772
3072
|
for (const [file, testCases] of byFile) {
|
|
2773
3073
|
parts.push(
|
|
@@ -2806,7 +3106,8 @@ function createHtmlFormatter(options = {}) {
|
|
|
2806
3106
|
const stepsDeps = {
|
|
2807
3107
|
escapeHtml,
|
|
2808
3108
|
getStatusIcon,
|
|
2809
|
-
renderDocs
|
|
3109
|
+
renderDocs,
|
|
3110
|
+
highlightStepParams: (text) => highlightStepParams(text, { escapeHtml })
|
|
2810
3111
|
};
|
|
2811
3112
|
const scenarioDeps = {
|
|
2812
3113
|
escapeHtml,
|
|
@@ -2824,12 +3125,15 @@ function createHtmlFormatter(options = {}) {
|
|
|
2824
3125
|
renderScenario: (args) => renderScenario(args, scenarioDeps),
|
|
2825
3126
|
scenarioDeps
|
|
2826
3127
|
};
|
|
3128
|
+
const tagBarDeps = { escapeHtml };
|
|
2827
3129
|
const bodyDeps = {
|
|
2828
3130
|
renderMetaInfo,
|
|
2829
3131
|
renderSummary,
|
|
3132
|
+
renderTagBar,
|
|
2830
3133
|
renderFeature,
|
|
2831
3134
|
metaDeps: { escapeHtml },
|
|
2832
3135
|
summaryDeps: {},
|
|
3136
|
+
tagBarDeps,
|
|
2833
3137
|
featureDeps
|
|
2834
3138
|
};
|
|
2835
3139
|
return {
|
|
@@ -3105,6 +3409,7 @@ var MarkdownFormatter = class {
|
|
|
3105
3409
|
includeSummaryTable: options.includeSummaryTable ?? false,
|
|
3106
3410
|
permalinkBaseUrl: options.permalinkBaseUrl,
|
|
3107
3411
|
ticketUrlTemplate: options.ticketUrlTemplate,
|
|
3412
|
+
traceUrlTemplate: options.traceUrlTemplate,
|
|
3108
3413
|
includeSourceLinks: options.includeSourceLinks ?? true,
|
|
3109
3414
|
customRenderers: options.customRenderers
|
|
3110
3415
|
};
|
|
@@ -3327,6 +3632,18 @@ var MarkdownFormatter = class {
|
|
|
3327
3632
|
meta.push(`Tickets: ${tc.story.tickets.map((t) => `\`${t}\``).join(", ")}`);
|
|
3328
3633
|
}
|
|
3329
3634
|
}
|
|
3635
|
+
const otelMeta = tc.story.meta?.otel;
|
|
3636
|
+
if (otelMeta?.traceId) {
|
|
3637
|
+
const traceTemplate = this.options.traceUrlTemplate;
|
|
3638
|
+
if (traceTemplate) {
|
|
3639
|
+
const url = traceTemplate.replace(/\{traceId\}/g, otelMeta.traceId);
|
|
3640
|
+
meta.push(
|
|
3641
|
+
`Trace: [${otelMeta.traceId.slice(0, 16)}\u2026](${url})`
|
|
3642
|
+
);
|
|
3643
|
+
} else {
|
|
3644
|
+
meta.push(`Trace: \`${otelMeta.traceId}\``);
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3330
3647
|
if (meta.length > 0) {
|
|
3331
3648
|
lines.push(meta.join(" | "));
|
|
3332
3649
|
}
|
|
@@ -4881,6 +5198,7 @@ var ReportGenerator = class {
|
|
|
4881
5198
|
includeSummaryTable: options.markdown?.includeSummaryTable ?? false,
|
|
4882
5199
|
permalinkBaseUrl: options.markdown?.permalinkBaseUrl,
|
|
4883
5200
|
ticketUrlTemplate: options.markdown?.ticketUrlTemplate,
|
|
5201
|
+
traceUrlTemplate: options.markdown?.traceUrlTemplate,
|
|
4884
5202
|
includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
|
|
4885
5203
|
customRenderers: options.markdown?.customRenderers
|
|
4886
5204
|
}
|
|
@@ -5006,6 +5324,7 @@ var ReportGenerator = class {
|
|
|
5006
5324
|
includeSummaryTable: this.options.markdown.includeSummaryTable,
|
|
5007
5325
|
permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,
|
|
5008
5326
|
ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,
|
|
5327
|
+
traceUrlTemplate: this.options.markdown.traceUrlTemplate,
|
|
5009
5328
|
includeSourceLinks: this.options.markdown.includeSourceLinks,
|
|
5010
5329
|
customRenderers: this.options.markdown.customRenderers
|
|
5011
5330
|
});
|