executable-stories-formatters 0.7.15 → 0.8.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/dist/index.d.cts CHANGED
@@ -319,7 +319,7 @@ interface FormatterOptions {
319
319
  includeTags?: string[];
320
320
  /** Tags to exclude test cases (any match). Applied after includeTags. */
321
321
  excludeTags?: string[];
322
- /** Output formats to generate. Default: ["cucumber-json"] */
322
+ /** Output formats to generate. Default: ["html"] */
323
323
  formats?: OutputFormat[];
324
324
  /** Output directory for generated reports. Default: "reports" */
325
325
  outputDir?: string;
package/dist/index.d.ts CHANGED
@@ -319,7 +319,7 @@ interface FormatterOptions {
319
319
  includeTags?: string[];
320
320
  /** Tags to exclude test cases (any match). Applied after includeTags. */
321
321
  excludeTags?: string[];
322
- /** Output formats to generate. Default: ["cucumber-json"] */
322
+ /** Output formats to generate. Default: ["html"] */
323
323
  formats?: OutputFormat[];
324
324
  /** Output directory for generated reports. Default: "reports" */
325
325
  outputDir?: string;
package/dist/index.js CHANGED
@@ -1321,11 +1321,53 @@ function initKeyboardShortcuts() {
1321
1321
  });
1322
1322
  }
1323
1323
 
1324
- // Collapse/expand functionality
1324
+ // Collapse/expand functionality (persisted in localStorage)
1325
+ var COLLAPSE_KEY = 'es-collapsed-ids';
1326
+
1327
+ function loadCollapseState() {
1328
+ try {
1329
+ var raw = localStorage.getItem(COLLAPSE_KEY);
1330
+ return raw ? new Set(JSON.parse(raw)) : new Set();
1331
+ } catch (e) {
1332
+ return new Set();
1333
+ }
1334
+ }
1335
+
1336
+ function saveCollapseState(set) {
1337
+ try {
1338
+ localStorage.setItem(COLLAPSE_KEY, JSON.stringify(Array.from(set)));
1339
+ } catch (e) { /* quota or disabled */ }
1340
+ }
1341
+
1342
+ function persistCollapse(container) {
1343
+ if (!container || !container.id) return;
1344
+ var state = loadCollapseState();
1345
+ if (container.classList.contains('collapsed')) {
1346
+ state.add(container.id);
1347
+ } else {
1348
+ state.delete(container.id);
1349
+ }
1350
+ saveCollapseState(state);
1351
+ }
1352
+
1325
1353
  function toggleCollapse(header, container) {
1326
1354
  container?.classList.toggle('collapsed');
1327
1355
  const isCollapsed = container?.classList.contains('collapsed');
1328
1356
  header.setAttribute('aria-expanded', !isCollapsed);
1357
+ persistCollapse(container);
1358
+ }
1359
+
1360
+ function restoreCollapseState() {
1361
+ var state = loadCollapseState();
1362
+ if (state.size === 0) return;
1363
+ state.forEach(function(id) {
1364
+ var el = document.getElementById(id);
1365
+ if (!el) return;
1366
+ if (!el.classList.contains('feature') && !el.classList.contains('scenario')) return;
1367
+ el.classList.add('collapsed');
1368
+ var header = el.querySelector('.feature-header, .scenario-header');
1369
+ if (header) header.setAttribute('aria-expanded', 'false');
1370
+ });
1329
1371
  }
1330
1372
 
1331
1373
  function initCollapse() {
@@ -1372,14 +1414,20 @@ function expandAll() {
1372
1414
  const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
1373
1415
  header?.setAttribute('aria-expanded', 'true');
1374
1416
  });
1417
+ saveCollapseState(new Set());
1375
1418
  }
1376
1419
 
1377
1420
  function collapseAll() {
1421
+ var ids = new Set();
1378
1422
  document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {
1379
1423
  el.classList.add('collapsed');
1380
1424
  const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
1381
1425
  header?.setAttribute('aria-expanded', 'false');
1426
+ if (el.id && (el.classList.contains('feature') || el.classList.contains('scenario'))) {
1427
+ ids.add(el.id);
1428
+ }
1382
1429
  });
1430
+ saveCollapseState(ids);
1383
1431
  }
1384
1432
 
1385
1433
  // Detail level toggle
