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/dist/index.cjs CHANGED
@@ -65,7 +65,9 @@ __export(src_exports, {
65
65
  readPackageVersion: () => readPackageVersion,
66
66
  resolveAttachment: () => resolveAttachment,
67
67
  resolveAttachments: () => resolveAttachments,
68
+ resolveTraceUrl: () => resolveTraceUrl,
68
69
  slugify: () => slugify,
70
+ tryGetActiveOtelContext: () => tryGetActiveOtelContext,
69
71
  validateCanonicalRun: () => validateCanonicalRun
70
72
  });
71
73
  module.exports = __toCommonJS(src_exports);
@@ -953,6 +955,18 @@ function initCollapse() {
953
955
  }
954
956
  });
955
957
  });
958
+
959
+ document.querySelectorAll('.trace-view-header').forEach(header => {
960
+ header.addEventListener('click', () => {
961
+ toggleCollapse(header, header.closest('.trace-view'));
962
+ });
963
+ header.addEventListener('keydown', (e) => {
964
+ if (e.key === 'Enter' || e.key === ' ') {
965
+ e.preventDefault();
966
+ toggleCollapse(header, header.closest('.trace-view'));
967
+ }
968
+ });
969
+ });
956
970
  }
957
971
 
958
972
  function expandAll() {
@@ -1156,6 +1170,7 @@ var CSS_STYLES = `
1156
1170
  --tag-bg: hsl(145 55% 95%);
1157
1171
  --tag-color: hsl(145 63% 30%);
1158
1172
  --tag-border: hsl(145 55% 85%);
1173
+ --step-param-color: hsl(220 70% 50%);
1159
1174
 
1160
1175
  /* Accordion/Collapsible styling */
1161
1176
  --accordion-header-hover: hsl(0 0% 98%);
@@ -1214,6 +1229,7 @@ var CSS_STYLES = `
1214
1229
  --tag-bg: hsl(145 35% 14%);
1215
1230
  --tag-color: hsl(145 63% 60%);
1216
1231
  --tag-border: hsl(145 35% 22%);
1232
+ --step-param-color: hsl(220 70% 70%);
1217
1233
 
1218
1234
  /* Accordion/Collapsible styling */
1219
1235
  --accordion-header-hover: hsl(0 0% 11%);
@@ -1262,6 +1278,7 @@ var CSS_STYLES = `
1262
1278
  --tag-bg: hsl(145 35% 14%);
1263
1279
  --tag-color: hsl(145 63% 60%);
1264
1280
  --tag-border: hsl(145 35% 22%);
1281
+ --step-param-color: hsl(220 70% 70%);
1265
1282
  --accordion-header-hover: hsl(0 0% 11%);
1266
1283
  --accordion-content-bg: hsl(0 0% 7%);
1267
1284
  }
@@ -1807,6 +1824,12 @@ body {
1807
1824
  color: var(--foreground);
1808
1825
  }
1809
1826
 
1827
+ .step-param {
1828
+ font-style: italic;
1829
+ font-weight: 500;
1830
+ color: var(--step-param-color);
1831
+ }
1832
+
1810
1833
  .step-duration {
1811
1834
  color: var(--muted-foreground);
1812
1835
  font-size: 0.6875rem;
@@ -2483,6 +2506,7 @@ body {
2483
2506
  font-family: inherit;
2484
2507
  background: none;
2485
2508
  }
2509
+
2486
2510
  `;
2487
2511
 
2488
2512
  // src/formatters/html/renderers/status.ts
@@ -2727,10 +2751,11 @@ function renderStep(step, stepResult, index, deps) {
2727
2751
  const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);
2728
2752
  const stepClass = isContinuation ? "step continuation" : "step";
2729
2753
  const stepDocs = deps.renderDocs(step.docs, "step-docs");
2754
+ const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
2730
2755
  return `<div class="${stepClass}">
2731
2756
  <span class="step-status ${statusClass}">${statusIcon}</span>
2732
2757
  <span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
2733
- <span class="step-text">${deps.escapeHtml(step.text)}</span>
2758
+ <span class="step-text">${textHtml}</span>
2734
2759
  <span class="step-duration">${duration}</span>
2735
2760
  </div>${stepDocs}`;
2736
2761
  }
