executable-stories-formatters 0.7.4 → 0.7.5
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/dist/cli.js +765 -28
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +758 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +757 -27
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -852,6 +852,7 @@ function applyAllFilters() {
|
|
|
852
852
|
});
|
|
853
853
|
|
|
854
854
|
updateFilterResults(visibleCount, totalCount);
|
|
855
|
+
syncTocVisibility();
|
|
855
856
|
writeUrlState();
|
|
856
857
|
}
|
|
857
858
|
|
|
@@ -868,13 +869,135 @@ function updateFilterResults(visible, total) {
|
|
|
868
869
|
if (tc) tc.textContent = total;
|
|
869
870
|
}
|
|
870
871
|
|
|
871
|
-
// Keyboard
|
|
872
|
+
// Keyboard navigation
|
|
873
|
+
var focusedScenarioIndex = -1;
|
|
874
|
+
|
|
875
|
+
function getVisibleScenarios() {
|
|
876
|
+
return Array.from(document.querySelectorAll('.scenario')).filter(function(s) {
|
|
877
|
+
return s.style.display !== 'none' && s.closest('.feature').style.display !== 'none';
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function focusScenario(index) {
|
|
882
|
+
var scenarios = getVisibleScenarios();
|
|
883
|
+
if (scenarios.length === 0) return;
|
|
884
|
+
|
|
885
|
+
// Remove previous focus
|
|
886
|
+
var prev = document.querySelector('.scenario-focused');
|
|
887
|
+
if (prev) prev.classList.remove('scenario-focused');
|
|
888
|
+
|
|
889
|
+
// Wrap around
|
|
890
|
+
if (index < 0) index = scenarios.length - 1;
|
|
891
|
+
if (index >= scenarios.length) index = 0;
|
|
892
|
+
focusedScenarioIndex = index;
|
|
893
|
+
|
|
894
|
+
var scenario = scenarios[index];
|
|
895
|
+
scenario.classList.add('scenario-focused');
|
|
896
|
+
scenario.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function showShortcutsOverlay() {
|
|
900
|
+
if (document.querySelector('.shortcuts-overlay')) return;
|
|
901
|
+
var overlay = document.createElement('div');
|
|
902
|
+
overlay.className = 'shortcuts-overlay';
|
|
903
|
+
overlay.innerHTML = '<div class="shortcuts-modal">' +
|
|
904
|
+
'<div class="shortcuts-title">Keyboard Shortcuts</div>' +
|
|
905
|
+
'<div class="shortcuts-grid">' +
|
|
906
|
+
'<kbd>j</kbd><span>Next scenario</span>' +
|
|
907
|
+
'<kbd>k</kbd><span>Previous scenario</span>' +
|
|
908
|
+
'<kbd>Enter</kbd><span>Expand/collapse scenario</span>' +
|
|
909
|
+
'<kbd>Escape</kbd><span>Collapse scenario / close</span>' +
|
|
910
|
+
'<kbd>/</kbd><span>Focus search</span>' +
|
|
911
|
+
'<kbd>?</kbd><span>Toggle this help</span>' +
|
|
912
|
+
'<kbd>e</kbd><span>Expand all</span>' +
|
|
913
|
+
'<kbd>c</kbd><span>Collapse all</span>' +
|
|
914
|
+
'<kbd>t</kbd><span>Toggle table of contents</span>' +
|
|
915
|
+
'</div></div>';
|
|
916
|
+
overlay.addEventListener('click', function(ev) {
|
|
917
|
+
if (ev.target === overlay) hideShortcutsOverlay();
|
|
918
|
+
});
|
|
919
|
+
document.body.appendChild(overlay);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function hideShortcutsOverlay() {
|
|
923
|
+
var overlay = document.querySelector('.shortcuts-overlay');
|
|
924
|
+
if (overlay) overlay.remove();
|
|
925
|
+
}
|
|
926
|
+
|
|
872
927
|
function initKeyboardShortcuts() {
|
|
873
928
|
document.addEventListener('keydown', function(e) {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
929
|
+
var tag = e.target.tagName;
|
|
930
|
+
if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') {
|
|
931
|
+
if (e.key === 'Escape') {
|
|
932
|
+
e.target.blur();
|
|
933
|
+
if (e.target.classList.contains('search-input')) {
|
|
934
|
+
e.target.value = '';
|
|
935
|
+
applyAllFilters();
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
942
|
+
|
|
943
|
+
switch (e.key) {
|
|
944
|
+
case 'j':
|
|
945
|
+
e.preventDefault();
|
|
946
|
+
focusScenario(focusedScenarioIndex + 1);
|
|
947
|
+
break;
|
|
948
|
+
case 'k':
|
|
949
|
+
e.preventDefault();
|
|
950
|
+
focusScenario(focusedScenarioIndex - 1);
|
|
951
|
+
break;
|
|
952
|
+
case 'Enter':
|
|
953
|
+
e.preventDefault();
|
|
954
|
+
var scenarios = getVisibleScenarios();
|
|
955
|
+
if (focusedScenarioIndex >= 0 && focusedScenarioIndex < scenarios.length) {
|
|
956
|
+
var s = scenarios[focusedScenarioIndex];
|
|
957
|
+
var h = s.querySelector('.scenario-header');
|
|
958
|
+
if (h) toggleCollapse(h, s);
|
|
959
|
+
}
|
|
960
|
+
break;
|
|
961
|
+
case 'Escape':
|
|
962
|
+
if (document.querySelector('.shortcuts-overlay')) {
|
|
963
|
+
hideShortcutsOverlay();
|
|
964
|
+
} else {
|
|
965
|
+
var scenarios2 = getVisibleScenarios();
|
|
966
|
+
if (focusedScenarioIndex >= 0 && focusedScenarioIndex < scenarios2.length) {
|
|
967
|
+
var sc = scenarios2[focusedScenarioIndex];
|
|
968
|
+
if (!sc.classList.contains('collapsed')) {
|
|
969
|
+
sc.classList.add('collapsed');
|
|
970
|
+
var sh = sc.querySelector('.scenario-header');
|
|
971
|
+
if (sh) sh.setAttribute('aria-expanded', 'false');
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
break;
|
|
976
|
+
case '/':
|
|
977
|
+
e.preventDefault();
|
|
978
|
+
var input = document.querySelector('.search-input');
|
|
979
|
+
if (input) input.focus();
|
|
980
|
+
break;
|
|
981
|
+
case '?':
|
|
982
|
+
e.preventDefault();
|
|
983
|
+
if (document.querySelector('.shortcuts-overlay')) {
|
|
984
|
+
hideShortcutsOverlay();
|
|
985
|
+
} else {
|
|
986
|
+
showShortcutsOverlay();
|
|
987
|
+
}
|
|
988
|
+
break;
|
|
989
|
+
case 'e':
|
|
990
|
+
e.preventDefault();
|
|
991
|
+
expandAll();
|
|
992
|
+
break;
|
|
993
|
+
case 'c':
|
|
994
|
+
e.preventDefault();
|
|
995
|
+
collapseAll();
|
|
996
|
+
break;
|
|
997
|
+
case 't':
|
|
998
|
+
e.preventDefault();
|
|
999
|
+
if (typeof toggleToc === 'function') toggleToc();
|
|
1000
|
+
break;
|
|
878
1001
|
}
|
|
879
1002
|
});
|
|
880
1003
|
}
|
|
@@ -1012,6 +1135,189 @@ function writeUrlState() {
|
|
|
1012
1135
|
var url = window.location.pathname + (qs ? '?' + qs : '');
|
|
1013
1136
|
history.replaceState(null, '', url);
|
|
1014
1137
|
}
|
|
1138
|
+
|
|
1139
|
+
// Permalink copy
|
|
1140
|
+
function copyPermalink(anchorId) {
|
|
1141
|
+
var url = location.origin + location.pathname + location.search + '#' + anchorId;
|
|
1142
|
+
navigator.clipboard.writeText(url).then(function() {
|
|
1143
|
+
var el = document.getElementById(anchorId);
|
|
1144
|
+
if (el) showCopyToast(el);
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
function showCopyToast(el) {
|
|
1149
|
+
var existing = el.querySelector('.copy-toast');
|
|
1150
|
+
if (existing) existing.remove();
|
|
1151
|
+
var toast = document.createElement('span');
|
|
1152
|
+
toast.className = 'copy-toast';
|
|
1153
|
+
toast.textContent = 'Copied!';
|
|
1154
|
+
var header = el.querySelector('.feature-header, .scenario-header');
|
|
1155
|
+
if (header) {
|
|
1156
|
+
header.style.position = 'relative';
|
|
1157
|
+
header.appendChild(toast);
|
|
1158
|
+
}
|
|
1159
|
+
setTimeout(function() { toast.remove(); }, 1500);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Copy scenario as markdown
|
|
1163
|
+
function copyScenarioAsMarkdown(scenarioId) {
|
|
1164
|
+
var scenario = document.getElementById(scenarioId);
|
|
1165
|
+
if (!scenario) return;
|
|
1166
|
+
|
|
1167
|
+
var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';
|
|
1168
|
+
var steps = scenario.querySelectorAll('.step, .step.continuation');
|
|
1169
|
+
var lines = ['### Scenario: ' + title.trim(), ''];
|
|
1170
|
+
|
|
1171
|
+
steps.forEach(function(step) {
|
|
1172
|
+
var keyword = step.getAttribute('data-keyword') || '';
|
|
1173
|
+
var text = step.getAttribute('data-text') || '';
|
|
1174
|
+
lines.push('- **' + keyword + '** ' + text);
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
var errorBox = scenario.querySelector('.error-message');
|
|
1178
|
+
if (errorBox) {
|
|
1179
|
+
var errorText = errorBox.textContent || '';
|
|
1180
|
+
lines.push('');
|
|
1181
|
+
lines.push('> **Error:** ' + errorText.trim());
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
var md = lines.join('\\n');
|
|
1185
|
+
navigator.clipboard.writeText(md).then(function() {
|
|
1186
|
+
showCopyToast(scenario);
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// Hash scroll on load
|
|
1191
|
+
function initHashScroll() {
|
|
1192
|
+
if (!location.hash) return;
|
|
1193
|
+
var target = document.querySelector(location.hash);
|
|
1194
|
+
if (!target) return;
|
|
1195
|
+
var feature = target.closest('.feature');
|
|
1196
|
+
if (feature && feature.classList.contains('collapsed')) {
|
|
1197
|
+
feature.classList.remove('collapsed');
|
|
1198
|
+
var fh = feature.querySelector('.feature-header');
|
|
1199
|
+
if (fh) fh.setAttribute('aria-expanded', 'true');
|
|
1200
|
+
}
|
|
1201
|
+
if (target.classList.contains('collapsed')) {
|
|
1202
|
+
target.classList.remove('collapsed');
|
|
1203
|
+
var sh = target.querySelector('.scenario-header');
|
|
1204
|
+
if (sh) sh.setAttribute('aria-expanded', 'true');
|
|
1205
|
+
}
|
|
1206
|
+
setTimeout(function() {
|
|
1207
|
+
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
1208
|
+
target.classList.add('hash-highlight');
|
|
1209
|
+
}, 100);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// Table of contents
|
|
1213
|
+
function toggleToc() {
|
|
1214
|
+
var sidebar = document.querySelector('.toc-sidebar');
|
|
1215
|
+
var wrapper = document.querySelector('.report-layout');
|
|
1216
|
+
if (!sidebar || !wrapper) return;
|
|
1217
|
+
var isMobile = window.matchMedia('(max-width: 767px)').matches;
|
|
1218
|
+
if (isMobile) {
|
|
1219
|
+
sidebar.classList.toggle('toc-mobile-open');
|
|
1220
|
+
} else {
|
|
1221
|
+
wrapper.classList.toggle('toc-hidden');
|
|
1222
|
+
var hidden = wrapper.classList.contains('toc-hidden');
|
|
1223
|
+
localStorage.setItem('toc-visible', String(!hidden));
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
function initToc() {
|
|
1228
|
+
var sidebar = document.querySelector('.toc-sidebar');
|
|
1229
|
+
if (!sidebar) return;
|
|
1230
|
+
|
|
1231
|
+
var saved = localStorage.getItem('toc-visible');
|
|
1232
|
+
var wrapper = document.querySelector('.report-layout');
|
|
1233
|
+
if (saved === 'false' && wrapper) {
|
|
1234
|
+
wrapper.classList.add('toc-hidden');
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// Active tracking via IntersectionObserver
|
|
1238
|
+
var observer = new IntersectionObserver(function(entries) {
|
|
1239
|
+
entries.forEach(function(entry) {
|
|
1240
|
+
if (entry.isIntersecting) {
|
|
1241
|
+
var id = entry.target.id;
|
|
1242
|
+
if (!id) return;
|
|
1243
|
+
document.querySelectorAll('.toc-scenario, .toc-feature-toggle').forEach(function(el) {
|
|
1244
|
+
el.classList.remove('toc-active');
|
|
1245
|
+
});
|
|
1246
|
+
var tocLink = sidebar.querySelector('a[href="#' + id + '"]');
|
|
1247
|
+
if (tocLink) tocLink.classList.add('toc-active');
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
}, { rootMargin: '-10% 0px -80% 0px' });
|
|
1251
|
+
|
|
1252
|
+
document.querySelectorAll('.feature, .scenario').forEach(function(el) {
|
|
1253
|
+
if (el.id) observer.observe(el);
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
// Click navigation: expand collapsed parents
|
|
1257
|
+
sidebar.querySelectorAll('.toc-scenario').forEach(function(link) {
|
|
1258
|
+
link.addEventListener('click', function(e) {
|
|
1259
|
+
var hash = link.getAttribute('href');
|
|
1260
|
+
if (!hash) return;
|
|
1261
|
+
var target = document.querySelector(hash);
|
|
1262
|
+
if (!target) return;
|
|
1263
|
+
var feature = target.closest('.feature');
|
|
1264
|
+
if (feature && feature.classList.contains('collapsed')) {
|
|
1265
|
+
feature.classList.remove('collapsed');
|
|
1266
|
+
var fh = feature.querySelector('.feature-header');
|
|
1267
|
+
if (fh) fh.setAttribute('aria-expanded', 'true');
|
|
1268
|
+
}
|
|
1269
|
+
if (target.classList.contains('collapsed')) {
|
|
1270
|
+
target.classList.remove('collapsed');
|
|
1271
|
+
var sh = target.querySelector('.scenario-header');
|
|
1272
|
+
if (sh) sh.setAttribute('aria-expanded', 'true');
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Theme picker
|
|
1279
|
+
function initThemePicker() {
|
|
1280
|
+
var picker = document.querySelector('.theme-picker');
|
|
1281
|
+
if (!picker) return;
|
|
1282
|
+
|
|
1283
|
+
var saved = localStorage.getItem('report-theme');
|
|
1284
|
+
if (saved) {
|
|
1285
|
+
picker.value = saved;
|
|
1286
|
+
switchReportTheme(saved);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
picker.addEventListener('change', function(e) {
|
|
1290
|
+
switchReportTheme(e.target.value);
|
|
1291
|
+
localStorage.setItem('report-theme', e.target.value);
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function switchReportTheme(name) {
|
|
1296
|
+
document.querySelectorAll('style[data-theme-name]').forEach(function(s) {
|
|
1297
|
+
s.disabled = s.dataset.themeName !== name;
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// Sync TOC visibility with filters
|
|
1302
|
+
function syncTocVisibility() {
|
|
1303
|
+
var sidebar = document.querySelector('.toc-sidebar');
|
|
1304
|
+
if (!sidebar) return;
|
|
1305
|
+
|
|
1306
|
+
sidebar.querySelectorAll('.toc-scenario').forEach(function(link) {
|
|
1307
|
+
var href = link.getAttribute('href');
|
|
1308
|
+
if (!href) return;
|
|
1309
|
+
var target = document.querySelector(href);
|
|
1310
|
+
link.style.display = (target && target.style.display !== 'none') ? '' : 'none';
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
sidebar.querySelectorAll('.toc-feature').forEach(function(feature) {
|
|
1314
|
+
var visibleScenarios = feature.querySelectorAll('.toc-scenario');
|
|
1315
|
+
var anyVisible = Array.from(visibleScenarios).some(function(s) {
|
|
1316
|
+
return s.style.display !== 'none';
|
|
1317
|
+
});
|
|
1318
|
+
feature.style.display = anyVisible ? '' : 'none';
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1015
1321
|
`;
|
|
1016
1322
|
var JS_MARKDOWN_FN = `
|
|
1017
1323
|
function parseMarkdownSections(marked) {
|
|
@@ -1052,6 +1358,9 @@ function generateScript(options) {
|
|
|
1052
1358
|
initCalls.push("initCollapse();");
|
|
1053
1359
|
initCalls.push("initDetailLevel();");
|
|
1054
1360
|
initCalls.push("applyAllFilters();");
|
|
1361
|
+
initCalls.push("initHashScroll();");
|
|
1362
|
+
initCalls.push("initToc();");
|
|
1363
|
+
initCalls.push("initThemePicker();");
|
|
1055
1364
|
const initScript = `
|
|
1056
1365
|
// Initialize on load
|
|
1057
1366
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -1113,6 +1422,7 @@ function generateHtmlTemplate(title, styles, body, options = {}) {
|
|
|
1113
1422
|
}
|
|
1114
1423
|
const cdnStylesHtml = cdnStyles.length > 0 ? "\n " + cdnStyles.join("\n ") : "";
|
|
1115
1424
|
const esmScriptHtml = generateEsmScript(options);
|
|
1425
|
+
const additionalThemeStyles = (options.additionalThemeCss ?? []).map((t) => `<style data-theme-name="${escapeHtml(t.name)}" disabled>${t.css}</style>`).join("\n ");
|
|
1116
1426
|
return `<!DOCTYPE html>
|
|
1117
1427
|
<html lang="en"${themeAttr} data-detail-level="full">
|
|
1118
1428
|
<head>
|
|
@@ -1120,19 +1430,27 @@ function generateHtmlTemplate(title, styles, body, options = {}) {
|
|
|
1120
1430
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1121
1431
|
<meta name="color-scheme" content="light dark">
|
|
1122
1432
|
<title>${escapeHtml(title)}</title>${cdnStylesHtml}
|
|
1123
|
-
<style>${styles}</style>
|
|
1433
|
+
<style${options.additionalThemeCss ? ` data-theme-name="${escapeHtml(options.activeThemeName ?? "default")}"` : ""}>${styles}</style>
|
|
1434
|
+
${additionalThemeStyles}
|
|
1124
1435
|
</head>
|
|
1125
1436
|
<body>
|
|
1126
|
-
<div class="
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
<div class="
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1437
|
+
<div class="report-layout">
|
|
1438
|
+
${options.tocHtml ?? ""}
|
|
1439
|
+
<div class="main-content">
|
|
1440
|
+
<div class="container">
|
|
1441
|
+
<header class="header">
|
|
1442
|
+
<h1>${escapeHtml(title)}</h1>
|
|
1443
|
+
<div class="header-actions">
|
|
1444
|
+
<button type="button" class="toc-toggle" onclick="toggleToc()" aria-label="Toggle table of contents" title="Toggle contents">☰</button>
|
|
1445
|
+
${includeSearch ? '<input type="text" class="search-input" placeholder="Search scenarios..." aria-label="Search scenarios">' : ""}
|
|
1446
|
+
<button type="button" class="detail-toggle" onclick="toggleDetailLevel()" aria-label="Toggle detail level" title="Toggle documentation detail"></button>
|
|
1447
|
+
${options.themePickerHtml ?? ""}
|
|
1448
|
+
${includeDarkMode ? '<button type="button" class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle theme"></button>' : ""}
|
|
1449
|
+
</div>
|
|
1450
|
+
</header>
|
|
1451
|
+
${body}
|
|
1133
1452
|
</div>
|
|
1134
|
-
</
|
|
1135
|
-
${body}
|
|
1453
|
+
</div>
|
|
1136
1454
|
</div>
|
|
1137
1455
|
<script>${script}</script>${esmScriptHtml}
|
|
1138
1456
|
</body>
|
|
@@ -2836,6 +3154,331 @@ body {
|
|
|
2836
3154
|
display: none;
|
|
2837
3155
|
}
|
|
2838
3156
|
|
|
3157
|
+
/* ============================================================================
|
|
3158
|
+
Permalink Anchors
|
|
3159
|
+
============================================================================ */
|
|
3160
|
+
.permalink-anchor {
|
|
3161
|
+
display: inline-flex;
|
|
3162
|
+
align-items: center;
|
|
3163
|
+
justify-content: center;
|
|
3164
|
+
width: 1.5rem;
|
|
3165
|
+
height: 1.5rem;
|
|
3166
|
+
border: none;
|
|
3167
|
+
background: none;
|
|
3168
|
+
color: var(--muted-foreground);
|
|
3169
|
+
cursor: pointer;
|
|
3170
|
+
opacity: 0;
|
|
3171
|
+
transition: opacity 0.15s ease;
|
|
3172
|
+
font-size: 0.875rem;
|
|
3173
|
+
font-weight: 600;
|
|
3174
|
+
padding: 0;
|
|
3175
|
+
flex-shrink: 0;
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
.feature-header:hover .permalink-anchor,
|
|
3179
|
+
.scenario-header:hover .permalink-anchor,
|
|
3180
|
+
.permalink-anchor:focus-visible {
|
|
3181
|
+
opacity: 1;
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
.permalink-anchor:hover {
|
|
3185
|
+
color: var(--primary);
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
.copy-toast {
|
|
3189
|
+
position: absolute;
|
|
3190
|
+
right: 0.5rem;
|
|
3191
|
+
top: 50%;
|
|
3192
|
+
transform: translateY(-50%);
|
|
3193
|
+
background: var(--foreground);
|
|
3194
|
+
color: var(--background);
|
|
3195
|
+
padding: 0.25rem 0.5rem;
|
|
3196
|
+
border-radius: var(--radius);
|
|
3197
|
+
font-size: 0.75rem;
|
|
3198
|
+
font-weight: 500;
|
|
3199
|
+
pointer-events: none;
|
|
3200
|
+
animation: fadeOut 1.5s ease forwards;
|
|
3201
|
+
z-index: 10;
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
@keyframes fadeOut {
|
|
3205
|
+
0%, 70% { opacity: 1; }
|
|
3206
|
+
100% { opacity: 0; }
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
.hash-highlight {
|
|
3210
|
+
animation: hashPulse 2s ease;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
@keyframes hashPulse {
|
|
3214
|
+
0%, 100% { background: transparent; }
|
|
3215
|
+
20% { background: color-mix(in srgb, var(--primary) 12%, transparent); }
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
.scenario-actions {
|
|
3219
|
+
display: flex;
|
|
3220
|
+
align-items: center;
|
|
3221
|
+
gap: 0.25rem;
|
|
3222
|
+
flex-shrink: 0;
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
.copy-scenario-btn {
|
|
3226
|
+
display: inline-flex;
|
|
3227
|
+
align-items: center;
|
|
3228
|
+
justify-content: center;
|
|
3229
|
+
width: 1.5rem;
|
|
3230
|
+
height: 1.5rem;
|
|
3231
|
+
border: none;
|
|
3232
|
+
background: none;
|
|
3233
|
+
color: var(--muted-foreground);
|
|
3234
|
+
cursor: pointer;
|
|
3235
|
+
opacity: 0;
|
|
3236
|
+
transition: opacity 0.15s ease;
|
|
3237
|
+
font-size: 0.875rem;
|
|
3238
|
+
padding: 0;
|
|
3239
|
+
flex-shrink: 0;
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
.scenario-header:hover .copy-scenario-btn,
|
|
3243
|
+
.copy-scenario-btn:focus-visible {
|
|
3244
|
+
opacity: 1;
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
.copy-scenario-btn:hover {
|
|
3248
|
+
color: var(--primary);
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
/* ============================================================================
|
|
3252
|
+
Keyboard Navigation
|
|
3253
|
+
============================================================================ */
|
|
3254
|
+
.scenario-focused {
|
|
3255
|
+
border-left: 2px solid var(--primary);
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
.shortcuts-overlay {
|
|
3259
|
+
position: fixed;
|
|
3260
|
+
inset: 0;
|
|
3261
|
+
background: rgb(0 0 0 / 0.5);
|
|
3262
|
+
display: flex;
|
|
3263
|
+
align-items: center;
|
|
3264
|
+
justify-content: center;
|
|
3265
|
+
z-index: 100;
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
.shortcuts-modal {
|
|
3269
|
+
background: var(--card);
|
|
3270
|
+
color: var(--card-foreground);
|
|
3271
|
+
border: 1px solid var(--border);
|
|
3272
|
+
border-radius: calc(var(--radius) * 2);
|
|
3273
|
+
padding: 1.5rem 2rem;
|
|
3274
|
+
max-width: 400px;
|
|
3275
|
+
width: 90vw;
|
|
3276
|
+
box-shadow: var(--shadow-md, 0 4px 12px rgb(0 0 0 / 0.15));
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
.shortcuts-title {
|
|
3280
|
+
font-weight: 600;
|
|
3281
|
+
font-size: 1.125rem;
|
|
3282
|
+
margin-bottom: 1rem;
|
|
3283
|
+
padding-bottom: 0.5rem;
|
|
3284
|
+
border-bottom: 1px solid var(--border);
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
.shortcuts-grid {
|
|
3288
|
+
display: grid;
|
|
3289
|
+
grid-template-columns: auto 1fr;
|
|
3290
|
+
gap: 0.5rem 1rem;
|
|
3291
|
+
align-items: center;
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3294
|
+
.shortcuts-grid kbd {
|
|
3295
|
+
display: inline-flex;
|
|
3296
|
+
align-items: center;
|
|
3297
|
+
justify-content: center;
|
|
3298
|
+
min-width: 1.75rem;
|
|
3299
|
+
padding: 0.125rem 0.375rem;
|
|
3300
|
+
background: var(--muted);
|
|
3301
|
+
border: 1px solid var(--border);
|
|
3302
|
+
border-radius: calc(var(--radius) * 0.5);
|
|
3303
|
+
font-family: var(--font-mono);
|
|
3304
|
+
font-size: 0.75rem;
|
|
3305
|
+
font-weight: 500;
|
|
3306
|
+
color: var(--muted-foreground);
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
.shortcuts-grid span {
|
|
3310
|
+
font-size: 0.875rem;
|
|
3311
|
+
color: var(--foreground);
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
/* ============================================================================
|
|
3315
|
+
Table of Contents Sidebar
|
|
3316
|
+
============================================================================ */
|
|
3317
|
+
.report-layout {
|
|
3318
|
+
display: flex;
|
|
3319
|
+
min-height: 100vh;
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
.report-layout.toc-hidden .toc-sidebar {
|
|
3323
|
+
display: none;
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
.main-content {
|
|
3327
|
+
flex: 1;
|
|
3328
|
+
min-width: 0;
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
.toc-sidebar {
|
|
3332
|
+
width: 260px;
|
|
3333
|
+
flex-shrink: 0;
|
|
3334
|
+
position: sticky;
|
|
3335
|
+
top: 0;
|
|
3336
|
+
height: 100vh;
|
|
3337
|
+
overflow-y: auto;
|
|
3338
|
+
border-right: 1px solid var(--border);
|
|
3339
|
+
background: var(--card);
|
|
3340
|
+
padding: 1rem 0;
|
|
3341
|
+
font-size: 0.8125rem;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
.toc-header {
|
|
3345
|
+
padding: 0 1rem 0.75rem;
|
|
3346
|
+
border-bottom: 1px solid var(--border);
|
|
3347
|
+
margin-bottom: 0.5rem;
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
.toc-title {
|
|
3351
|
+
font-weight: 600;
|
|
3352
|
+
font-size: 0.875rem;
|
|
3353
|
+
color: var(--foreground);
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
.toc-feature {
|
|
3357
|
+
margin-bottom: 0.25rem;
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
.toc-feature-toggle {
|
|
3361
|
+
display: flex;
|
|
3362
|
+
align-items: center;
|
|
3363
|
+
width: 100%;
|
|
3364
|
+
padding: 0.375rem 1rem;
|
|
3365
|
+
border: none;
|
|
3366
|
+
background: none;
|
|
3367
|
+
text-align: left;
|
|
3368
|
+
cursor: pointer;
|
|
3369
|
+
font-size: 0.8125rem;
|
|
3370
|
+
font-weight: 600;
|
|
3371
|
+
color: var(--foreground);
|
|
3372
|
+
font-family: var(--font-sans);
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
.toc-feature-toggle:hover {
|
|
3376
|
+
background: var(--accent);
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
.toc-feature-toggle[aria-expanded="false"] + .toc-scenarios {
|
|
3380
|
+
display: none;
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
.toc-scenarios {
|
|
3384
|
+
display: flex;
|
|
3385
|
+
flex-direction: column;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
.toc-scenario {
|
|
3389
|
+
display: flex;
|
|
3390
|
+
align-items: baseline;
|
|
3391
|
+
gap: 0.375rem;
|
|
3392
|
+
padding: 0.25rem 1rem 0.25rem 1.5rem;
|
|
3393
|
+
color: var(--muted-foreground);
|
|
3394
|
+
text-decoration: none;
|
|
3395
|
+
font-size: 0.8125rem;
|
|
3396
|
+
line-height: 1.4;
|
|
3397
|
+
border-left: 2px solid transparent;
|
|
3398
|
+
transition: all 0.1s ease;
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
.toc-scenario:hover {
|
|
3402
|
+
color: var(--foreground);
|
|
3403
|
+
background: var(--accent);
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
.toc-scenario.toc-active {
|
|
3407
|
+
color: var(--foreground);
|
|
3408
|
+
border-left-color: var(--primary);
|
|
3409
|
+
font-weight: 500;
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
.toc-scenario.toc-failed {
|
|
3413
|
+
border-left-color: var(--error, var(--destructive));
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
.toc-status {
|
|
3417
|
+
flex-shrink: 0;
|
|
3418
|
+
font-size: 0.75rem;
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
.toc-toggle {
|
|
3422
|
+
display: inline-flex;
|
|
3423
|
+
align-items: center;
|
|
3424
|
+
justify-content: center;
|
|
3425
|
+
width: 2.25rem;
|
|
3426
|
+
height: 2.25rem;
|
|
3427
|
+
border: 1px solid var(--border);
|
|
3428
|
+
border-radius: var(--radius);
|
|
3429
|
+
background: var(--background);
|
|
3430
|
+
cursor: pointer;
|
|
3431
|
+
color: var(--foreground);
|
|
3432
|
+
font-size: 1rem;
|
|
3433
|
+
transition: all 0.15s ease;
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
.toc-toggle:hover {
|
|
3437
|
+
background: var(--accent);
|
|
3438
|
+
}
|
|
3439
|
+
|
|
3440
|
+
/* Mobile: overlay sidebar */
|
|
3441
|
+
@media (max-width: 767px) {
|
|
3442
|
+
.toc-sidebar {
|
|
3443
|
+
position: fixed;
|
|
3444
|
+
left: 0;
|
|
3445
|
+
top: 0;
|
|
3446
|
+
z-index: 50;
|
|
3447
|
+
box-shadow: var(--shadow-sm, 0 1px 3px rgb(0 0 0 / 0.1));
|
|
3448
|
+
transform: translateX(-100%);
|
|
3449
|
+
transition: transform 0.2s ease;
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
.toc-sidebar.toc-mobile-open {
|
|
3453
|
+
transform: translateX(0);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
/* ============================================================================
|
|
3458
|
+
Theme Picker
|
|
3459
|
+
============================================================================ */
|
|
3460
|
+
.theme-picker {
|
|
3461
|
+
height: 2.25rem;
|
|
3462
|
+
padding: 0 0.5rem;
|
|
3463
|
+
border: 1px solid var(--border);
|
|
3464
|
+
border-radius: var(--radius);
|
|
3465
|
+
background: var(--background);
|
|
3466
|
+
color: var(--foreground);
|
|
3467
|
+
font-size: 0.8125rem;
|
|
3468
|
+
font-family: var(--font-sans);
|
|
3469
|
+
cursor: pointer;
|
|
3470
|
+
transition: all 0.15s ease;
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
.theme-picker:hover {
|
|
3474
|
+
background: var(--accent);
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
.theme-picker:focus-visible {
|
|
3478
|
+
outline: 2px solid var(--ring);
|
|
3479
|
+
outline-offset: 2px;
|
|
3480
|
+
}
|
|
3481
|
+
|
|
2839
3482
|
`;
|
|
2840
3483
|
|
|
2841
3484
|
// src/formatters/html/themes/default.ts
|
|
@@ -12095,6 +12738,11 @@ function resolveTheme(nameOrTheme) {
|
|
|
12095
12738
|
function getAvailableThemes() {
|
|
12096
12739
|
return [...THEME_REGISTRY.keys()];
|
|
12097
12740
|
}
|
|
12741
|
+
function getCssOnlyThemes() {
|
|
12742
|
+
return [...THEME_REGISTRY.values()].filter(
|
|
12743
|
+
(theme) => !theme.buildBody && !theme.generateTemplate
|
|
12744
|
+
);
|
|
12745
|
+
}
|
|
12098
12746
|
|
|
12099
12747
|
// src/formatters/html/renderers/status.ts
|
|
12100
12748
|
function getStatusIcon(status) {
|
|
@@ -12376,7 +13024,7 @@ function renderStep(step, stepResult, index, deps) {
|
|
|
12376
13024
|
const stepClass = isContinuation ? "step continuation" : "step";
|
|
12377
13025
|
const stepDocs = deps.renderDocs(step.docs, "step-docs");
|
|
12378
13026
|
const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
|
|
12379
|
-
return `<div class="${stepClass}">
|
|
13027
|
+
return `<div class="${stepClass}" data-keyword="${deps.escapeHtml(keywordTrimmed)}" data-text="${deps.escapeHtml(step.text)}">
|
|
12380
13028
|
<span class="step-status ${statusClass}">${statusIcon}</span>
|
|
12381
13029
|
<span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
|
|
12382
13030
|
<span class="step-text">${textHtml}</span>
|
|
@@ -12507,7 +13155,11 @@ function renderScenario(args, deps) {
|
|
|
12507
13155
|
</div>
|
|
12508
13156
|
<div class="scenario-meta">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
12509
13157
|
</div>
|
|
12510
|
-
<
|
|
13158
|
+
<div class="scenario-actions">
|
|
13159
|
+
<button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown">⎘</button>
|
|
13160
|
+
<button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
|
|
13161
|
+
<span class="scenario-duration">${duration}</span>
|
|
13162
|
+
</div>
|
|
12511
13163
|
</div>
|
|
12512
13164
|
<div class="scenario-content">
|
|
12513
13165
|
${storyDocs}
|
|
@@ -12717,6 +13369,7 @@ function renderFeature(args, deps) {
|
|
|
12717
13369
|
const featureName = suitePaths.length > 0 && suitePaths[0].length > 0 ? suitePaths[0][0] : file.split("/").pop()?.replace(/\.[^.]+$/, "") ?? file;
|
|
12718
13370
|
const collapsedClass = deps.startCollapsed ? " collapsed" : "";
|
|
12719
13371
|
const ariaExpanded = !deps.startCollapsed;
|
|
13372
|
+
const featureSlug = `feature-${slugify(file)}`;
|
|
12720
13373
|
const scenarios = testCases.map(
|
|
12721
13374
|
(tc) => deps.renderScenario(
|
|
12722
13375
|
{ tc, metrics: args.metricsMap?.get(tc.id) },
|
|
@@ -12724,8 +13377,9 @@ function renderFeature(args, deps) {
|
|
|
12724
13377
|
)
|
|
12725
13378
|
).join("\n");
|
|
12726
13379
|
return `
|
|
12727
|
-
<div class="feature${collapsedClass}">
|
|
13380
|
+
<div class="feature${collapsedClass}" id="${featureSlug}">
|
|
12728
13381
|
<div class="feature-header" role="button" tabindex="0" aria-expanded="${ariaExpanded}">
|
|
13382
|
+
<button class="permalink-anchor" onclick="copyPermalink('${featureSlug}')" aria-label="Copy link to feature" title="Copy link">#</button>
|
|
12729
13383
|
<div class="feature-info">
|
|
12730
13384
|
<div class="feature-title">${deps.escapeHtml(featureName)}</div>
|
|
12731
13385
|
<div class="feature-path">${deps.escapeHtml(file)}</div>
|
|
@@ -12838,6 +13492,57 @@ function renderFailureSummary(args, deps) {
|
|
|
12838
13492
|
</div>`;
|
|
12839
13493
|
}
|
|
12840
13494
|
|
|
13495
|
+
// src/formatters/html/renderers/toc.ts
|
|
13496
|
+
function groupBy4(items, keyFn) {
|
|
13497
|
+
const map = /* @__PURE__ */ new Map();
|
|
13498
|
+
for (const item of items) {
|
|
13499
|
+
const key = keyFn(item);
|
|
13500
|
+
const existing = map.get(key);
|
|
13501
|
+
if (existing) {
|
|
13502
|
+
existing.push(item);
|
|
13503
|
+
} else {
|
|
13504
|
+
map.set(key, [item]);
|
|
13505
|
+
}
|
|
13506
|
+
}
|
|
13507
|
+
return map;
|
|
13508
|
+
}
|
|
13509
|
+
function renderToc(args, deps) {
|
|
13510
|
+
const { run } = args;
|
|
13511
|
+
if (run.testCases.length === 0) return "";
|
|
13512
|
+
const byFile = groupBy4(run.testCases, (tc) => tc.sourceFile);
|
|
13513
|
+
const features = [];
|
|
13514
|
+
for (const [file, testCases] of byFile) {
|
|
13515
|
+
const suitePaths = testCases.map((tc) => tc.titlePath).filter((p) => p.length > 0);
|
|
13516
|
+
const featureName = suitePaths.length > 0 && suitePaths[0].length > 0 ? suitePaths[0][0] : file.split("/").pop()?.replace(/\.[^.]+$/, "") ?? file;
|
|
13517
|
+
const featureSlug = `feature-${slugify(file)}`;
|
|
13518
|
+
const scenarios = testCases.map((tc) => {
|
|
13519
|
+
const statusIcon = deps.getStatusIcon(tc.status);
|
|
13520
|
+
const statusClass = `status-${tc.status}`;
|
|
13521
|
+
const failedClass = tc.status === "failed" ? " toc-failed" : "";
|
|
13522
|
+
return `<a class="toc-scenario${failedClass}" href="#scenario-${tc.id}">
|
|
13523
|
+
<span class="toc-status ${statusClass}">${statusIcon}</span>
|
|
13524
|
+
${deps.escapeHtml(tc.story.scenario)}
|
|
13525
|
+
</a>`;
|
|
13526
|
+
}).join("\n");
|
|
13527
|
+
features.push(`<div class="toc-feature">
|
|
13528
|
+
<button class="toc-feature-toggle" aria-expanded="true" onclick="this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'); this.nextElementSibling.style.display = this.getAttribute('aria-expanded') === 'true' ? '' : 'none'" data-feature="#${featureSlug}">
|
|
13529
|
+
${deps.escapeHtml(featureName)}
|
|
13530
|
+
</button>
|
|
13531
|
+
<div class="toc-scenarios">
|
|
13532
|
+
${scenarios}
|
|
13533
|
+
</div>
|
|
13534
|
+
</div>`);
|
|
13535
|
+
}
|
|
13536
|
+
return `<nav class="toc-sidebar" aria-label="Table of contents">
|
|
13537
|
+
<div class="toc-header">
|
|
13538
|
+
<span class="toc-title">Contents</span>
|
|
13539
|
+
</div>
|
|
13540
|
+
<div class="toc-body">
|
|
13541
|
+
${features.join("\n")}
|
|
13542
|
+
</div>
|
|
13543
|
+
</nav>`;
|
|
13544
|
+
}
|
|
13545
|
+
|
|
12841
13546
|
// src/formatters/html/renderers/index.ts
|
|
12842
13547
|
function normalizeOptions(options = {}) {
|
|
12843
13548
|
return {
|
|
@@ -12851,7 +13556,9 @@ function normalizeOptions(options = {}) {
|
|
|
12851
13556
|
markdownEnabled: options.markdownEnabled ?? true,
|
|
12852
13557
|
permalinkBaseUrl: options.permalinkBaseUrl,
|
|
12853
13558
|
ticketUrlTemplate: options.ticketUrlTemplate,
|
|
12854
|
-
|
|
13559
|
+
tocEnabled: options.tocEnabled ?? true,
|
|
13560
|
+
theme: options.theme ?? "default",
|
|
13561
|
+
themePickerEnabled: options.themePickerEnabled ?? false
|
|
12855
13562
|
};
|
|
12856
13563
|
}
|
|
12857
13564
|
function createHtmlFormatter(options = {}) {
|
|
@@ -12893,6 +13600,10 @@ function createHtmlFormatter(options = {}) {
|
|
|
12893
13600
|
scenarioDeps
|
|
12894
13601
|
};
|
|
12895
13602
|
const tagBarDeps = { escapeHtml };
|
|
13603
|
+
const tocDeps = {
|
|
13604
|
+
escapeHtml,
|
|
13605
|
+
getStatusIcon
|
|
13606
|
+
};
|
|
12896
13607
|
const bodyDeps = {
|
|
12897
13608
|
renderMetaInfo,
|
|
12898
13609
|
renderSummary,
|
|
@@ -12911,6 +13622,16 @@ function createHtmlFormatter(options = {}) {
|
|
|
12911
13622
|
const bodyFn = theme.buildBody ?? buildBody;
|
|
12912
13623
|
const body = bodyFn({ run }, bodyDeps);
|
|
12913
13624
|
const templateFn = theme.generateTemplate ?? generateHtmlTemplate;
|
|
13625
|
+
const isStructuralTheme = !!(theme.buildBody || theme.generateTemplate);
|
|
13626
|
+
const tocHtml = opts.tocEnabled && !isStructuralTheme ? renderToc({ run }, tocDeps) : void 0;
|
|
13627
|
+
let themePickerHtml;
|
|
13628
|
+
let additionalThemeCss;
|
|
13629
|
+
if (opts.themePickerEnabled) {
|
|
13630
|
+
const cssOnlyThemes = getCssOnlyThemes();
|
|
13631
|
+
const pickerOptions = cssOnlyThemes.map((t) => `<option value="${t.name}"${t.name === theme.name ? " selected" : ""}>${t.label}</option>`).join("");
|
|
13632
|
+
themePickerHtml = `<select class="theme-picker" aria-label="Select theme">${pickerOptions}</select>`;
|
|
13633
|
+
additionalThemeCss = cssOnlyThemes.filter((t) => t.name !== theme.name).map((t) => ({ name: t.name, label: t.label, css: t.css }));
|
|
13634
|
+
}
|
|
12914
13635
|
return templateFn(
|
|
12915
13636
|
opts.title,
|
|
12916
13637
|
theme.css,
|
|
@@ -12922,7 +13643,11 @@ function createHtmlFormatter(options = {}) {
|
|
|
12922
13643
|
mermaidEnabled: opts.mermaidEnabled,
|
|
12923
13644
|
markdownEnabled: opts.markdownEnabled,
|
|
12924
13645
|
additionalJs: theme.additionalJs,
|
|
12925
|
-
additionalImports: theme.additionalImports
|
|
13646
|
+
additionalImports: theme.additionalImports,
|
|
13647
|
+
tocHtml,
|
|
13648
|
+
themePickerHtml,
|
|
13649
|
+
additionalThemeCss,
|
|
13650
|
+
activeThemeName: theme.name
|
|
12926
13651
|
}
|
|
12927
13652
|
);
|
|
12928
13653
|
}
|
|
@@ -12978,7 +13703,7 @@ var JUnitFormatter = class {
|
|
|
12978
13703
|
lines.push(
|
|
12979
13704
|
`<testsuites name="${escapeXml(this.options.suiteName)}" tests="${tests}" failures="${failures}" errors="${errors}" skipped="${skipped}" time="${time}">`
|
|
12980
13705
|
);
|
|
12981
|
-
const byFile =
|
|
13706
|
+
const byFile = groupBy5(run.testCases, (tc) => tc.sourceFile);
|
|
12982
13707
|
for (const [file, testCases] of byFile) {
|
|
12983
13708
|
lines.push(...this.buildTestSuite(file, testCases, indent, newline));
|
|
12984
13709
|
}
|
|
@@ -13151,7 +13876,7 @@ var JUnitFormatter = class {
|
|
|
13151
13876
|
function escapeXml(str) {
|
|
13152
13877
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
13153
13878
|
}
|
|
13154
|
-
function
|
|
13879
|
+
function groupBy5(items, keyFn) {
|
|
13155
13880
|
const map = /* @__PURE__ */ new Map();
|
|
13156
13881
|
for (const item of items) {
|
|
13157
13882
|
const key = keyFn(item);
|
|
@@ -13326,7 +14051,7 @@ var MarkdownFormatter = class {
|
|
|
13326
14051
|
* Render scenarios grouped by file.
|
|
13327
14052
|
*/
|
|
13328
14053
|
renderByFile(lines, testCases) {
|
|
13329
|
-
const byFile =
|
|
14054
|
+
const byFile = groupBy6(testCases, (tc) => tc.sourceFile);
|
|
13330
14055
|
for (const [file, fileTestCases] of byFile) {
|
|
13331
14056
|
lines.push(`## ${file}`);
|
|
13332
14057
|
lines.push("");
|
|
@@ -13343,7 +14068,7 @@ var MarkdownFormatter = class {
|
|
|
13343
14068
|
* Render suite groups.
|
|
13344
14069
|
*/
|
|
13345
14070
|
renderSuiteGroups(lines, testCases, baseLevel) {
|
|
13346
|
-
const bySuite =
|
|
14071
|
+
const bySuite = groupBy6(
|
|
13347
14072
|
testCases,
|
|
13348
14073
|
(tc) => tc.titlePath.join(this.options.suiteSeparator)
|
|
13349
14074
|
);
|
|
@@ -13637,7 +14362,7 @@ var MarkdownFormatter = class {
|
|
|
13637
14362
|
return entries;
|
|
13638
14363
|
}
|
|
13639
14364
|
};
|
|
13640
|
-
function
|
|
14365
|
+
function groupBy6(items, keyFn) {
|
|
13641
14366
|
const map = /* @__PURE__ */ new Map();
|
|
13642
14367
|
for (const item of items) {
|
|
13643
14368
|
const key = keyFn(item);
|
|
@@ -17377,7 +18102,9 @@ var ReportGenerator = class {
|
|
|
17377
18102
|
markdownEnabled: options.html?.markdownEnabled ?? true,
|
|
17378
18103
|
permalinkBaseUrl: options.html?.permalinkBaseUrl,
|
|
17379
18104
|
ticketUrlTemplate: options.html?.ticketUrlTemplate,
|
|
17380
|
-
theme: options.html?.theme ?? "default"
|
|
18105
|
+
theme: options.html?.theme ?? "default",
|
|
18106
|
+
tocEnabled: options.html?.tocEnabled ?? true,
|
|
18107
|
+
themePickerEnabled: options.html?.themePickerEnabled ?? false
|
|
17381
18108
|
},
|
|
17382
18109
|
junit: {
|
|
17383
18110
|
suiteName: options.junit?.suiteName ?? "Test Suite",
|
|
@@ -17500,7 +18227,9 @@ var ReportGenerator = class {
|
|
|
17500
18227
|
mermaidEnabled: this.options.html.mermaidEnabled,
|
|
17501
18228
|
markdownEnabled: this.options.html.markdownEnabled,
|
|
17502
18229
|
permalinkBaseUrl: this.options.html.permalinkBaseUrl,
|
|
17503
|
-
ticketUrlTemplate: this.options.html.ticketUrlTemplate
|
|
18230
|
+
ticketUrlTemplate: this.options.html.ticketUrlTemplate,
|
|
18231
|
+
tocEnabled: this.options.html.tocEnabled,
|
|
18232
|
+
themePickerEnabled: this.options.html.themePickerEnabled
|
|
17504
18233
|
});
|
|
17505
18234
|
return formatter.format(run);
|
|
17506
18235
|
}
|
|
@@ -17623,6 +18352,7 @@ export {
|
|
|
17623
18352
|
generateRunId,
|
|
17624
18353
|
generateTestCaseId,
|
|
17625
18354
|
getAvailableThemes,
|
|
18355
|
+
getCssOnlyThemes,
|
|
17626
18356
|
hasSufficientHistory,
|
|
17627
18357
|
listScenarios,
|
|
17628
18358
|
loadHistory,
|