@@ -1506,6 +1554,68 @@ function copyScenarioAsMarkdown(scenarioId) {
1506
1554
  });
1507
1555
  }
1508
1556
 
1557
+ // Copy scenario as Claude-ready prompt (failure investigation context)
1558
+ function copyScenarioAsPrompt(scenarioId) {
1559
+ var scenario = document.getElementById(scenarioId);
1560
+ if (!scenario) return;
1561
+
1562
+ var feature = scenario.closest('.feature');
1563
+ var featureTitle = feature ? (feature.querySelector('.feature-title') || {}).textContent || '' : '';
1564
+ var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';
1565
+ var statusEl = scenario.querySelector('.status-icon');
1566
+ var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
1567
+ statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
1568
+ statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
1569
+ var sourceLink = scenario.querySelector('.source-link');
1570
+ var source = sourceLink ? sourceLink.textContent || '' : '';
1571
+ var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.trim(); });
1572
+ var steps = scenario.querySelectorAll('.step, .step.continuation');
1573
+
1574
+ var lines = [];
1575
+ lines.push('I need help investigating a failing executable-story scenario.');
1576
+ lines.push('');
1577
+ if (featureTitle.trim()) lines.push('Feature: ' + featureTitle.trim());
1578
+ lines.push('Scenario: ' + title.trim());
1579
+ lines.push('Status: ' + status);
1580
+ if (source.trim()) lines.push('Source: ' + source.trim());
1581
+ if (tags.length > 0) lines.push('Tags: ' + tags.join(', '));
1582
+ lines.push('');
1583
+ lines.push('Steps:');
1584
+ steps.forEach(function(step) {
1585
+ var keyword = step.getAttribute('data-keyword') || '';
1586
+ var text = step.getAttribute('data-text') || '';
1587
+ var stepStatusEl = step.querySelector('.step-status');
1588
+ var marker = ' ';
1589
+ if (stepStatusEl) {
1590
+ if (stepStatusEl.classList.contains('status-failed')) marker = 'x ';
1591
+ else if (stepStatusEl.classList.contains('status-passed')) marker = '+ ';
1592
+ else if (stepStatusEl.classList.contains('status-skipped')) marker = '- ';
1593
+ }
1594
+ lines.push(marker + keyword + ' ' + text);
1595
+ });
1596
+
1597
+ var errorBox = scenario.querySelector('.error-message');
1598
+ if (errorBox) {
1599
+ lines.push('');
1600
+ lines.push('Error:');
1601
+ lines.push((errorBox.textContent || '').trim());
1602
+ }
1603
+ var stackBox = scenario.querySelector('.error-stack');
1604
+ if (stackBox) {
1605
+ lines.push('');
1606
+ lines.push('Stack:');
1607
+ lines.push((stackBox.textContent || '').trim());
1608
+ }
1609
+
1610
+ lines.push('');
1611
+ lines.push('Please read the source file, identify the root cause, and propose a fix.');
1612
+
1613
+ var text = lines.join('\\n');
1614
+ navigator.clipboard.writeText(text).then(function() {
1615
+ showCopyToast(scenario);
1616
+ });
1617
+ }
1618
+
1509
1619
  // Hash scroll on load