@@ -2742,6 +2767,30 @@ function renderSteps(args, deps) {
2742
2767
  return `<div class="steps">${stepsHtml}</div>`;
2743
2768
  }
2744
2769
 
2770
+ // src/formatters/html/renderers/step-params.ts
2771
+ var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.\-])\d+(?:\.\d+)?(?![\w.\-])/g;
2772
+ function highlightStepParams(text, deps) {
2773
+ const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
2774
+ if (matches.length === 0) {
2775
+ return deps.escapeHtml(text);
2776
+ }
2777
+ let result = "";
2778
+ let lastIndex = 0;
2779
+ for (const match of matches) {
2780
+ const matchStart = match.index;
2781
+ const matchEnd = matchStart + match[0].length;
2782
+ if (matchStart > lastIndex) {
2783
+ result += deps.escapeHtml(text.slice(lastIndex, matchStart));
2784
+ }
2785
+ result += `<span class="step-param">${deps.escapeHtml(match[0])}</span>`;
2786
+ lastIndex = matchEnd;
2787
+ }
2788
+ if (lastIndex < text.length) {
2789
+ result += deps.escapeHtml(text.slice(lastIndex));
2790
+ }
2791
+ return result;
2792
+ }
2793
+
2745
2794
  // src/formatters/html/renderers/scenario.ts
