executable-stories-formatters 0.1.0 → 0.2.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/dist/cli.js +268 -25
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +268 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +268 -25
- package/dist/index.js.map +1 -1
- package/package.json +12 -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
|
|
|
@@ -821,6 +933,9 @@ function generateScript(options) {
|
|
|
821
933
|
initCalls.push("initTheme();");
|
|
822
934
|
}
|
|
823
935
|
initCalls.push("initSearch();");
|
|
936
|
+
initCalls.push("initTagFilter();");
|
|
937
|
+
initCalls.push("initStatusFilter();");
|
|
938
|
+
initCalls.push("initKeyboardShortcuts();");
|
|
824
939
|
initCalls.push("initCollapse();");
|
|
825
940
|
const initScript = `
|
|
826
941
|
// Initialize on load
|
|
@@ -1304,6 +1419,98 @@ body {
|
|
|
1304
1419
|
}
|
|
1305
1420
|
.summary-card.pending .value { color: var(--pending); }
|
|
1306
1421
|
|
|
1422
|
+
/* ============================================================================
|
|
1423
|
+
Tag Filter Bar
|
|
1424
|
+
============================================================================ */
|
|
1425
|
+
.tag-bar {
|
|
1426
|
+
margin-bottom: 1rem;
|
|
1427
|
+
padding: 0.75rem 1rem;
|
|
1428
|
+
background: var(--card);
|
|
1429
|
+
border: 1px solid var(--border);
|
|
1430
|
+
border-radius: var(--radius);
|
|
1431
|
+
position: sticky;
|
|
1432
|
+
top: 0;
|
|
1433
|
+
z-index: 10;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.tag-bar-header {
|
|
1437
|
+
display: flex;
|
|
1438
|
+
justify-content: space-between;
|
|
1439
|
+
align-items: center;
|
|
1440
|
+
margin-bottom: 0.5rem;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
.tag-bar-label {
|
|
1444
|
+
font-size: 0.6875rem;
|
|
1445
|
+
text-transform: uppercase;
|
|
1446
|
+
letter-spacing: 0.05em;
|
|
1447
|
+
color: var(--muted-foreground);
|
|
1448
|
+
font-weight: 500;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
.tag-bar-clear {
|
|
1452
|
+
font-size: 0.75rem;
|
|
1453
|
+
font-weight: 500;
|
|
1454
|
+
color: var(--primary);
|
|
1455
|
+
background: none;
|
|
1456
|
+
border: none;
|
|
1457
|
+
cursor: pointer;
|
|
1458
|
+
padding: 0.125rem 0.5rem;
|
|
1459
|
+
border-radius: var(--radius);
|
|
1460
|
+
transition: all 0.15s ease;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
.tag-bar-clear:hover {
|
|
1464
|
+
background: var(--muted);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
.tag-bar-pills {
|
|
1468
|
+
display: flex;
|
|
1469
|
+
flex-wrap: wrap;
|
|
1470
|
+
gap: 0.375rem;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.tag-pill {
|
|
1474
|
+
font-size: 0.75rem;
|
|
1475
|
+
font-weight: 500;
|
|
1476
|
+
padding: 0.25rem 0.625rem;
|
|
1477
|
+
background: var(--tag-bg);
|
|
1478
|
+
color: var(--tag-color);
|
|
1479
|
+
border: 1px solid var(--tag-border);
|
|
1480
|
+
border-radius: 9999px;
|
|
1481
|
+
font-family: var(--font-mono);
|
|
1482
|
+
cursor: pointer;
|
|
1483
|
+
transition: all 0.15s ease;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
.tag-pill:hover {
|
|
1487
|
+
background: var(--success-border);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
.tag-pill.active {
|
|
1491
|
+
background: var(--primary);
|
|
1492
|
+
color: var(--primary-foreground);
|
|
1493
|
+
border-color: var(--primary);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
/* ============================================================================
|
|
1497
|
+
Summary Card Status Filter
|
|
1498
|
+
============================================================================ */
|
|
1499
|
+
.summary-card.status-active {
|
|
1500
|
+
box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/* ============================================================================
|
|
1504
|
+
Filter Results Counter
|
|
1505
|
+
============================================================================ */
|
|
1506
|
+
.filter-results {
|
|
1507
|
+
text-align: center;
|
|
1508
|
+
font-size: 0.8125rem;
|
|
1509
|
+
color: var(--muted-foreground);
|
|
1510
|
+
margin-bottom: 1rem;
|
|
1511
|
+
font-weight: 500;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1307
1514
|
/* ============================================================================
|
|
1308
1515
|
Feature Sections - shadcn accordion style
|
|
1309
1516
|
============================================================================ */
|
|
@@ -1691,8 +1898,10 @@ body {
|
|
|
1691
1898
|
padding: 0;
|
|
1692
1899
|
}
|
|
1693
1900
|
|
|
1694
|
-
.header-actions
|
|
1695
|
-
|
|
1901
|
+
.header-actions,
|
|
1902
|
+
.tag-bar,
|
|
1903
|
+
.filter-results {
|
|
1904
|
+
display: none !important;
|
|
1696
1905
|
}
|
|
1697
1906
|
|
|
1698
1907
|
.feature,
|
|
@@ -2266,6 +2475,28 @@ function renderSummary(args, _deps) {
|
|
|
2266
2475
|
</div>`;
|
|
2267
2476
|
}
|
|
2268
2477
|
|
|
2478
|
+
// src/formatters/html/renderers/tag-bar.ts
|
|
2479
|
+
function renderTagBar(args, deps) {
|
|
2480
|
+
const { tags, totalScenarios } = args;
|
|
2481
|
+
if (tags.length === 0) return "";
|
|
2482
|
+
const pills = tags.map(
|
|
2483
|
+
(tag) => `<button type="button" class="tag-pill" data-tag="${deps.escapeHtml(tag)}">${deps.escapeHtml(tag)}</button>`
|
|
2484
|
+
).join("\n ");
|
|
2485
|
+
return `
|
|
2486
|
+
<div class="tag-bar">
|
|
2487
|
+
<div class="tag-bar-header">
|
|
2488
|
+
<span class="tag-bar-label">Filter by tag</span>
|
|
2489
|
+
<button type="button" class="tag-bar-clear" style="display:none">Clear</button>
|
|
2490
|
+
</div>
|
|
2491
|
+
<div class="tag-bar-pills">
|
|
2492
|
+
${pills}
|
|
2493
|
+
</div>
|
|
2494
|
+
</div>
|
|
2495
|
+
<div class="filter-results" style="display:none">
|
|
2496
|
+
Showing <span class="visible-count">0</span> of <span class="total-count">${totalScenarios}</span> scenarios
|
|
2497
|
+
</div>`;
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2269
2500
|
// src/formatters/html/renderers/error-box.ts
|
|
2270
2501
|
function renderErrorBox(args, deps) {
|
|
2271
2502
|
const body = args.stack != null ? `${deps.escapeHtml(args.message)}
|
|
@@ -2565,6 +2796,15 @@ function buildBody(args, deps) {
|
|
|
2565
2796
|
deps.summaryDeps
|
|
2566
2797
|
)
|
|
2567
2798
|
);
|
|
2799
|
+
const allTags = [
|
|
2800
|
+
...new Set(run.testCases.flatMap((tc) => tc.tags))
|
|
2801
|
+
].sort();
|
|
2802
|
+
parts.push(
|
|
2803
|
+
deps.renderTagBar(
|
|
2804
|
+
{ tags: allTags, totalScenarios: total },
|
|
2805
|
+
deps.tagBarDeps
|
|
2806
|
+
)
|
|
2807
|
+
);
|
|
2568
2808
|
const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);
|
|
2569
2809
|
for (const [file, testCases] of byFile) {
|
|
2570
2810
|
parts.push(
|
|
@@ -2621,12 +2861,15 @@ function createHtmlFormatter(options = {}) {
|
|
|
2621
2861
|
renderScenario: (args) => renderScenario(args, scenarioDeps),
|
|
2622
2862
|
scenarioDeps
|
|
2623
2863
|
};
|
|
2864
|
+
const tagBarDeps = { escapeHtml };
|
|
2624
2865
|
const bodyDeps = {
|
|
2625
2866
|
renderMetaInfo,
|
|
2626
2867
|
renderSummary,
|
|
2868
|
+
renderTagBar,
|
|
2627
2869
|
renderFeature,
|
|
2628
2870
|
metaDeps: { escapeHtml },
|
|
2629
2871
|
summaryDeps: {},
|
|
2872
|
+
tagBarDeps,
|
|
2630
2873
|
featureDeps
|
|
2631
2874
|
};
|
|
2632
2875
|
return {
|