executable-stories-formatters 0.2.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/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 screenshots | `.html` |
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
@@ -1086,6 +1086,18 @@ function initCollapse() {
1086
1086
  }
1087
1087
  });
1088
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
+ });
1089
1101
  }
1090
1102
 
1091
1103
  function expandAll() {
@@ -1289,6 +1301,7 @@ var CSS_STYLES = `
1289
1301
  --tag-bg: hsl(145 55% 95%);
1290
1302
  --tag-color: hsl(145 63% 30%);
1291
1303
  --tag-border: hsl(145 55% 85%);
1304
+ --step-param-color: hsl(220 70% 50%);
1292
1305
 
1293
1306
  /* Accordion/Collapsible styling */
1294
1307
  --accordion-header-hover: hsl(0 0% 98%);
@@ -1347,6 +1360,7 @@ var CSS_STYLES = `
1347
1360
  --tag-bg: hsl(145 35% 14%);
1348
1361
  --tag-color: hsl(145 63% 60%);
1349
1362
  --tag-border: hsl(145 35% 22%);
1363
+ --step-param-color: hsl(220 70% 70%);
1350
1364
 
1351
1365
  /* Accordion/Collapsible styling */
1352
1366
  --accordion-header-hover: hsl(0 0% 11%);
@@ -1395,6 +1409,7 @@ var CSS_STYLES = `
1395
1409
  --tag-bg: hsl(145 35% 14%);
1396
1410
  --tag-color: hsl(145 63% 60%);
1397
1411
  --tag-border: hsl(145 35% 22%);
1412
+ --step-param-color: hsl(220 70% 70%);
1398
1413
  --accordion-header-hover: hsl(0 0% 11%);
1399
1414
  --accordion-content-bg: hsl(0 0% 7%);
1400
1415
  }
@@ -1940,6 +1955,12 @@ body {
1940
1955
  color: var(--foreground);
1941
1956
  }
1942
1957
 
1958
+ .step-param {
1959
+ font-style: italic;
1960
+ font-weight: 500;
1961
+ color: var(--step-param-color);
1962
+ }
1963
+
1943
1964
  .step-duration {
1944
1965
  color: var(--muted-foreground);
1945
1966
  font-size: 0.6875rem;
@@ -2616,6 +2637,7 @@ body {
2616
2637
  font-family: inherit;
2617
2638
  background: none;
2618
2639
  }
2640
+
2619
2641
  `;
2620
2642
 
2621
2643
  // src/formatters/html/renderers/status.ts
@@ -2860,10 +2882,11 @@ function renderStep(step, stepResult, index, deps) {
2860
2882
  const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);
2861
2883
  const stepClass = isContinuation ? "step continuation" : "step";
2862
2884
  const stepDocs = deps.renderDocs(step.docs, "step-docs");
2885
+ const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
2863
2886
  return `<div class="${stepClass}">
2864
2887
  <span class="step-status ${statusClass}">${statusIcon}</span>
2865
2888
  <span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
2866
- <span class="step-text">${deps.escapeHtml(step.text)}</span>
2889
+ <span class="step-text">${textHtml}</span>
2867
2890
  <span class="step-duration">${duration}</span>
2868
2891
  </div>${stepDocs}`;
2869
2892
  }
@@ -2875,6 +2898,30 @@ function renderSteps(args, deps) {
2875
2898
  return `<div class="steps">${stepsHtml}</div>`;
2876
2899
  }
2877
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
+
2878
2925
  // src/formatters/html/renderers/scenario.ts
2879
2926
  function renderScenario(args, deps) {
2880
2927
  const { tc } = args;
@@ -2882,6 +2929,19 @@ function renderScenario(args, deps) {
2882
2929
  const statusClass = `status-${tc.status}`;
2883
2930
  const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
2884
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
+ }
2885
2945
  const storyDocs = deps.renderDocs(tc.story.docs, "story-docs");
2886
2946
  const steps = deps.renderSteps(
2887
2947
  { steps: tc.story.steps, stepResults: tc.stepResults },
@@ -2912,7 +2972,7 @@ function renderScenario(args, deps) {
2912
2972
  <span class="status-icon ${statusClass}">${statusIcon}</span>
2913
2973
  <span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
2914
2974
  </div>
2915
- <div class="scenario-meta">${tags}</div>
2975
+ <div class="scenario-meta">${tags}${traceBadge}</div>
2916
2976
  </div>
2917
2977
  <span class="scenario-duration">${duration}</span>
2918
2978
  </div>
@@ -3046,7 +3106,8 @@ function createHtmlFormatter(options = {}) {
3046
3106
  const stepsDeps = {
3047
3107
  escapeHtml,
3048
3108
  getStatusIcon,
3049
- renderDocs
3109
+ renderDocs,
3110
+ highlightStepParams: (text) => highlightStepParams(text, { escapeHtml })
3050
3111
  };
3051
3112
  const scenarioDeps = {
3052
3113
  escapeHtml,
@@ -3348,6 +3409,7 @@ var MarkdownFormatter = class {
3348
3409
  includeSummaryTable: options.includeSummaryTable ?? false,
3349
3410
  permalinkBaseUrl: options.permalinkBaseUrl,
3350
3411
  ticketUrlTemplate: options.ticketUrlTemplate,
3412
+ traceUrlTemplate: options.traceUrlTemplate,
3351
3413
  includeSourceLinks: options.includeSourceLinks ?? true,
3352
3414
  customRenderers: options.customRenderers
3353
3415
  };
@@ -3570,6 +3632,18 @@ var MarkdownFormatter = class {
3570
3632
  meta.push(`Tickets: ${tc.story.tickets.map((t) => `\`${t}\``).join(", ")}`);
3571
3633
  }
3572
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
+ }
3573
3647
  if (meta.length > 0) {
3574
3648
  lines.push(meta.join(" | "));
3575
3649
  }
@@ -5124,6 +5198,7 @@ var ReportGenerator = class {
5124
5198
  includeSummaryTable: options.markdown?.includeSummaryTable ?? false,
5125
5199
  permalinkBaseUrl: options.markdown?.permalinkBaseUrl,
5126
5200
  ticketUrlTemplate: options.markdown?.ticketUrlTemplate,
5201
+ traceUrlTemplate: options.markdown?.traceUrlTemplate,
5127
5202
  includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
5128
5203
  customRenderers: options.markdown?.customRenderers
5129
5204
  }
@@ -5249,6 +5324,7 @@ var ReportGenerator = class {
5249
5324
  includeSummaryTable: this.options.markdown.includeSummaryTable,
5250
5325
  permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,
5251
5326
  ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,
5327
+ traceUrlTemplate: this.options.markdown.traceUrlTemplate,
5252
5328
  includeSourceLinks: this.options.markdown.includeSourceLinks,
5253
5329
  customRenderers: this.options.markdown.customRenderers
5254
5330
  });