1510
1620
  function initHashScroll() {
1511
1621
  if (!location.hash) return;
@@ -1675,6 +1785,7 @@ function generateScript(options) {
1675
1785
  initCalls.push("initStatusFilter();");
1676
1786
  initCalls.push("initKeyboardShortcuts();");
1677
1787
  initCalls.push("initCollapse();");
1788
+ initCalls.push("restoreCollapseState();");
1678
1789
  initCalls.push("initDetailLevel();");
1679
1790
  initCalls.push("applyAllFilters();");
1680
1791
  initCalls.push("initHashScroll();");
@@ -3782,6 +3893,33 @@ body {
3782
3893
  color: var(--primary);
3783
3894
  }
3784
3895
 
3896
+ .copy-prompt-btn {
3897
+ display: inline-flex;
3898
+ align-items: center;
3899
+ justify-content: center;
3900
+ width: 1.5rem;
3901
+ height: 1.5rem;
3902
+ border: none;
3903
+ background: none;
3904
+ color: var(--muted-foreground);
3905
+ cursor: pointer;
3906
+ opacity: 0.6;
3907
+ transition: opacity 0.15s ease, transform 0.15s ease;
3908
+ font-size: 0.95rem;
3909
+ padding: 0;
3910
+ flex-shrink: 0;
3911
+ }
3912
+
3913
+ .scenario-header:hover .copy-prompt-btn,
3914
+ .copy-prompt-btn:focus-visible {
3915
+ opacity: 1;
3916
+ }
3917
+
3918
+ .copy-prompt-btn:hover {
3919
+ color: var(--primary);
3920
+ transform: scale(1.15);
3921
+ }
3922
+
3785
3923
  /* ============================================================================
3786
3924
  Keyboard Navigation
3787
3925
  ============================================================================ */
@@ -4019,6 +4157,82 @@ a.toc-title:hover {
4019
4157
  outline-offset: 2px;
4020
4158
  }
4021
4159
 
4160
+ /* ============================================================================
4161
+ Mobile responsive refinements
4162
+ ============================================================================ */
4163
+ @media (max-width: 640px) {
4164
+ .container {
4165
+ padding: 0.875rem;
4166
+ }
4167
+
4168
+ .header {
4169
+ flex-direction: column;
4170
+ align-items: stretch;
4171
+ gap: 0.75rem;
4172
+ }
4173
+
4174
+ .header-actions {
4175
+ flex-wrap: wrap;
4176
+ gap: 0.5rem;
4177
+ }
4178
+
4179
+ .search-input {
4180
+ width: 100%;
4181
+ flex: 1 1 100%;
4182
+ min-width: 0;
4183
+ }
4184
+
4185
+ .header h1 {
4186
+ font-size: 1.25rem;
4187
+ }
4188
+
4189
+ .scenario-header,
4190
+ .feature-header {
4191
+ flex-wrap: wrap;
4192
+ gap: 0.5rem;
4193
+ }
4194
+
4195
+ .scenario-meta {
4196
+ flex-wrap: wrap;
4197
+ }
4198
+
4199
+ .scenario-actions {
4200
+ flex-wrap: wrap;
4201
+ }
4202
+
4203
+ /* Always-visible action buttons on touch (no hover) */
4204
+ .copy-scenario-btn,
4205
+ .copy-prompt-btn,
4206
+ .permalink-anchor {
4207
+ opacity: 1 !important;
4208
+ }
4209
+
4210
+ .summary-card {
4211
+ padding: 0.75rem 0.875rem;
4212
+ }
4213
+
4214
+ .summary-card .value {
4215
+ font-size: 1.5rem;
4216
+ }
4217
+
4218
+ .tag-bar {
4219
+ overflow-x: auto;
4220
+ -webkit-overflow-scrolling: touch;
4221
+ }
4222
+
4223
+ .shortcuts-overlay {
4224
+ padding: 1rem;
4225
+ }
4226
+ }
4227
+
4228
+ @media (hover: none) and (pointer: coarse) {
4229
+ .copy-scenario-btn,
4230
+ .copy-prompt-btn,
4231
+ .permalink-anchor {
4232
+ opacity: 1;
4233
+ }
4234
+ }
4235
+
4022
4236
  `;
4023
4237
 
4024
4238
  // src/formatters/html/themes/default.ts
@@ -13745,6 +13959,7 @@ function renderScenario(args, deps) {
13745
13959
  </div>
13746
13960
  <div class="scenario-actions">
13747
13961
  <button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
13962
+ ${tc.status === "failed" ? `<button class="copy-prompt-btn" onclick="copyScenarioAsPrompt('scenario-${tc.id}')" aria-label="Copy as Claude prompt" title="Copy as AI prompt">\u2728</button>` : ""}
13748
13963
  <button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
13749
13964
  <span class="scenario-duration">${duration}</span>
13750
13965
  </div>
@@ -19540,7 +19755,7 @@ var ReportGenerator = class {
19540
19755
  exclude: options.exclude ?? [],
19541
19756
  includeTags: options.includeTags ?? [],
19542
19757
  excludeTags: options.excludeTags ?? [],
19543
- formats: options.formats ?? ["cucumber-json"],
19758
+ formats: options.formats ?? ["html"],
19544
19759
  outputDir: options.outputDir ?? "reports",
19545
19760
  outputName: options.outputName ?? "index",
19546
19761
  outputNameTimestamp: options.outputNameTimestamp ?? false,