2746
2795
  function renderScenario(args, deps) {
2747
2796
  const { tc } = args;
@@ -2749,6 +2798,19 @@ function renderScenario(args, deps) {
2749
2798
  const statusClass = `status-${tc.status}`;
2750
2799
  const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
2751
2800
  const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
2801
+ const otelMeta = tc.story.meta?.otel;
2802
+ let traceBadge = "";
2803
+ if (otelMeta?.traceId) {
2804
+ const shortId = otelMeta.traceId.slice(0, 16);
2805
+ const traceLink = tc.story.docs?.find(
2806
+ (d) => d.kind === "link" && d.label === "View Trace"
2807
+ );
2808
+ if (traceLink) {
2809
+ 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>`;
2810
+ } else {
2811
+ traceBadge = `<span class="tag trace-tag" title="${deps.escapeHtml(otelMeta.traceId)}">${deps.escapeHtml(shortId)}\u2026</span>`;
2812
+ }
2813
+ }
2752
2814
  const storyDocs = deps.renderDocs(tc.story.docs, "story-docs");
2753
2815
  const steps = deps.renderSteps(
2754
2816
  { steps: tc.story.steps, stepResults: tc.stepResults },
@@ -2779,7 +2841,7 @@ function renderScenario(args, deps) {
2779
2841
  <span class="status-icon ${statusClass}">${statusIcon}</span>
2780
2842
  <span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
2781
2843
  </div>
2782
- <div class="scenario-meta">${tags}</div>
2844
+ <div class="scenario-meta">${tags}${traceBadge}</div>
2783
2845
  </div>
2784
2846
  <span class="scenario-duration">${duration}</span>
2785
2847
  </div>
@@ -2913,7 +2975,8 @@ function createHtmlFormatter(options = {}) {
2913
2975
  const stepsDeps = {
2914
2976
  escapeHtml,
2915
2977
  getStatusIcon,
2916
- renderDocs
2978
+ renderDocs,
2979
+ highlightStepParams: (text) => highlightStepParams(text, { escapeHtml })
2917
2980
  };
2918
2981
  const scenarioDeps = {
2919
2982
  escapeHtml,
@@ -3215,6 +3278,7 @@ var MarkdownFormatter = class {
3215
3278
  includeSummaryTable: options.includeSummaryTable ?? false,
3216
3279
  permalinkBaseUrl: options.permalinkBaseUrl,
3217
3280
  ticketUrlTemplate: options.ticketUrlTemplate,
3281
+ traceUrlTemplate: options.traceUrlTemplate,
3218
3282
  includeSourceLinks: options.includeSourceLinks ?? true,
3219
3283
  customRenderers: options.customRenderers
3220
3284
  };
@@ -3437,6 +3501,18 @@ var MarkdownFormatter = class {
3437
3501
  meta.push(`Tickets: ${tc.story.tickets.map((t) => `\`${t}\``).join(", ")}`);
3438
3502
  }
3439
3503
  }
3504
+ const otelMeta = tc.story.meta?.otel;
3505
+ if (otelMeta?.traceId) {
3506
+ const traceTemplate = this.options.traceUrlTemplate;
3507
+ if (traceTemplate) {
3508
+ const url = traceTemplate.replace(/\{traceId\}/g, otelMeta.traceId);
3509
+ meta.push(
3510
+ `Trace: [${otelMeta.traceId.slice(0, 16)}\u2026](${url})`
3511
+ );
3512
+ } else {
3513
+ meta.push(`Trace: \`${otelMeta.traceId}\``);
3514
+ }
3515
+ }
3440
3516
  if (meta.length > 0) {
3441
3517
  lines.push(meta.join(" | "));
3442
3518
  }
@@ -5379,6 +5455,32 @@ function detectCI4(env = process.env) {
5379
5455
  return void 0;
5380
5456
  }
5381
5457
 
5458
+ // src/utils/otel-detect.ts
5459
+ var import_node_module = require("module");
5460
+ var import_meta2 = {};
5461
+ function getRequire() {
5462
+ const url = import_meta2.url ?? (typeof __filename !== "undefined" ? `file://${__filename}` : void 0);
5463
+ if (!url) throw new Error("Cannot determine module URL");
5464
+ return (0, import_node_module.createRequire)(url);
5465
+ }
5466
+ function tryGetActiveOtelContext() {
5467
+ try {
5468
+ const api = getRequire()("@opentelemetry/api");
5469
+ const span = api.trace?.getActiveSpan?.();
5470
+ if (!span) return void 0;
5471
+ const ctx = span.spanContext?.();
5472
+ if (!ctx?.traceId || ctx.traceId === "00000000000000000000000000000000")
5473
+ return void 0;
5474
+ return { traceId: ctx.traceId, spanId: ctx.spanId };
5475
+ } catch {
5476
+ return void 0;
5477
+ }
5478
+ }
5479
+ function resolveTraceUrl(template, traceId) {
5480
+ if (!template) return void 0;
5481
+ return template.replace(/\{traceId\}/g, traceId);
5482
+ }
5483
+
5382
5484
  // src/index.ts
5383
5485
  var FORMAT_EXTENSIONS = {
5384
5486
  markdown: ".md",
@@ -5569,6 +5671,7 @@ var ReportGenerator = class {
5569
5671
  includeSummaryTable: options.markdown?.includeSummaryTable ?? false,
5570
5672
  permalinkBaseUrl: options.markdown?.permalinkBaseUrl,
5571
5673
  ticketUrlTemplate: options.markdown?.ticketUrlTemplate,
5674
+ traceUrlTemplate: options.markdown?.traceUrlTemplate,
5572
5675
  includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
5573
5676
  customRenderers: options.markdown?.customRenderers
5574
5677
  }
@@ -5694,6 +5797,7 @@ var ReportGenerator = class {
5694
5797
  includeSummaryTable: this.options.markdown.includeSummaryTable,
5695
5798
  permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,
5696
5799
  ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,
5800
+ traceUrlTemplate: this.options.markdown.traceUrlTemplate,
5697
5801
  includeSourceLinks: this.options.markdown.includeSourceLinks,
5698
5802
  customRenderers: this.options.markdown.customRenderers
5699
5803
  });
@@ -5756,7 +5860,9 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
5756
5860
  readPackageVersion,
5757
5861
  resolveAttachment,
5758
5862
  resolveAttachments,
5863
+ resolveTraceUrl,
5759
5864
  slugify,
5865
+ tryGetActiveOtelContext,
5760
5866
  validateCanonicalRun
5761
5867
  });
5762
5868
  //# sourceMappingURL=index.cjs.map