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.cjs
CHANGED
|
@@ -763,50 +763,162 @@ function initTheme() {
|
|
|
763
763
|
}
|
|
764
764
|
`;
|
|
765
765
|
var JS_CORE = `
|
|
766
|
+
// Filter state
|
|
767
|
+
var activeTags = new Set();
|
|
768
|
+
var activeStatus = null;
|
|
769
|
+
|
|
766
770
|
// Search functionality
|
|
767
771
|
function initSearch() {
|
|
768
|
-
|
|
772
|
+
var input = document.querySelector('.search-input');
|
|
769
773
|
if (!input) return;
|
|
770
774
|
|
|
771
|
-
|
|
772
|
-
input.addEventListener('input', (
|
|
775
|
+
var debounceTimer;
|
|
776
|
+
input.addEventListener('input', function() {
|
|
773
777
|
clearTimeout(debounceTimer);
|
|
774
|
-
debounceTimer = setTimeout(()
|
|
775
|
-
|
|
778
|
+
debounceTimer = setTimeout(function() {
|
|
779
|
+
applyAllFilters();
|
|
776
780
|
}, 150);
|
|
777
781
|
});
|
|
778
782
|
|
|
779
783
|
// Clear search on Escape
|
|
780
|
-
input.addEventListener('keydown', (e)
|
|
784
|
+
input.addEventListener('keydown', function(e) {
|
|
781
785
|
if (e.key === 'Escape') {
|
|
782
786
|
e.target.value = '';
|
|
783
|
-
|
|
787
|
+
applyAllFilters();
|
|
784
788
|
}
|
|
785
789
|
});
|
|
786
790
|
}
|
|
787
791
|
|
|
788
|
-
|
|
789
|
-
|
|
792
|
+
// Tag filter
|
|
793
|
+
function initTagFilter() {
|
|
794
|
+
document.querySelectorAll('.tag-pill').forEach(function(pill) {
|
|
795
|
+
pill.addEventListener('click', function() {
|
|
796
|
+
var tag = pill.dataset.tag;
|
|
797
|
+
if (activeTags.has(tag)) {
|
|
798
|
+
activeTags.delete(tag);
|
|
799
|
+
pill.classList.remove('active');
|
|
800
|
+
} else {
|
|
801
|
+
activeTags.add(tag);
|
|
802
|
+
pill.classList.add('active');
|
|
803
|
+
}
|
|
804
|
+
updateClearButton();
|
|
805
|
+
applyAllFilters();
|
|
806
|
+
});
|
|
807
|
+
});
|
|
790
808
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
809
|
+
var clearBtn = document.querySelector('.tag-bar-clear');
|
|
810
|
+
if (clearBtn) {
|
|
811
|
+
clearBtn.addEventListener('click', function() {
|
|
812
|
+
activeTags.clear();
|
|
813
|
+
document.querySelectorAll('.tag-pill.active').forEach(function(p) { p.classList.remove('active'); });
|
|
814
|
+
updateClearButton();
|
|
815
|
+
applyAllFilters();
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
794
819
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
820
|
+
function updateClearButton() {
|
|
821
|
+
var clearBtn = document.querySelector('.tag-bar-clear');
|
|
822
|
+
if (clearBtn) {
|
|
823
|
+
clearBtn.style.display = activeTags.size > 0 ? '' : 'none';
|
|
824
|
+
}
|
|
825
|
+
}
|
|
799
826
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
827
|
+
// Status filter (clickable summary cards)
|
|
828
|
+
function initStatusFilter() {
|
|
829
|
+
document.querySelectorAll('.summary-card').forEach(function(card) {
|
|
830
|
+
card.style.cursor = 'pointer';
|
|
831
|
+
if (!card.classList.contains('passed') && !card.classList.contains('failed') && !card.classList.contains('skipped')) {
|
|
832
|
+
card.addEventListener('click', function() {
|
|
833
|
+
activeStatus = null;
|
|
834
|
+
document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });
|
|
835
|
+
applyAllFilters();
|
|
836
|
+
});
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
card.addEventListener('click', function() {
|
|
840
|
+
var status = card.classList.contains('passed') ? 'passed' :
|
|
841
|
+
card.classList.contains('failed') ? 'failed' : 'skipped';
|
|
842
|
+
if (activeStatus === status) {
|
|
843
|
+
activeStatus = null;
|
|
844
|
+
card.classList.remove('status-active');
|
|
845
|
+
} else {
|
|
846
|
+
activeStatus = status;
|
|
847
|
+
document.querySelectorAll('.summary-card').forEach(function(c) { c.classList.remove('status-active'); });
|
|
848
|
+
card.classList.add('status-active');
|
|
849
|
+
}
|
|
850
|
+
applyAllFilters();
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
}
|
|
804
854
|
|
|
805
|
-
|
|
806
|
-
|
|
855
|
+
// Unified filter: composes search + tags + status
|
|
856
|
+
function applyAllFilters() {
|
|
857
|
+
var searchInput = document.querySelector('.search-input');
|
|
858
|
+
var searchQuery = searchInput ? searchInput.value.toLowerCase().trim() : '';
|
|
859
|
+
var features = document.querySelectorAll('.feature');
|
|
860
|
+
var visibleCount = 0;
|
|
861
|
+
var totalCount = 0;
|
|
862
|
+
|
|
863
|
+
features.forEach(function(feature) {
|
|
864
|
+
var scenarios = feature.querySelectorAll('.scenario');
|
|
865
|
+
var featureVisible = 0;
|
|
866
|
+
|
|
867
|
+
scenarios.forEach(function(scenario) {
|
|
868
|
+
totalCount++;
|
|
869
|
+
var title = (scenario.querySelector('.scenario-title') || {}).textContent || '';
|
|
870
|
+
title = title.toLowerCase();
|
|
871
|
+
var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.toLowerCase(); });
|
|
872
|
+
var steps = Array.from(scenario.querySelectorAll('.step-text')).map(function(s) { return s.textContent.toLowerCase(); });
|
|
873
|
+
var statusEl = scenario.querySelector('.status-icon');
|
|
874
|
+
var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
|
|
875
|
+
statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
|
|
876
|
+
statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
|
|
877
|
+
|
|
878
|
+
var matchesSearch = !searchQuery ||
|
|
879
|
+
title.includes(searchQuery) ||
|
|
880
|
+
tags.some(function(t) { return t.includes(searchQuery); }) ||
|
|
881
|
+
steps.some(function(s) { return s.includes(searchQuery); });
|
|
882
|
+
|
|
883
|
+
var matchesTags = activeTags.size === 0 ||
|
|
884
|
+
tags.some(function(t) { return activeTags.has(t); });
|
|
885
|
+
|
|
886
|
+
var matchesStatus = !activeStatus ||
|
|
887
|
+
status === activeStatus ||
|
|
888
|
+
(activeStatus === 'skipped' && status === 'pending');
|
|
889
|
+
|
|
890
|
+
var visible = matchesSearch && matchesTags && matchesStatus;
|
|
891
|
+
scenario.style.display = visible ? '' : 'none';
|
|
892
|
+
if (visible) { visibleCount++; featureVisible++; }
|
|
807
893
|
});
|
|
808
894
|
|
|
809
|
-
feature.style.display =
|
|
895
|
+
feature.style.display = featureVisible > 0 ? '' : 'none';
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
updateFilterResults(visibleCount, totalCount);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function updateFilterResults(visible, total) {
|
|
902
|
+
var el = document.querySelector('.filter-results');
|
|
903
|
+
if (!el) return;
|
|
904
|
+
var searchInput = document.querySelector('.search-input');
|
|
905
|
+
var isFiltering = activeTags.size > 0 || activeStatus ||
|
|
906
|
+
(searchInput && searchInput.value.trim().length > 0);
|
|
907
|
+
el.style.display = isFiltering ? '' : 'none';
|
|
908
|
+
var vc = el.querySelector('.visible-count');
|
|
909
|
+
var tc = el.querySelector('.total-count');
|
|
910
|
+
if (vc) vc.textContent = visible;
|
|
911
|
+
if (tc) tc.textContent = total;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// Keyboard shortcuts
|
|
915
|
+
function initKeyboardShortcuts() {
|
|
916
|
+
document.addEventListener('keydown', function(e) {
|
|
917
|
+
if (e.key === '/' && !e.ctrlKey && !e.metaKey && e.target.tagName !== 'INPUT') {
|
|
918
|
+
e.preventDefault();
|
|
919
|
+
var input = document.querySelector('.search-input');
|
|
920
|
+
if (input) input.focus();
|
|
921
|
+
}
|
|
810
922
|
});
|
|
811
923
|
}
|
|
812
924
|
|
|
@@ -891,6 +1003,9 @@ function generateScript(options) {
|
|
|
891
1003
|
initCalls.push("initTheme();");
|
|
892
1004
|
}
|
|
893
1005
|
initCalls.push("initSearch();");
|
|
1006
|
+
initCalls.push("initTagFilter();");
|
|
1007
|
+
initCalls.push("initStatusFilter();");
|
|
1008
|
+
initCalls.push("initKeyboardShortcuts();");
|
|
894
1009
|
initCalls.push("initCollapse();");
|
|
895
1010
|
const initScript = `
|
|
896
1011
|
// Initialize on load
|
|
@@ -1374,6 +1489,98 @@ body {
|
|
|
1374
1489
|
}
|
|
1375
1490
|
.summary-card.pending .value { color: var(--pending); }
|
|
1376
1491
|
|
|
1492
|
+
/* ============================================================================
|
|
1493
|
+
Tag Filter Bar
|
|
1494
|
+
============================================================================ */
|
|
1495
|
+
.tag-bar {
|
|
1496
|
+
margin-bottom: 1rem;
|
|
1497
|
+
padding: 0.75rem 1rem;
|
|
1498
|
+
background: var(--card);
|
|
1499
|
+
border: 1px solid var(--border);
|
|
1500
|
+
border-radius: var(--radius);
|
|
1501
|
+
position: sticky;
|
|
1502
|
+
top: 0;
|
|
1503
|
+
z-index: 10;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
.tag-bar-header {
|
|
1507
|
+
display: flex;
|
|
1508
|
+
justify-content: space-between;
|
|
1509
|
+
align-items: center;
|
|
1510
|
+
margin-bottom: 0.5rem;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
.tag-bar-label {
|
|
1514
|
+
font-size: 0.6875rem;
|
|
1515
|
+
text-transform: uppercase;
|
|
1516
|
+
letter-spacing: 0.05em;
|
|
1517
|
+
color: var(--muted-foreground);
|
|
1518
|
+
font-weight: 500;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
.tag-bar-clear {
|
|
1522
|
+
font-size: 0.75rem;
|
|
1523
|
+
font-weight: 500;
|
|
1524
|
+
color: var(--primary);
|
|
1525
|
+
background: none;
|
|
1526
|
+
border: none;
|
|
1527
|
+
cursor: pointer;
|
|
1528
|
+
padding: 0.125rem 0.5rem;
|
|
1529
|
+
border-radius: var(--radius);
|
|
1530
|
+
transition: all 0.15s ease;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
.tag-bar-clear:hover {
|
|
1534
|
+
background: var(--muted);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
.tag-bar-pills {
|
|
1538
|
+
display: flex;
|
|
1539
|
+
flex-wrap: wrap;
|
|
1540
|
+
gap: 0.375rem;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
.tag-pill {
|
|
1544
|
+
font-size: 0.75rem;
|
|
1545
|
+
font-weight: 500;
|
|
1546
|
+
padding: 0.25rem 0.625rem;
|
|
1547
|
+
background: var(--tag-bg);
|
|
1548
|
+
color: var(--tag-color);
|
|
1549
|
+
border: 1px solid var(--tag-border);
|
|
1550
|
+
border-radius: 9999px;
|
|
1551
|
+
font-family: var(--font-mono);
|
|
1552
|
+
cursor: pointer;
|
|
1553
|
+
transition: all 0.15s ease;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
.tag-pill:hover {
|
|
1557
|
+
background: var(--success-border);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
.tag-pill.active {
|
|
1561
|
+
background: var(--primary);
|
|
1562
|
+
color: var(--primary-foreground);
|
|
1563
|
+
border-color: var(--primary);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/* ============================================================================
|
|
1567
|
+
Summary Card Status Filter
|
|
1568
|
+
============================================================================ */
|
|
1569
|
+
.summary-card.status-active {
|
|
1570
|
+
box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
/* ============================================================================
|
|
1574
|
+
Filter Results Counter
|
|
1575
|
+
============================================================================ */
|
|
1576
|
+
.filter-results {
|
|
1577
|
+
text-align: center;
|
|
1578
|
+
font-size: 0.8125rem;
|
|
1579
|
+
color: var(--muted-foreground);
|
|
1580
|
+
margin-bottom: 1rem;
|
|
1581
|
+
font-weight: 500;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1377
1584
|
/* ============================================================================
|
|
1378
1585
|
Feature Sections - shadcn accordion style
|
|
1379
1586
|
============================================================================ */
|
|
@@ -1761,8 +1968,10 @@ body {
|
|
|
1761
1968
|
padding: 0;
|
|
1762
1969
|
}
|
|
1763
1970
|
|
|
1764
|
-
.header-actions
|
|
1765
|
-
|
|
1971
|
+
.header-actions,
|
|
1972
|
+
.tag-bar,
|
|
1973
|
+
.filter-results {
|
|
1974
|
+
display: none !important;
|
|
1766
1975
|
}
|
|
1767
1976
|
|
|
1768
1977
|
.feature,
|
|
@@ -2336,6 +2545,28 @@ function renderSummary(args, _deps) {
|
|
|
2336
2545
|
</div>`;
|
|
2337
2546
|
}
|
|
2338
2547
|
|
|
2548
|
+
// src/formatters/html/renderers/tag-bar.ts
|
|
2549
|
+
function renderTagBar(args, deps) {
|
|
2550
|
+
const { tags, totalScenarios } = args;
|
|
2551
|
+
if (tags.length === 0) return "";
|
|
2552
|
+
const pills = tags.map(
|
|
2553
|
+
(tag) => `<button type="button" class="tag-pill" data-tag="${deps.escapeHtml(tag)}">${deps.escapeHtml(tag)}</button>`
|
|
2554
|
+
).join("\n ");
|
|
2555
|
+
return `
|
|
2556
|
+
<div class="tag-bar">
|
|
2557
|
+
<div class="tag-bar-header">
|
|
2558
|
+
<span class="tag-bar-label">Filter by tag</span>
|
|
2559
|
+
<button type="button" class="tag-bar-clear" style="display:none">Clear</button>
|
|
2560
|
+
</div>
|
|
2561
|
+
<div class="tag-bar-pills">
|
|
2562
|
+
${pills}
|
|
2563
|
+
</div>
|
|
2564
|
+
</div>
|
|
2565
|
+
<div class="filter-results" style="display:none">
|
|
2566
|
+
Showing <span class="visible-count">0</span> of <span class="total-count">${totalScenarios}</span> scenarios
|
|
2567
|
+
</div>`;
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2339
2570
|
// src/formatters/html/renderers/error-box.ts
|
|
2340
2571
|
function renderErrorBox(args, deps) {
|
|
2341
2572
|
const body = args.stack != null ? `${deps.escapeHtml(args.message)}
|
|
@@ -2635,6 +2866,15 @@ function buildBody(args, deps) {
|
|
|
2635
2866
|
deps.summaryDeps
|
|
2636
2867
|
)
|
|
2637
2868
|
);
|
|
2869
|
+
const allTags = [
|
|
2870
|
+
...new Set(run.testCases.flatMap((tc) => tc.tags))
|
|
2871
|
+
].sort();
|
|
2872
|
+
parts.push(
|
|
2873
|
+
deps.renderTagBar(
|
|
2874
|
+
{ tags: allTags, totalScenarios: total },
|
|
2875
|
+
deps.tagBarDeps
|
|
2876
|
+
)
|
|
2877
|
+
);
|
|
2638
2878
|
const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);
|
|
2639
2879
|
for (const [file, testCases] of byFile) {
|
|
2640
2880
|
parts.push(
|
|
@@ -2691,12 +2931,15 @@ function createHtmlFormatter(options = {}) {
|
|
|
2691
2931
|
renderScenario: (args) => renderScenario(args, scenarioDeps),
|
|
2692
2932
|
scenarioDeps
|
|
2693
2933
|
};
|
|
2934
|
+
const tagBarDeps = { escapeHtml };
|
|
2694
2935
|
const bodyDeps = {
|
|
2695
2936
|
renderMetaInfo,
|
|
2696
2937
|
renderSummary,
|
|
2938
|
+
renderTagBar,
|
|
2697
2939
|
renderFeature,
|
|
2698
2940
|
metaDeps: { escapeHtml },
|
|
2699
2941
|
summaryDeps: {},
|
|
2942
|
+
tagBarDeps,
|
|
2700
2943
|
featureDeps
|
|
2701
2944
|
};
|
|
2702
2945
|
return {
|