executable-stories-playwright 8.1.0 → 8.1.3

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.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as _playwright_test from '@playwright/test';
2
2
  import { TestInfo, PlaywrightTestArgs, PlaywrightTestOptions } from '@playwright/test';
3
- import { StepKeyword } from 'executable-stories-formatters';
4
- export { DocEntry, StepKeyword, StoryMeta, StoryStep } from 'executable-stories-formatters';
3
+ import * as executable_stories_formatters from 'executable-stories-formatters';
4
+ import { DocEntry, StepKeyword } from 'executable-stories-formatters';
5
+ export { DocEntry, NormalizedTicket, StepKeyword, StoryMeta, StoryStep } from 'executable-stories-formatters';
5
6
 
6
7
  /**
7
8
  * Type definitions for executable-stories-playwright.
@@ -10,6 +11,11 @@ export { DocEntry, StepKeyword, StoryMeta, StoryStep } from 'executable-stories-
10
11
  * Playwright-specific types are defined here.
11
12
  */
12
13
 
14
+ /** A ticket reference: either a plain string ID or an object with id and optional url */
15
+ type TicketInput = string | {
16
+ id: string;
17
+ url?: string;
18
+ };
13
19
  interface KvOptions {
14
20
  label: string;
15
21
  value: unknown;
@@ -65,7 +71,7 @@ interface StoryDocs {
65
71
  /** Options for story.init(). */
66
72
  interface StoryOptions {
67
73
  tags?: string[];
68
- ticket?: string | string[];
74
+ ticket?: TicketInput | TicketInput[];
69
75
  meta?: Record<string, unknown>;
70
76
  /** URL template for OTel trace links. Uses {traceId} placeholder. Also settable via OTEL_TRACE_URL_TEMPLATE env var. */
71
77
  traceUrlTemplate?: string;
@@ -127,67 +133,80 @@ declare const story: {
127
133
  init: typeof init;
128
134
  given: {
129
135
  (text: string, docs?: StoryDocs): void;
136
+ (text: string, children: DocEntry[]): void;
130
137
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
131
138
  };
132
139
  when: {
133
140
  (text: string, docs?: StoryDocs): void;
141
+ (text: string, children: DocEntry[]): void;
134
142
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
135
143
  };
136
144
  then: {
137
145
  (text: string, docs?: StoryDocs): void;
146
+ (text: string, children: DocEntry[]): void;
138
147
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
139
148
  };
140
149
  and: {
141
150
  (text: string, docs?: StoryDocs): void;
151
+ (text: string, children: DocEntry[]): void;
142
152
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
143
153
  };
144
154
  but: {
145
155
  (text: string, docs?: StoryDocs): void;
156
+ (text: string, children: DocEntry[]): void;
146
157
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
147
158
  };
148
159
  arrange: {
149
160
  (text: string, docs?: StoryDocs): void;
161
+ (text: string, children: DocEntry[]): void;
150
162
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
151
163
  };
152
164
  act: {
153
165
  (text: string, docs?: StoryDocs): void;
166
+ (text: string, children: DocEntry[]): void;
154
167
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
155
168
  };
156
169
  assert: {
157
170
  (text: string, docs?: StoryDocs): void;
171
+ (text: string, children: DocEntry[]): void;
158
172
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
159
173
  };
160
174
  setup: {
161
175
  (text: string, docs?: StoryDocs): void;
176
+ (text: string, children: DocEntry[]): void;
162
177
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
163
178
  };
164
179
  context: {
165
180
  (text: string, docs?: StoryDocs): void;
181
+ (text: string, children: DocEntry[]): void;
166
182
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
167
183
  };
168
184
  execute: {
169
185
  (text: string, docs?: StoryDocs): void;
186
+ (text: string, children: DocEntry[]): void;
170
187
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
171
188
  };
172
189
  action: {
173
190
  (text: string, docs?: StoryDocs): void;
191
+ (text: string, children: DocEntry[]): void;
174
192
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
175
193
  };
176
194
  verify: {
177
195
  (text: string, docs?: StoryDocs): void;
196
+ (text: string, children: DocEntry[]): void;
178
197
  <T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;
179
198
  };
180
- note(text: string): void;
181
- tag(name: string | string[]): void;
182
- kv(options: KvOptions): void;
183
- json(options: JsonOptions): void;
184
- code(options: CodeOptions): void;
185
- table(options: TableOptions): void;
186
- link(options: LinkOptions): void;
187
- section(options: SectionOptions): void;
188
- mermaid(options: MermaidOptions): void;
189
- screenshot(options: ScreenshotOptions): void;
190
- custom(options: CustomOptions): void;
199
+ note(text: string, children?: DocEntry[]): DocEntry;
200
+ tag(name: string | string[], children?: DocEntry[]): DocEntry;
201
+ kv(options: KvOptions, children?: DocEntry[]): DocEntry;
202
+ json(options: JsonOptions, children?: DocEntry[]): DocEntry;
203
+ code(options: CodeOptions, children?: DocEntry[]): DocEntry;
204
+ table(options: TableOptions, children?: DocEntry[]): DocEntry;
205
+ link(options: LinkOptions, children?: DocEntry[]): DocEntry;
206
+ section(options: SectionOptions, children?: DocEntry[]): DocEntry;
207
+ mermaid(options: MermaidOptions, children?: DocEntry[]): DocEntry;
208
+ screenshot(options: ScreenshotOptions, children?: DocEntry[]): DocEntry;
209
+ custom(options: CustomOptions, children?: DocEntry[]): DocEntry;
191
210
  attach: typeof playwrightAttach;
192
211
  attachSpans(spans: ReadonlyArray<Record<string, unknown>>): void;
193
212
  startTimer(): number;
@@ -199,23 +218,28 @@ type Story = typeof story;
199
218
 
200
219
  declare const given: {
201
220
  (text: string, docs?: StoryDocs): void;
221
+ (text: string, children: executable_stories_formatters.DocEntry[]): void;
202
222
  <T>(text: string, body: (fixtures: _playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & Record<string, unknown>) => T): T;
203
223
  };
204
224
  declare const when: {
205
225
  (text: string, docs?: StoryDocs): void;
226
+ (text: string, children: executable_stories_formatters.DocEntry[]): void;
206
227
  <T>(text: string, body: (fixtures: _playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & Record<string, unknown>) => T): T;
207
228
  };
208
229
  declare const then: {
209
230
  (text: string, docs?: StoryDocs): void;
231
+ (text: string, children: executable_stories_formatters.DocEntry[]): void;
210
232
  <T>(text: string, body: (fixtures: _playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & Record<string, unknown>) => T): T;
211
233
  };
212
234
  declare const and: {
213
235
  (text: string, docs?: StoryDocs): void;
236
+ (text: string, children: executable_stories_formatters.DocEntry[]): void;
214
237
  <T>(text: string, body: (fixtures: _playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & Record<string, unknown>) => T): T;
215
238
  };
216
239
  declare const but: {
217
240
  (text: string, docs?: StoryDocs): void;
241
+ (text: string, children: executable_stories_formatters.DocEntry[]): void;
218
242
  <T>(text: string, body: (fixtures: _playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & Record<string, unknown>) => T): T;
219
243
  };
220
244
 
221
- export { type CodeOptions, type CustomOptions, type JsonOptions, type KvOptions, type LinkOptions, type MermaidOptions, type ScreenshotOptions, type SectionOptions, type Story, type StoryDocs, type StoryOptions, type TableOptions, and, but, given, story, then, when };
245
+ export { type CodeOptions, type CustomOptions, type JsonOptions, type KvOptions, type LinkOptions, type MermaidOptions, type ScreenshotOptions, type SectionOptions, type Story, type StoryDocs, type StoryOptions, type TableOptions, type TicketInput, and, but, given, story, then, when };
package/dist/index.js CHANGED
@@ -17,7 +17,8 @@ function getContext() {
17
17
  }
18
18
  function normalizeTickets(ticket) {
19
19
  if (!ticket) return void 0;
20
- return Array.isArray(ticket) ? ticket : [ticket];
20
+ const arr = Array.isArray(ticket) ? ticket : [ticket];
21
+ return arr.map((t) => typeof t === "string" ? { id: t } : t);
21
22
  }
22
23
  function convertStoryDocsToEntries(docs) {
23
24
  const entries = [];
@@ -102,8 +103,17 @@ function convertStoryDocsToEntries(docs) {
102
103
  }
103
104
  return entries;
104
105
  }
105
- function attachDoc(entry) {
106
+ function attachDoc(entry, children) {
106
107
  const ctx = getContext();
108
+ if (children && children.length > 0) {
109
+ entry.children = children;
110
+ const childSet = new Set(children);
111
+ const filterDocs = (docs) => docs.filter((d) => !childSet.has(d));
112
+ ctx.meta.docs = filterDocs(ctx.meta.docs ?? []);
113
+ for (const step of ctx.meta.steps) {
114
+ if (step.docs) step.docs = filterDocs(step.docs);
115
+ }
116
+ }
107
117
  if (ctx.currentStep) {
108
118
  ctx.currentStep.docs ??= [];
109
119
  ctx.currentStep.docs.push(entry);
@@ -111,6 +121,8 @@ function attachDoc(entry) {
111
121
  ctx.meta.docs ??= [];
112
122
  ctx.meta.docs.push(entry);
113
123
  }
124
+ syncAnnotationToTest();
125
+ return entry;
114
126
  }
115
127
  function extractSuitePath(testInfo) {
116
128
  const titlePath = testInfo.titlePath;
@@ -124,17 +136,37 @@ function createStepMarker(keyword) {
124
136
  function stepMarker(text, docsOrBody) {
125
137
  const ctx = getContext();
126
138
  const isCallback = typeof docsOrBody === "function";
139
+ const isChildrenArray = Array.isArray(docsOrBody);
127
140
  const resolvedKeyword = (keyword === "Given" || keyword === "When" || keyword === "Then") && ctx.meta.steps.some((s) => s.keyword === keyword) ? "And" : keyword;
141
+ let stepDocs = [];
142
+ if (!isCallback && !isChildrenArray && docsOrBody) {
143
+ stepDocs = convertStoryDocsToEntries(docsOrBody);
144
+ }
128
145
  const step = {
129
146
  id: `step-${ctx.stepCounter++}`,
130
147
  keyword: resolvedKeyword,
131
148
  text,
132
- docs: !isCallback && docsOrBody ? convertStoryDocsToEntries(docsOrBody) : [],
149
+ docs: stepDocs,
133
150
  ...isCallback ? { wrapped: true } : {}
134
151
  };
135
152
  ctx.meta.steps.push(step);
136
153
  ctx.currentStep = step;
137
154
  syncAnnotationToTest();
155
+ if (isChildrenArray) {
156
+ const children = docsOrBody;
157
+ if (children.length > 0) {
158
+ const childSet = new Set(children);
159
+ ctx.meta.docs = (ctx.meta.docs ?? []).filter((d) => !childSet.has(d));
160
+ for (const prevStep of ctx.meta.steps) {
161
+ if (prevStep !== step && prevStep.docs) {
162
+ prevStep.docs = prevStep.docs.filter((d) => !childSet.has(d));
163
+ }
164
+ }
165
+ step.docs = [...step.docs ?? [], ...children];
166
+ }
167
+ syncAnnotationToTest();
168
+ return;
169
+ }
138
170
  if (!isCallback) return;
139
171
  const body = docsOrBody;
140
172
  const start = performance.now();
@@ -210,7 +242,7 @@ function init(first, second, third) {
210
242
  if (options?.tags?.length) span.setAttribute("story.tags", options.tags);
211
243
  if (options?.ticket) {
212
244
  const tickets = Array.isArray(options.ticket) ? options.ticket : [options.ticket];
213
- span.setAttribute("story.tickets", tickets);
245
+ span.setAttribute("story.tickets", tickets.map((t) => typeof t === "string" ? t : t.id));
214
246
  }
215
247
  }
216
248
  } catch {
@@ -319,51 +351,40 @@ var story = {
319
351
  action: createStepMarker("When"),
320
352
  verify: createStepMarker("Then"),
321
353
  // Standalone doc methods
322
- note(text) {
323
- attachDoc({ kind: "note", text, phase: "runtime" });
324
- syncAnnotationToTest();
354
+ note(text, children) {
355
+ return attachDoc({ kind: "note", text, phase: "runtime" }, children);
325
356
  },
326
- tag(name) {
357
+ tag(name, children) {
327
358
  const names = Array.isArray(name) ? name : [name];
328
- attachDoc({ kind: "tag", names, phase: "runtime" });
329
- syncAnnotationToTest();
359
+ return attachDoc({ kind: "tag", names, phase: "runtime" }, children);
330
360
  },
331
- kv(options) {
332
- attachDoc({ kind: "kv", label: options.label, value: options.value, phase: "runtime" });
333
- syncAnnotationToTest();
361
+ kv(options, children) {
362
+ return attachDoc({ kind: "kv", label: options.label, value: options.value, phase: "runtime" }, children);
334
363
  },
335
- json(options) {
364
+ json(options, children) {
336
365
  const content = JSON.stringify(options.value, null, 2);
337
- attachDoc({ kind: "code", label: options.label, content, lang: "json", phase: "runtime" });
338
- syncAnnotationToTest();
366
+ return attachDoc({ kind: "code", label: options.label, content, lang: "json", phase: "runtime" }, children);
339
367
  },
340
- code(options) {
341
- attachDoc({ kind: "code", label: options.label, content: options.content, lang: options.lang, phase: "runtime" });
342
- syncAnnotationToTest();
368
+ code(options, children) {
369
+ return attachDoc({ kind: "code", label: options.label, content: options.content, lang: options.lang, phase: "runtime" }, children);
343
370
  },
344
- table(options) {
345
- attachDoc({ kind: "table", label: options.label, columns: options.columns, rows: options.rows, phase: "runtime" });
346
- syncAnnotationToTest();
371
+ table(options, children) {
372
+ return attachDoc({ kind: "table", label: options.label, columns: options.columns, rows: options.rows, phase: "runtime" }, children);
347
373
  },
348
- link(options) {
349
- attachDoc({ kind: "link", label: options.label, url: options.url, phase: "runtime" });
350
- syncAnnotationToTest();
374
+ link(options, children) {
375
+ return attachDoc({ kind: "link", label: options.label, url: options.url, phase: "runtime" }, children);
351
376
  },
352
- section(options) {
353
- attachDoc({ kind: "section", title: options.title, markdown: options.markdown, phase: "runtime" });
354
- syncAnnotationToTest();
377
+ section(options, children) {
378
+ return attachDoc({ kind: "section", title: options.title, markdown: options.markdown, phase: "runtime" }, children);
355
379
  },
356
- mermaid(options) {
357
- attachDoc({ kind: "mermaid", code: options.code, title: options.title, phase: "runtime" });
358
- syncAnnotationToTest();
380
+ mermaid(options, children) {
381
+ return attachDoc({ kind: "mermaid", code: options.code, title: options.title, phase: "runtime" }, children);
359
382
  },
360
- screenshot(options) {
361
- attachDoc({ kind: "screenshot", path: options.path, alt: options.alt, phase: "runtime" });
362
- syncAnnotationToTest();
383
+ screenshot(options, children) {
384
+ return attachDoc({ kind: "screenshot", path: options.path, alt: options.alt, phase: "runtime" }, children);
363
385
  },
364
- custom(options) {
365
- attachDoc({ kind: "custom", type: options.type, data: options.data, phase: "runtime" });
366
- syncAnnotationToTest();
386
+ custom(options, children) {
387
+ return attachDoc({ kind: "custom", type: options.type, data: options.data, phase: "runtime" }, children);
367
388
  },
368
389
  // Attachments
369
390
  attach: playwrightAttach,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/story-api.ts","../src/index.ts"],"sourcesContent":["/**\n * Playwright story.* API for executable-stories.\n *\n * Uses native Playwright test() with opt-in documentation:\n *\n * @example\n * ```ts\n * import { test, expect } from '@playwright/test';\n * import { story } from 'executable-stories-playwright';\n *\n * test.describe('Calculator', () => {\n * test('adds two numbers', async ({ page }, testInfo) => {\n * story.init(testInfo);\n *\n * story.given('two numbers 5 and 3');\n * const a = 5;\n * const b = 3;\n *\n * story.when('I add them together');\n * const result = a + b;\n *\n * story.then('the result is 8');\n * expect(result).toBe(8);\n * });\n * });\n * ```\n */\n\nimport { createRequire } from 'node:module';\nimport type { TestInfo, PlaywrightTestArgs, PlaywrightTestOptions } from '@playwright/test';\nimport {\n tryGetActiveOtelContext,\n resolveTraceUrl,\n} from 'executable-stories-formatters';\nimport type {\n StepKeyword,\n StoryMeta,\n StoryStep,\n DocEntry,\n} from './types';\nimport type {\n StoryDocs,\n StoryOptions,\n AttachmentOptions,\n ScopedAttachment,\n KvOptions,\n JsonOptions,\n CodeOptions,\n TableOptions,\n LinkOptions,\n SectionOptions,\n MermaidOptions,\n ScreenshotOptions,\n CustomOptions,\n} from './types';\n\n// Re-export types for consumers\nexport type {\n StoryMeta,\n StoryStep,\n DocEntry,\n StepKeyword,\n StoryDocs,\n StoryOptions,\n AttachmentOptions,\n} from './types';\n\n// ============================================================================\n// Internal types\n// ============================================================================\n\n/** Fixture type for step callbacks: Playwright test args + options; custom extend() fixtures as unknown. */\ntype PlaywrightFixtures = PlaywrightTestArgs & PlaywrightTestOptions & Record<string, unknown>;\n\ninterface TimerEntry {\n start: number;\n stepIndex?: number;\n stepId?: string;\n consumed: boolean;\n}\n\ninterface StoryContext {\n meta: StoryMeta;\n currentStep: StoryStep | null;\n stepCounter: number;\n attachments: ScopedAttachment[];\n activeTimers: Map<number, TimerEntry>;\n timerCounter: number;\n fixtures?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Playwright-specific context\n// ============================================================================\n\n/** Active story context - set by story.init() */\nlet activeContext: StoryContext | null = null;\n\n/** Reference to testInfo for attaching metadata */\nlet activeTestInfo: TestInfo | null = null;\n\n/** Counter to track source order of stories (increments on each story.init call) */\nlet sourceOrderCounter = 0;\n\n/**\n * Get the current story context. Throws if story.init() wasn't called.\n */\nfunction getContext(): StoryContext {\n if (!activeContext) {\n throw new Error(\n \"story.init(testInfo) must be called first. Use: test('name', async ({ page }, testInfo) => { story.init(testInfo); ... });\",\n );\n }\n return activeContext;\n}\n\n// ============================================================================\n// Helper functions (inlined from core)\n// ============================================================================\n\nfunction normalizeTickets(ticket: string | string[] | undefined): string[] | undefined {\n if (!ticket) return undefined;\n return Array.isArray(ticket) ? ticket : [ticket];\n}\n\nfunction convertStoryDocsToEntries(docs: StoryDocs): DocEntry[] {\n const entries: DocEntry[] = [];\n\n if (docs.note) {\n entries.push({ kind: 'note', text: docs.note, phase: 'runtime' });\n }\n if (docs.tag) {\n const names = Array.isArray(docs.tag) ? docs.tag : [docs.tag];\n entries.push({ kind: 'tag', names, phase: 'runtime' });\n }\n if (docs.kv) {\n for (const [label, value] of Object.entries(docs.kv)) {\n entries.push({ kind: 'kv', label, value, phase: 'runtime' });\n }\n }\n if (docs.code) {\n entries.push({\n kind: 'code',\n label: docs.code.label,\n content: docs.code.content,\n lang: docs.code.lang,\n phase: 'runtime',\n });\n }\n if (docs.json) {\n entries.push({\n kind: 'code',\n label: docs.json.label,\n content: JSON.stringify(docs.json.value, null, 2),\n lang: 'json',\n phase: 'runtime',\n });\n }\n if (docs.table) {\n entries.push({\n kind: 'table',\n label: docs.table.label,\n columns: docs.table.columns,\n rows: docs.table.rows,\n phase: 'runtime',\n });\n }\n if (docs.link) {\n entries.push({\n kind: 'link',\n label: docs.link.label,\n url: docs.link.url,\n phase: 'runtime',\n });\n }\n if (docs.section) {\n entries.push({\n kind: 'section',\n title: docs.section.title,\n markdown: docs.section.markdown,\n phase: 'runtime',\n });\n }\n if (docs.mermaid) {\n entries.push({\n kind: 'mermaid',\n code: docs.mermaid.code,\n title: docs.mermaid.title,\n phase: 'runtime',\n });\n }\n if (docs.screenshot) {\n entries.push({\n kind: 'screenshot',\n path: docs.screenshot.path,\n alt: docs.screenshot.alt,\n phase: 'runtime',\n });\n }\n if (docs.custom) {\n entries.push({\n kind: 'custom',\n type: docs.custom.type,\n data: docs.custom.data,\n phase: 'runtime',\n });\n }\n\n return entries;\n}\n\nfunction attachDoc(entry: DocEntry): void {\n const ctx = getContext();\n if (ctx.currentStep) {\n ctx.currentStep.docs ??= [];\n ctx.currentStep.docs.push(entry);\n } else {\n ctx.meta.docs ??= [];\n ctx.meta.docs.push(entry);\n }\n}\n\n// ============================================================================\n// Suite path extraction\n// ============================================================================\n\n/**\n * Extract the suite path from testInfo.titlePath.\n * Playwright's titlePath includes: [projectName, ...describeTitles, testTitle]\n * We want just the describe titles (excluding project and test name).\n */\nfunction extractSuitePath(testInfo: TestInfo): string[] | undefined {\n const titlePath = testInfo.titlePath;\n if (titlePath.length <= 2) {\n return undefined;\n }\n const suitePath = titlePath.slice(1, -1);\n return suitePath.length > 0 ? suitePath : undefined;\n}\n\n// ============================================================================\n// Step markers\n// ============================================================================\n\nfunction createStepMarker(keyword: StepKeyword) {\n function stepMarker(text: string, docs?: StoryDocs): void;\n function stepMarker<T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function stepMarker<T>(text: string, docsOrBody?: StoryDocs | ((...args: any[]) => T)): T | void {\n const ctx = getContext();\n const isCallback = typeof docsOrBody === 'function';\n\n const resolvedKeyword: StepKeyword =\n (keyword === 'Given' || keyword === 'When' || keyword === 'Then') &&\n ctx.meta.steps.some((s) => s.keyword === keyword)\n ? 'And'\n : keyword;\n\n const step: StoryStep = {\n id: `step-${ctx.stepCounter++}`,\n keyword: resolvedKeyword,\n text,\n docs: (!isCallback && docsOrBody) ? convertStoryDocsToEntries(docsOrBody) : [],\n ...(isCallback ? { wrapped: true } : {}),\n };\n\n ctx.meta.steps.push(step);\n ctx.currentStep = step;\n syncAnnotationToTest();\n\n if (!isCallback) return;\n\n const body = docsOrBody as (fixtures?: PlaywrightFixtures) => T;\n const start = performance.now();\n\n try {\n const result = ctx.fixtures !== undefined ? body(ctx.fixtures as PlaywrightFixtures) : body();\n if (result instanceof Promise) {\n return result.then(\n (val) => { step.durationMs = performance.now() - start; syncAnnotationToTest(); return val; },\n (err) => { step.durationMs = performance.now() - start; syncAnnotationToTest(); throw err; },\n ) as T;\n }\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n return result;\n } catch (err) {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n throw err;\n }\n }\n return stepMarker;\n}\n\n// ============================================================================\n// story.init() - Playwright-specific\n// ============================================================================\n\nfunction isTestInfo(x: unknown): x is TestInfo {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'title' in x &&\n 'annotations' in x &&\n Array.isArray((x as TestInfo).annotations)\n );\n}\n\n/** init(testInfo) or init(fixtures, testInfo) or init(testInfo, { fixtures }). */\nfunction init(\n first: TestInfo | unknown,\n second?: StoryOptions | TestInfo,\n third?: StoryOptions,\n): void {\n let testInfo: TestInfo;\n let options: StoryOptions | undefined;\n let fixtures: unknown;\n\n if (second !== undefined && isTestInfo(second)) {\n fixtures = first;\n testInfo = second;\n options = third;\n } else {\n testInfo = first as TestInfo;\n options = second;\n fixtures = options?.fixtures;\n }\n\n const meta: StoryMeta = {\n scenario: testInfo.title,\n steps: [],\n suitePath: extractSuitePath(testInfo),\n tags: options?.tags,\n tickets: normalizeTickets(options?.ticket),\n meta: options?.meta,\n sourceOrder: sourceOrderCounter++,\n };\n\n // OTel bridge: detect active span, flow data bidirectionally\n const otelCtx = tryGetActiveOtelContext();\n if (otelCtx) {\n // OTel -> Story: capture traceId in structured meta\n meta.meta = { ...meta.meta, otel: { traceId: otelCtx.traceId, spanId: otelCtx.spanId } };\n\n // OTel -> Story: inject human-readable doc entries\n meta.docs = meta.docs ?? [];\n meta.docs.push({ kind: 'kv', label: 'Trace ID', value: otelCtx.traceId, phase: 'runtime' });\n\n const template = options?.traceUrlTemplate ?? process.env.OTEL_TRACE_URL_TEMPLATE;\n const url = resolveTraceUrl(template, otelCtx.traceId);\n if (url) {\n meta.docs.push({ kind: 'link', label: 'View Trace', url, phase: 'runtime' });\n }\n\n // Story -> OTel: enrich active span with story attributes\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const reqUrl = import.meta.url\n ?? (typeof __filename !== 'undefined' ? `file://${__filename}` : undefined);\n const req = createRequire(reqUrl!);\n const api = req('@opentelemetry/api');\n const span = api.trace?.getActiveSpan?.();\n if (span) {\n span.setAttribute('story.scenario', testInfo.title);\n if (options?.tags?.length) span.setAttribute('story.tags', options.tags);\n if (options?.ticket) {\n const tickets = Array.isArray(options.ticket) ? options.ticket : [options.ticket];\n span.setAttribute('story.tickets', tickets);\n }\n }\n } catch { /* OTel not available */ }\n }\n\n testInfo.annotations.push({\n type: 'story-meta',\n description: JSON.stringify(meta),\n });\n\n activeContext = {\n meta,\n currentStep: null,\n stepCounter: 0,\n attachments: [],\n activeTimers: new Map(),\n timerCounter: 0,\n fixtures: fixtures as Record<string, unknown> | undefined,\n };\n activeTestInfo = testInfo;\n}\n\n/**\n * Update the story-meta annotation on testInfo with the current meta (including steps).\n * Called after each step/doc so the reporter sees the full story in onTestEnd.\n */\nfunction syncAnnotationToTest(): void {\n if (!activeTestInfo || !activeContext) return;\n const annotation = activeTestInfo.annotations.find(\n (a) => a.type === 'story-meta',\n );\n if (annotation) {\n annotation.description = JSON.stringify(activeContext.meta);\n }\n}\n\n// ============================================================================\n// story.fn() and story.expect()\n// ============================================================================\n\n/**\n * Wrap a function as a step with timing and error capture.\n * Records the step with `wrapped: true` and `durationMs`.\n */\nfunction fn<T>(keyword: StepKeyword, text: string, body: (fixtures: PlaywrightFixtures) => T): T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction fn<T>(keyword: StepKeyword, text: string, body: (...args: any[]) => T): T {\n const ctx = getContext();\n const resolvedKeyword: StepKeyword =\n (keyword === 'Given' || keyword === 'When' || keyword === 'Then') &&\n ctx.meta.steps.some((s) => s.keyword === keyword)\n ? 'And'\n : keyword;\n const step: StoryStep = {\n id: `step-${ctx.stepCounter++}`,\n keyword: resolvedKeyword,\n text,\n docs: [],\n wrapped: true,\n };\n ctx.meta.steps.push(step);\n ctx.currentStep = step;\n syncAnnotationToTest();\n\n const start = performance.now();\n try {\n const result = ctx.fixtures !== undefined ? body(ctx.fixtures as PlaywrightFixtures) : body();\n if (result instanceof Promise) {\n return result.then(\n (val) => {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n return val;\n },\n (err) => {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n throw err;\n },\n ) as T;\n }\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n return result;\n } catch (err) {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n throw err;\n }\n}\n\n/**\n * Wrap an assertion as a Then step with timing and error capture.\n * Shorthand for `story.fn('Then', text, body)`.\n */\nfunction storyExpect<T>(text: string, body: () => T): T {\n return fn('Then', text, body);\n}\n\n// ============================================================================\n// Playwright-specific attach\n// ============================================================================\n\nfunction playwrightAttach(options: AttachmentOptions): void {\n const ctx = getContext();\n const stepIndex = ctx.currentStep\n ? ctx.meta.steps.indexOf(ctx.currentStep)\n : undefined;\n ctx.attachments.push({\n ...options,\n stepId: ctx.currentStep?.id,\n });\n syncAnnotationToTest();\n\n if (activeTestInfo) {\n const attachOptions: { name: string; contentType: string; path?: string; body?: string | Buffer } = {\n name: options.name,\n contentType: options.mediaType,\n };\n if (options.path) attachOptions.path = options.path;\n if (options.body) attachOptions.body = options.body;\n activeTestInfo.attach(options.name, attachOptions);\n }\n}\n\n// ============================================================================\n// Export story object\n// ============================================================================\n\nexport const story = {\n init,\n\n // BDD step markers\n given: createStepMarker('Given'),\n when: createStepMarker('When'),\n then: createStepMarker('Then'),\n and: createStepMarker('And'),\n but: createStepMarker('But'),\n\n // AAA pattern aliases\n arrange: createStepMarker('Given'),\n act: createStepMarker('When'),\n assert: createStepMarker('Then'),\n\n // Additional aliases\n setup: createStepMarker('Given'),\n context: createStepMarker('Given'),\n execute: createStepMarker('When'),\n action: createStepMarker('When'),\n verify: createStepMarker('Then'),\n\n // Standalone doc methods\n note(text: string): void {\n attachDoc({ kind: 'note', text, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n tag(name: string | string[]): void {\n const names = Array.isArray(name) ? name : [name];\n attachDoc({ kind: 'tag', names, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n kv(options: KvOptions): void {\n attachDoc({ kind: 'kv', label: options.label, value: options.value, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n json(options: JsonOptions): void {\n const content = JSON.stringify(options.value, null, 2);\n attachDoc({ kind: 'code', label: options.label, content, lang: 'json', phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n code(options: CodeOptions): void {\n attachDoc({ kind: 'code', label: options.label, content: options.content, lang: options.lang, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n table(options: TableOptions): void {\n attachDoc({ kind: 'table', label: options.label, columns: options.columns, rows: options.rows, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n link(options: LinkOptions): void {\n attachDoc({ kind: 'link', label: options.label, url: options.url, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n section(options: SectionOptions): void {\n attachDoc({ kind: 'section', title: options.title, markdown: options.markdown, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n mermaid(options: MermaidOptions): void {\n attachDoc({ kind: 'mermaid', code: options.code, title: options.title, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n screenshot(options: ScreenshotOptions): void {\n attachDoc({ kind: 'screenshot', path: options.path, alt: options.alt, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n custom(options: CustomOptions): void {\n attachDoc({ kind: 'custom', type: options.type, data: options.data, phase: 'runtime' });\n syncAnnotationToTest();\n },\n\n // Attachments\n attach: playwrightAttach,\n\n // OTel span attachment\n attachSpans(spans: ReadonlyArray<Record<string, unknown>>): void {\n if (!activeTestInfo) return;\n const existing = activeTestInfo.annotations.find(\n (a) => a.type === 'story-otel-spans',\n );\n const description = JSON.stringify(spans);\n if (existing) {\n existing.description = description;\n } else {\n activeTestInfo.annotations.push({\n type: 'story-otel-spans',\n description,\n });\n }\n },\n\n // Step timing\n startTimer(): number {\n const ctx = getContext();\n const token = ctx.timerCounter++;\n const stepIndex = ctx.currentStep\n ? ctx.meta.steps.indexOf(ctx.currentStep)\n : undefined;\n ctx.activeTimers.set(token, {\n start: performance.now(),\n stepIndex: stepIndex !== undefined && stepIndex >= 0 ? stepIndex : undefined,\n stepId: ctx.currentStep?.id,\n consumed: false,\n });\n syncAnnotationToTest();\n return token;\n },\n\n endTimer(token: number): void {\n const ctx = getContext();\n const entry = ctx.activeTimers.get(token);\n if (!entry || entry.consumed) return;\n\n entry.consumed = true;\n const durationMs = performance.now() - entry.start;\n\n let step: StoryStep | undefined;\n if (entry.stepId) {\n step = ctx.meta.steps.find((s) => s.id === entry.stepId);\n }\n if (!step && entry.stepIndex !== undefined) {\n step = ctx.meta.steps[entry.stepIndex];\n }\n\n if (step) {\n step.durationMs = durationMs;\n }\n syncAnnotationToTest();\n },\n\n // Step wrappers\n fn,\n expect: storyExpect,\n};\n\nexport type Story = typeof story;\n","/**\n * Playwright Executable Stories\n *\n * BDD-style executable documentation for Playwright Test.\n *\n * @example\n * ```ts\n * import { test, expect } from '@playwright/test';\n * import { story } from 'executable-stories-playwright';\n *\n * test.describe('Calculator', () => {\n * test('adds two numbers', async ({ page }, testInfo) => {\n * story.init(testInfo);\n *\n * story.given('two numbers 5 and 3');\n * const a = 5;\n * const b = 3;\n *\n * story.when('I add them together');\n * const result = a + b;\n *\n * story.then('the result is 8');\n * expect(result).toBe(8);\n * });\n * });\n * ```\n */\n\n// Story API\nimport { story } from './story-api';\nexport { story };\nexport type { Story } from './story-api';\n\n// Top-level step helpers (framework contract)\nexport const given = story.given;\nexport const when = story.when;\nexport const then = story.then;\nexport const and = story.and;\nexport const but = story.but;\n\n// Re-export types from local types module\nexport type {\n StoryMeta,\n StoryStep,\n DocEntry,\n StepKeyword,\n StoryDocs,\n StoryOptions,\n KvOptions,\n JsonOptions,\n CodeOptions,\n TableOptions,\n LinkOptions,\n SectionOptions,\n MermaidOptions,\n ScreenshotOptions,\n CustomOptions,\n} from './types';\n"],"mappings":";AA4BA,SAAS,qBAAqB;AAE9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AA+DP,IAAI,gBAAqC;AAGzC,IAAI,iBAAkC;AAGtC,IAAI,qBAAqB;AAKzB,SAAS,aAA2B;AAClC,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,iBAAiB,QAA6D;AACrF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACjD;AAEA,SAAS,0BAA0B,MAA6B;AAC9D,QAAM,UAAsB,CAAC;AAE7B,MAAI,KAAK,MAAM;AACb,YAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,KAAK,MAAM,OAAO,UAAU,CAAC;AAAA,EAClE;AACA,MAAI,KAAK,KAAK;AACZ,UAAM,QAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC,KAAK,GAAG;AAC5D,YAAQ,KAAK,EAAE,MAAM,OAAO,OAAO,OAAO,UAAU,CAAC;AAAA,EACvD;AACA,MAAI,KAAK,IAAI;AACX,eAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,KAAK,EAAE,GAAG;AACpD,cAAQ,KAAK,EAAE,MAAM,MAAM,OAAO,OAAO,OAAO,UAAU,CAAC;AAAA,IAC7D;AAAA,EACF;AACA,MAAI,KAAK,MAAM;AACb,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,OAAO,KAAK,KAAK;AAAA,MACjB,SAAS,KAAK,KAAK;AAAA,MACnB,MAAM,KAAK,KAAK;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,MAAM;AACb,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,OAAO,KAAK,KAAK;AAAA,MACjB,SAAS,KAAK,UAAU,KAAK,KAAK,OAAO,MAAM,CAAC;AAAA,MAChD,MAAM;AAAA,MACN,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,OAAO;AACd,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,OAAO,KAAK,MAAM;AAAA,MAClB,SAAS,KAAK,MAAM;AAAA,MACpB,MAAM,KAAK,MAAM;AAAA,MACjB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,MAAM;AACb,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,OAAO,KAAK,KAAK;AAAA,MACjB,KAAK,KAAK,KAAK;AAAA,MACf,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,SAAS;AAChB,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,OAAO,KAAK,QAAQ;AAAA,MACpB,UAAU,KAAK,QAAQ;AAAA,MACvB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,SAAS;AAChB,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,MAAM,KAAK,QAAQ;AAAA,MACnB,OAAO,KAAK,QAAQ;AAAA,MACpB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,YAAY;AACnB,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,MAAM,KAAK,WAAW;AAAA,MACtB,KAAK,KAAK,WAAW;AAAA,MACrB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,MAAI,KAAK,QAAQ;AACf,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,MAAM,KAAK,OAAO;AAAA,MAClB,MAAM,KAAK,OAAO;AAAA,MAClB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,UAAU,OAAuB;AACxC,QAAM,MAAM,WAAW;AACvB,MAAI,IAAI,aAAa;AACnB,QAAI,YAAY,SAAS,CAAC;AAC1B,QAAI,YAAY,KAAK,KAAK,KAAK;AAAA,EACjC,OAAO;AACL,QAAI,KAAK,SAAS,CAAC;AACnB,QAAI,KAAK,KAAK,KAAK,KAAK;AAAA,EAC1B;AACF;AAWA,SAAS,iBAAiB,UAA0C;AAClE,QAAM,YAAY,SAAS;AAC3B,MAAI,UAAU,UAAU,GAAG;AACzB,WAAO;AAAA,EACT;AACA,QAAM,YAAY,UAAU,MAAM,GAAG,EAAE;AACvC,SAAO,UAAU,SAAS,IAAI,YAAY;AAC5C;AAMA,SAAS,iBAAiB,SAAsB;AAI9C,WAAS,WAAc,MAAc,YAA4D;AAC/F,UAAM,MAAM,WAAW;AACvB,UAAM,aAAa,OAAO,eAAe;AAEzC,UAAM,mBACH,YAAY,WAAW,YAAY,UAAU,YAAY,WAC1D,IAAI,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO,IAC5C,QACA;AAEN,UAAM,OAAkB;AAAA,MACtB,IAAI,QAAQ,IAAI,aAAa;AAAA,MAC7B,SAAS;AAAA,MACT;AAAA,MACA,MAAO,CAAC,cAAc,aAAc,0BAA0B,UAAU,IAAI,CAAC;AAAA,MAC7E,GAAI,aAAa,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IACxC;AAEA,QAAI,KAAK,MAAM,KAAK,IAAI;AACxB,QAAI,cAAc;AAClB,yBAAqB;AAErB,QAAI,CAAC,WAAY;AAEjB,UAAM,OAAO;AACb,UAAM,QAAQ,YAAY,IAAI;AAE9B,QAAI;AACF,YAAM,SAAS,IAAI,aAAa,SAAY,KAAK,IAAI,QAA8B,IAAI,KAAK;AAC5F,UAAI,kBAAkB,SAAS;AAC7B,eAAO,OAAO;AAAA,UACZ,CAAC,QAAQ;AAAE,iBAAK,aAAa,YAAY,IAAI,IAAI;AAAO,iCAAqB;AAAG,mBAAO;AAAA,UAAK;AAAA,UAC5F,CAAC,QAAQ;AAAE,iBAAK,aAAa,YAAY,IAAI,IAAI;AAAO,iCAAqB;AAAG,kBAAM;AAAA,UAAK;AAAA,QAC7F;AAAA,MACF;AACA,WAAK,aAAa,YAAY,IAAI,IAAI;AACtC,2BAAqB;AACrB,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,aAAa,YAAY,IAAI,IAAI;AACtC,2BAAqB;AACrB,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,WAAW,GAA2B;AAC7C,SACE,OAAO,MAAM,YACb,MAAM,QACN,WAAW,KACX,iBAAiB,KACjB,MAAM,QAAS,EAAe,WAAW;AAE7C;AAGA,SAAS,KACP,OACA,QACA,OACM;AACN,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI,WAAW,UAAa,WAAW,MAAM,GAAG;AAC9C,eAAW;AACX,eAAW;AACX,cAAU;AAAA,EACZ,OAAO;AACL,eAAW;AACX,cAAU;AACV,eAAW,SAAS;AAAA,EACtB;AAEA,QAAM,OAAkB;AAAA,IACtB,UAAU,SAAS;AAAA,IACnB,OAAO,CAAC;AAAA,IACR,WAAW,iBAAiB,QAAQ;AAAA,IACpC,MAAM,SAAS;AAAA,IACf,SAAS,iBAAiB,SAAS,MAAM;AAAA,IACzC,MAAM,SAAS;AAAA,IACf,aAAa;AAAA,EACf;AAGA,QAAM,UAAU,wBAAwB;AACxC,MAAI,SAAS;AAEX,SAAK,OAAO,EAAE,GAAG,KAAK,MAAM,MAAM,EAAE,SAAS,QAAQ,SAAS,QAAQ,QAAQ,OAAO,EAAE;AAGvF,SAAK,OAAO,KAAK,QAAQ,CAAC;AAC1B,SAAK,KAAK,KAAK,EAAE,MAAM,MAAM,OAAO,YAAY,OAAO,QAAQ,SAAS,OAAO,UAAU,CAAC;AAE1F,UAAM,WAAW,SAAS,oBAAoB,QAAQ,IAAI;AAC1D,UAAM,MAAM,gBAAgB,UAAU,QAAQ,OAAO;AACrD,QAAI,KAAK;AACP,WAAK,KAAK,KAAK,EAAE,MAAM,QAAQ,OAAO,cAAc,KAAK,OAAO,UAAU,CAAC;AAAA,IAC7E;AAGA,QAAI;AAEF,YAAM,SAAS,YAAY,QACrB,OAAO,eAAe,cAAc,UAAU,UAAU,KAAK;AACnE,YAAM,MAAM,cAAc,MAAO;AACjC,YAAM,MAAM,IAAI,oBAAoB;AACpC,YAAM,OAAO,IAAI,OAAO,gBAAgB;AACxC,UAAI,MAAM;AACR,aAAK,aAAa,kBAAkB,SAAS,KAAK;AAClD,YAAI,SAAS,MAAM,OAAQ,MAAK,aAAa,cAAc,QAAQ,IAAI;AACvE,YAAI,SAAS,QAAQ;AACnB,gBAAM,UAAU,MAAM,QAAQ,QAAQ,MAAM,IAAI,QAAQ,SAAS,CAAC,QAAQ,MAAM;AAChF,eAAK,aAAa,iBAAiB,OAAO;AAAA,QAC5C;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAA2B;AAAA,EACrC;AAEA,WAAS,YAAY,KAAK;AAAA,IACxB,MAAM;AAAA,IACN,aAAa,KAAK,UAAU,IAAI;AAAA,EAClC,CAAC;AAED,kBAAgB;AAAA,IACd;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa,CAAC;AAAA,IACd,cAAc,oBAAI,IAAI;AAAA,IACtB,cAAc;AAAA,IACd;AAAA,EACF;AACA,mBAAiB;AACnB;AAMA,SAAS,uBAA6B;AACpC,MAAI,CAAC,kBAAkB,CAAC,cAAe;AACvC,QAAM,aAAa,eAAe,YAAY;AAAA,IAC5C,CAAC,MAAM,EAAE,SAAS;AAAA,EACpB;AACA,MAAI,YAAY;AACd,eAAW,cAAc,KAAK,UAAU,cAAc,IAAI;AAAA,EAC5D;AACF;AAYA,SAAS,GAAM,SAAsB,MAAc,MAAgC;AACjF,QAAM,MAAM,WAAW;AACvB,QAAM,mBACH,YAAY,WAAW,YAAY,UAAU,YAAY,WAC1D,IAAI,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,YAAY,OAAO,IAC5C,QACA;AACN,QAAM,OAAkB;AAAA,IACtB,IAAI,QAAQ,IAAI,aAAa;AAAA,IAC7B,SAAS;AAAA,IACT;AAAA,IACA,MAAM,CAAC;AAAA,IACP,SAAS;AAAA,EACX;AACA,MAAI,KAAK,MAAM,KAAK,IAAI;AACxB,MAAI,cAAc;AAClB,uBAAqB;AAErB,QAAM,QAAQ,YAAY,IAAI;AAC9B,MAAI;AACF,UAAM,SAAS,IAAI,aAAa,SAAY,KAAK,IAAI,QAA8B,IAAI,KAAK;AAC5F,QAAI,kBAAkB,SAAS;AAC7B,aAAO,OAAO;AAAA,QACZ,CAAC,QAAQ;AACP,eAAK,aAAa,YAAY,IAAI,IAAI;AACtC,+BAAqB;AACrB,iBAAO;AAAA,QACT;AAAA,QACA,CAAC,QAAQ;AACP,eAAK,aAAa,YAAY,IAAI,IAAI;AACtC,+BAAqB;AACrB,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,SAAK,aAAa,YAAY,IAAI,IAAI;AACtC,yBAAqB;AACrB,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,SAAK,aAAa,YAAY,IAAI,IAAI;AACtC,yBAAqB;AACrB,UAAM;AAAA,EACR;AACF;AAMA,SAAS,YAAe,MAAc,MAAkB;AACtD,SAAO,GAAG,QAAQ,MAAM,IAAI;AAC9B;AAMA,SAAS,iBAAiB,SAAkC;AAC1D,QAAM,MAAM,WAAW;AACvB,QAAM,YAAY,IAAI,cAClB,IAAI,KAAK,MAAM,QAAQ,IAAI,WAAW,IACtC;AACJ,MAAI,YAAY,KAAK;AAAA,IACnB,GAAG;AAAA,IACH,QAAQ,IAAI,aAAa;AAAA,EAC3B,CAAC;AACD,uBAAqB;AAErB,MAAI,gBAAgB;AAClB,UAAM,gBAA8F;AAAA,MAClG,MAAM,QAAQ;AAAA,MACd,aAAa,QAAQ;AAAA,IACvB;AACA,QAAI,QAAQ,KAAM,eAAc,OAAO,QAAQ;AAC/C,QAAI,QAAQ,KAAM,eAAc,OAAO,QAAQ;AAC/C,mBAAe,OAAO,QAAQ,MAAM,aAAa;AAAA,EACnD;AACF;AAMO,IAAM,QAAQ;AAAA,EACnB;AAAA;AAAA,EAGA,OAAO,iBAAiB,OAAO;AAAA,EAC/B,MAAM,iBAAiB,MAAM;AAAA,EAC7B,MAAM,iBAAiB,MAAM;AAAA,EAC7B,KAAK,iBAAiB,KAAK;AAAA,EAC3B,KAAK,iBAAiB,KAAK;AAAA;AAAA,EAG3B,SAAS,iBAAiB,OAAO;AAAA,EACjC,KAAK,iBAAiB,MAAM;AAAA,EAC5B,QAAQ,iBAAiB,MAAM;AAAA;AAAA,EAG/B,OAAO,iBAAiB,OAAO;AAAA,EAC/B,SAAS,iBAAiB,OAAO;AAAA,EACjC,SAAS,iBAAiB,MAAM;AAAA,EAChC,QAAQ,iBAAiB,MAAM;AAAA,EAC/B,QAAQ,iBAAiB,MAAM;AAAA;AAAA,EAG/B,KAAK,MAAoB;AACvB,cAAU,EAAE,MAAM,QAAQ,MAAM,OAAO,UAAU,CAAC;AAClD,yBAAqB;AAAA,EACvB;AAAA,EAEA,IAAI,MAA+B;AACjC,UAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AAChD,cAAU,EAAE,MAAM,OAAO,OAAO,OAAO,UAAU,CAAC;AAClD,yBAAqB;AAAA,EACvB;AAAA,EAEA,GAAG,SAA0B;AAC3B,cAAU,EAAE,MAAM,MAAM,OAAO,QAAQ,OAAO,OAAO,QAAQ,OAAO,OAAO,UAAU,CAAC;AACtF,yBAAqB;AAAA,EACvB;AAAA,EAEA,KAAK,SAA4B;AAC/B,UAAM,UAAU,KAAK,UAAU,QAAQ,OAAO,MAAM,CAAC;AACrD,cAAU,EAAE,MAAM,QAAQ,OAAO,QAAQ,OAAO,SAAS,MAAM,QAAQ,OAAO,UAAU,CAAC;AACzF,yBAAqB;AAAA,EACvB;AAAA,EAEA,KAAK,SAA4B;AAC/B,cAAU,EAAE,MAAM,QAAQ,OAAO,QAAQ,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ,MAAM,OAAO,UAAU,CAAC;AAChH,yBAAqB;AAAA,EACvB;AAAA,EAEA,MAAM,SAA6B;AACjC,cAAU,EAAE,MAAM,SAAS,OAAO,QAAQ,OAAO,SAAS,QAAQ,SAAS,MAAM,QAAQ,MAAM,OAAO,UAAU,CAAC;AACjH,yBAAqB;AAAA,EACvB;AAAA,EAEA,KAAK,SAA4B;AAC/B,cAAU,EAAE,MAAM,QAAQ,OAAO,QAAQ,OAAO,KAAK,QAAQ,KAAK,OAAO,UAAU,CAAC;AACpF,yBAAqB;AAAA,EACvB;AAAA,EAEA,QAAQ,SAA+B;AACrC,cAAU,EAAE,MAAM,WAAW,OAAO,QAAQ,OAAO,UAAU,QAAQ,UAAU,OAAO,UAAU,CAAC;AACjG,yBAAqB;AAAA,EACvB;AAAA,EAEA,QAAQ,SAA+B;AACrC,cAAU,EAAE,MAAM,WAAW,MAAM,QAAQ,MAAM,OAAO,QAAQ,OAAO,OAAO,UAAU,CAAC;AACzF,yBAAqB;AAAA,EACvB;AAAA,EAEA,WAAW,SAAkC;AAC3C,cAAU,EAAE,MAAM,cAAc,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,OAAO,UAAU,CAAC;AACxF,yBAAqB;AAAA,EACvB;AAAA,EAEA,OAAO,SAA8B;AACnC,cAAU,EAAE,MAAM,UAAU,MAAM,QAAQ,MAAM,MAAM,QAAQ,MAAM,OAAO,UAAU,CAAC;AACtF,yBAAqB;AAAA,EACvB;AAAA;AAAA,EAGA,QAAQ;AAAA;AAAA,EAGR,YAAY,OAAqD;AAC/D,QAAI,CAAC,eAAgB;AACrB,UAAM,WAAW,eAAe,YAAY;AAAA,MAC1C,CAAC,MAAM,EAAE,SAAS;AAAA,IACpB;AACA,UAAM,cAAc,KAAK,UAAU,KAAK;AACxC,QAAI,UAAU;AACZ,eAAS,cAAc;AAAA,IACzB,OAAO;AACL,qBAAe,YAAY,KAAK;AAAA,QAC9B,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGA,aAAqB;AACnB,UAAM,MAAM,WAAW;AACvB,UAAM,QAAQ,IAAI;AAClB,UAAM,YAAY,IAAI,cAClB,IAAI,KAAK,MAAM,QAAQ,IAAI,WAAW,IACtC;AACJ,QAAI,aAAa,IAAI,OAAO;AAAA,MAC1B,OAAO,YAAY,IAAI;AAAA,MACvB,WAAW,cAAc,UAAa,aAAa,IAAI,YAAY;AAAA,MACnE,QAAQ,IAAI,aAAa;AAAA,MACzB,UAAU;AAAA,IACZ,CAAC;AACD,yBAAqB;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,OAAqB;AAC5B,UAAM,MAAM,WAAW;AACvB,UAAM,QAAQ,IAAI,aAAa,IAAI,KAAK;AACxC,QAAI,CAAC,SAAS,MAAM,SAAU;AAE9B,UAAM,WAAW;AACjB,UAAM,aAAa,YAAY,IAAI,IAAI,MAAM;AAE7C,QAAI;AACJ,QAAI,MAAM,QAAQ;AAChB,aAAO,IAAI,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,MAAM;AAAA,IACzD;AACA,QAAI,CAAC,QAAQ,MAAM,cAAc,QAAW;AAC1C,aAAO,IAAI,KAAK,MAAM,MAAM,SAAS;AAAA,IACvC;AAEA,QAAI,MAAM;AACR,WAAK,aAAa;AAAA,IACpB;AACA,yBAAqB;AAAA,EACvB;AAAA;AAAA,EAGA;AAAA,EACA,QAAQ;AACV;;;AC9lBO,IAAM,QAAQ,MAAM;AACpB,IAAM,OAAO,MAAM;AACnB,IAAM,OAAO,MAAM;AACnB,IAAM,MAAM,MAAM;AAClB,IAAM,MAAM,MAAM;","names":[]}
1
+ {"version":3,"sources":["../src/story-api.ts","../src/index.ts"],"sourcesContent":["/**\n * Playwright story.* API for executable-stories.\n *\n * Uses native Playwright test() with opt-in documentation:\n *\n * @example\n * ```ts\n * import { test, expect } from '@playwright/test';\n * import { story } from 'executable-stories-playwright';\n *\n * test.describe('Calculator', () => {\n * test('adds two numbers', async ({ page }, testInfo) => {\n * story.init(testInfo);\n *\n * story.given('two numbers 5 and 3');\n * const a = 5;\n * const b = 3;\n *\n * story.when('I add them together');\n * const result = a + b;\n *\n * story.then('the result is 8');\n * expect(result).toBe(8);\n * });\n * });\n * ```\n */\n\nimport { createRequire } from 'node:module';\nimport type { TestInfo, PlaywrightTestArgs, PlaywrightTestOptions } from '@playwright/test';\nimport {\n tryGetActiveOtelContext,\n resolveTraceUrl,\n} from 'executable-stories-formatters';\nimport type {\n StepKeyword,\n StoryMeta,\n StoryStep,\n DocEntry,\n NormalizedTicket,\n TicketInput,\n} from './types';\nimport type {\n StoryDocs,\n StoryOptions,\n AttachmentOptions,\n ScopedAttachment,\n KvOptions,\n JsonOptions,\n CodeOptions,\n TableOptions,\n LinkOptions,\n SectionOptions,\n MermaidOptions,\n ScreenshotOptions,\n CustomOptions,\n} from './types';\n\n// Re-export types for consumers\nexport type {\n StoryMeta,\n StoryStep,\n DocEntry,\n StepKeyword,\n StoryDocs,\n StoryOptions,\n AttachmentOptions,\n} from './types';\n\n// ============================================================================\n// Internal types\n// ============================================================================\n\n/** Fixture type for step callbacks: Playwright test args + options; custom extend() fixtures as unknown. */\ntype PlaywrightFixtures = PlaywrightTestArgs & PlaywrightTestOptions & Record<string, unknown>;\n\ninterface TimerEntry {\n start: number;\n stepIndex?: number;\n stepId?: string;\n consumed: boolean;\n}\n\ninterface StoryContext {\n meta: StoryMeta;\n currentStep: StoryStep | null;\n stepCounter: number;\n attachments: ScopedAttachment[];\n activeTimers: Map<number, TimerEntry>;\n timerCounter: number;\n fixtures?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Playwright-specific context\n// ============================================================================\n\n/** Active story context - set by story.init() */\nlet activeContext: StoryContext | null = null;\n\n/** Reference to testInfo for attaching metadata */\nlet activeTestInfo: TestInfo | null = null;\n\n/** Counter to track source order of stories (increments on each story.init call) */\nlet sourceOrderCounter = 0;\n\n/**\n * Get the current story context. Throws if story.init() wasn't called.\n */\nfunction getContext(): StoryContext {\n if (!activeContext) {\n throw new Error(\n \"story.init(testInfo) must be called first. Use: test('name', async ({ page }, testInfo) => { story.init(testInfo); ... });\",\n );\n }\n return activeContext;\n}\n\n// ============================================================================\n// Helper functions (inlined from core)\n// ============================================================================\n\nfunction normalizeTickets(\n ticket: TicketInput | TicketInput[] | undefined,\n): NormalizedTicket[] | undefined {\n if (!ticket) return undefined;\n const arr = Array.isArray(ticket) ? ticket : [ticket];\n return arr.map((t) => (typeof t === 'string' ? { id: t } : t));\n}\n\nfunction convertStoryDocsToEntries(docs: StoryDocs): DocEntry[] {\n const entries: DocEntry[] = [];\n\n if (docs.note) {\n entries.push({ kind: 'note', text: docs.note, phase: 'runtime' });\n }\n if (docs.tag) {\n const names = Array.isArray(docs.tag) ? docs.tag : [docs.tag];\n entries.push({ kind: 'tag', names, phase: 'runtime' });\n }\n if (docs.kv) {\n for (const [label, value] of Object.entries(docs.kv)) {\n entries.push({ kind: 'kv', label, value, phase: 'runtime' });\n }\n }\n if (docs.code) {\n entries.push({\n kind: 'code',\n label: docs.code.label,\n content: docs.code.content,\n lang: docs.code.lang,\n phase: 'runtime',\n });\n }\n if (docs.json) {\n entries.push({\n kind: 'code',\n label: docs.json.label,\n content: JSON.stringify(docs.json.value, null, 2),\n lang: 'json',\n phase: 'runtime',\n });\n }\n if (docs.table) {\n entries.push({\n kind: 'table',\n label: docs.table.label,\n columns: docs.table.columns,\n rows: docs.table.rows,\n phase: 'runtime',\n });\n }\n if (docs.link) {\n entries.push({\n kind: 'link',\n label: docs.link.label,\n url: docs.link.url,\n phase: 'runtime',\n });\n }\n if (docs.section) {\n entries.push({\n kind: 'section',\n title: docs.section.title,\n markdown: docs.section.markdown,\n phase: 'runtime',\n });\n }\n if (docs.mermaid) {\n entries.push({\n kind: 'mermaid',\n code: docs.mermaid.code,\n title: docs.mermaid.title,\n phase: 'runtime',\n });\n }\n if (docs.screenshot) {\n entries.push({\n kind: 'screenshot',\n path: docs.screenshot.path,\n alt: docs.screenshot.alt,\n phase: 'runtime',\n });\n }\n if (docs.custom) {\n entries.push({\n kind: 'custom',\n type: docs.custom.type,\n data: docs.custom.data,\n phase: 'runtime',\n });\n }\n\n return entries;\n}\n\nfunction attachDoc(entry: DocEntry, children?: DocEntry[]): DocEntry {\n const ctx = getContext();\n if (children && children.length > 0) {\n entry.children = children;\n const childSet = new Set<DocEntry>(children);\n const filterDocs = (docs: DocEntry[]) => docs.filter((d) => !childSet.has(d));\n // Remove children from ALL containers (story-level + every step)\n ctx.meta.docs = filterDocs(ctx.meta.docs ?? []);\n for (const step of ctx.meta.steps) {\n if (step.docs) step.docs = filterDocs(step.docs);\n }\n }\n if (ctx.currentStep) {\n ctx.currentStep.docs ??= [];\n ctx.currentStep.docs.push(entry);\n } else {\n ctx.meta.docs ??= [];\n ctx.meta.docs.push(entry);\n }\n syncAnnotationToTest();\n return entry;\n}\n\n// ============================================================================\n// Suite path extraction\n// ============================================================================\n\n/**\n * Extract the suite path from testInfo.titlePath.\n * Playwright's titlePath includes: [projectName, ...describeTitles, testTitle]\n * We want just the describe titles (excluding project and test name).\n */\nfunction extractSuitePath(testInfo: TestInfo): string[] | undefined {\n const titlePath = testInfo.titlePath;\n if (titlePath.length <= 2) {\n return undefined;\n }\n const suitePath = titlePath.slice(1, -1);\n return suitePath.length > 0 ? suitePath : undefined;\n}\n\n// ============================================================================\n// Step markers\n// ============================================================================\n\nfunction createStepMarker(keyword: StepKeyword) {\n function stepMarker(text: string, docs?: StoryDocs): void;\n function stepMarker(text: string, children: DocEntry[]): void;\n function stepMarker<T>(text: string, body: (fixtures: PlaywrightFixtures) => T): T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function stepMarker<T>(text: string, docsOrBody?: StoryDocs | DocEntry[] | ((...args: any[]) => T)): T | void {\n const ctx = getContext();\n const isCallback = typeof docsOrBody === 'function';\n const isChildrenArray = Array.isArray(docsOrBody);\n\n const resolvedKeyword: StepKeyword =\n (keyword === 'Given' || keyword === 'When' || keyword === 'Then') &&\n ctx.meta.steps.some((s) => s.keyword === keyword)\n ? 'And'\n : keyword;\n\n let stepDocs: DocEntry[] = [];\n if (!isCallback && !isChildrenArray && docsOrBody) {\n stepDocs = convertStoryDocsToEntries(docsOrBody as StoryDocs);\n }\n\n const step: StoryStep = {\n id: `step-${ctx.stepCounter++}`,\n keyword: resolvedKeyword,\n text,\n docs: stepDocs,\n ...(isCallback ? { wrapped: true } : {}),\n };\n\n ctx.meta.steps.push(step);\n ctx.currentStep = step;\n syncAnnotationToTest();\n\n // Handle DocEntry[] children: attach as step docs and deduplicate from story-level\n if (isChildrenArray) {\n const children = docsOrBody as DocEntry[];\n if (children.length > 0) {\n const childSet = new Set<DocEntry>(children);\n // Deduplicate from story-level docs\n ctx.meta.docs = (ctx.meta.docs ?? []).filter((d) => !childSet.has(d));\n // Deduplicate from step docs of earlier steps\n for (const prevStep of ctx.meta.steps) {\n if (prevStep !== step && prevStep.docs) {\n prevStep.docs = prevStep.docs.filter((d) => !childSet.has(d));\n }\n }\n step.docs = [...(step.docs ?? []), ...children];\n }\n syncAnnotationToTest();\n return;\n }\n\n if (!isCallback) return;\n\n const body = docsOrBody as (fixtures?: PlaywrightFixtures) => T;\n const start = performance.now();\n\n try {\n const result = ctx.fixtures !== undefined ? body(ctx.fixtures as PlaywrightFixtures) : body();\n if (result instanceof Promise) {\n return result.then(\n (val) => { step.durationMs = performance.now() - start; syncAnnotationToTest(); return val; },\n (err) => { step.durationMs = performance.now() - start; syncAnnotationToTest(); throw err; },\n ) as T;\n }\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n return result;\n } catch (err) {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n throw err;\n }\n }\n return stepMarker;\n}\n\n// ============================================================================\n// story.init() - Playwright-specific\n// ============================================================================\n\nfunction isTestInfo(x: unknown): x is TestInfo {\n return (\n typeof x === 'object' &&\n x !== null &&\n 'title' in x &&\n 'annotations' in x &&\n Array.isArray((x as TestInfo).annotations)\n );\n}\n\n/** init(testInfo) or init(fixtures, testInfo) or init(testInfo, { fixtures }). */\nfunction init(\n first: TestInfo | unknown,\n second?: StoryOptions | TestInfo,\n third?: StoryOptions,\n): void {\n let testInfo: TestInfo;\n let options: StoryOptions | undefined;\n let fixtures: unknown;\n\n if (second !== undefined && isTestInfo(second)) {\n fixtures = first;\n testInfo = second;\n options = third;\n } else {\n testInfo = first as TestInfo;\n options = second;\n fixtures = options?.fixtures;\n }\n\n const meta: StoryMeta = {\n scenario: testInfo.title,\n steps: [],\n suitePath: extractSuitePath(testInfo),\n tags: options?.tags,\n tickets: normalizeTickets(options?.ticket),\n meta: options?.meta,\n sourceOrder: sourceOrderCounter++,\n };\n\n // OTel bridge: detect active span, flow data bidirectionally\n const otelCtx = tryGetActiveOtelContext();\n if (otelCtx) {\n // OTel -> Story: capture traceId in structured meta\n meta.meta = { ...meta.meta, otel: { traceId: otelCtx.traceId, spanId: otelCtx.spanId } };\n\n // OTel -> Story: inject human-readable doc entries\n meta.docs = meta.docs ?? [];\n meta.docs.push({ kind: 'kv', label: 'Trace ID', value: otelCtx.traceId, phase: 'runtime' });\n\n const template = options?.traceUrlTemplate ?? process.env.OTEL_TRACE_URL_TEMPLATE;\n const url = resolveTraceUrl(template, otelCtx.traceId);\n if (url) {\n meta.docs.push({ kind: 'link', label: 'View Trace', url, phase: 'runtime' });\n }\n\n // Story -> OTel: enrich active span with story attributes\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const reqUrl = import.meta.url\n ?? (typeof __filename !== 'undefined' ? `file://${__filename}` : undefined);\n const req = createRequire(reqUrl!);\n const api = req('@opentelemetry/api');\n const span = api.trace?.getActiveSpan?.();\n if (span) {\n span.setAttribute('story.scenario', testInfo.title);\n if (options?.tags?.length) span.setAttribute('story.tags', options.tags);\n if (options?.ticket) {\n const tickets = Array.isArray(options.ticket) ? options.ticket : [options.ticket];\n span.setAttribute('story.tickets', tickets.map((t) => typeof t === 'string' ? t : t.id));\n }\n }\n } catch { /* OTel not available */ }\n }\n\n testInfo.annotations.push({\n type: 'story-meta',\n description: JSON.stringify(meta),\n });\n\n activeContext = {\n meta,\n currentStep: null,\n stepCounter: 0,\n attachments: [],\n activeTimers: new Map(),\n timerCounter: 0,\n fixtures: fixtures as Record<string, unknown> | undefined,\n };\n activeTestInfo = testInfo;\n}\n\n/**\n * Update the story-meta annotation on testInfo with the current meta (including steps).\n * Called after each step/doc so the reporter sees the full story in onTestEnd.\n */\nfunction syncAnnotationToTest(): void {\n if (!activeTestInfo || !activeContext) return;\n const annotation = activeTestInfo.annotations.find(\n (a) => a.type === 'story-meta',\n );\n if (annotation) {\n annotation.description = JSON.stringify(activeContext.meta);\n }\n}\n\n// ============================================================================\n// story.fn() and story.expect()\n// ============================================================================\n\n/**\n * Wrap a function as a step with timing and error capture.\n * Records the step with `wrapped: true` and `durationMs`.\n */\nfunction fn<T>(keyword: StepKeyword, text: string, body: (fixtures: PlaywrightFixtures) => T): T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction fn<T>(keyword: StepKeyword, text: string, body: (...args: any[]) => T): T {\n const ctx = getContext();\n const resolvedKeyword: StepKeyword =\n (keyword === 'Given' || keyword === 'When' || keyword === 'Then') &&\n ctx.meta.steps.some((s) => s.keyword === keyword)\n ? 'And'\n : keyword;\n const step: StoryStep = {\n id: `step-${ctx.stepCounter++}`,\n keyword: resolvedKeyword,\n text,\n docs: [],\n wrapped: true,\n };\n ctx.meta.steps.push(step);\n ctx.currentStep = step;\n syncAnnotationToTest();\n\n const start = performance.now();\n try {\n const result = ctx.fixtures !== undefined ? body(ctx.fixtures as PlaywrightFixtures) : body();\n if (result instanceof Promise) {\n return result.then(\n (val) => {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n return val;\n },\n (err) => {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n throw err;\n },\n ) as T;\n }\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n return result;\n } catch (err) {\n step.durationMs = performance.now() - start;\n syncAnnotationToTest();\n throw err;\n }\n}\n\n/**\n * Wrap an assertion as a Then step with timing and error capture.\n * Shorthand for `story.fn('Then', text, body)`.\n */\nfunction storyExpect<T>(text: string, body: () => T): T {\n return fn('Then', text, body);\n}\n\n// ============================================================================\n// Playwright-specific attach\n// ============================================================================\n\nfunction playwrightAttach(options: AttachmentOptions): void {\n const ctx = getContext();\n const stepIndex = ctx.currentStep\n ? ctx.meta.steps.indexOf(ctx.currentStep)\n : undefined;\n ctx.attachments.push({\n ...options,\n stepId: ctx.currentStep?.id,\n });\n syncAnnotationToTest();\n\n if (activeTestInfo) {\n const attachOptions: { name: string; contentType: string; path?: string; body?: string | Buffer } = {\n name: options.name,\n contentType: options.mediaType,\n };\n if (options.path) attachOptions.path = options.path;\n if (options.body) attachOptions.body = options.body;\n activeTestInfo.attach(options.name, attachOptions);\n }\n}\n\n// ============================================================================\n// Export story object\n// ============================================================================\n\nexport const story = {\n init,\n\n // BDD step markers\n given: createStepMarker('Given'),\n when: createStepMarker('When'),\n then: createStepMarker('Then'),\n and: createStepMarker('And'),\n but: createStepMarker('But'),\n\n // AAA pattern aliases\n arrange: createStepMarker('Given'),\n act: createStepMarker('When'),\n assert: createStepMarker('Then'),\n\n // Additional aliases\n setup: createStepMarker('Given'),\n context: createStepMarker('Given'),\n execute: createStepMarker('When'),\n action: createStepMarker('When'),\n verify: createStepMarker('Then'),\n\n // Standalone doc methods\n note(text: string, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'note', text, phase: 'runtime' }, children);\n },\n\n tag(name: string | string[], children?: DocEntry[]): DocEntry {\n const names = Array.isArray(name) ? name : [name];\n return attachDoc({ kind: 'tag', names, phase: 'runtime' }, children);\n },\n\n kv(options: KvOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'kv', label: options.label, value: options.value, phase: 'runtime' }, children);\n },\n\n json(options: JsonOptions, children?: DocEntry[]): DocEntry {\n const content = JSON.stringify(options.value, null, 2);\n return attachDoc({ kind: 'code', label: options.label, content, lang: 'json', phase: 'runtime' }, children);\n },\n\n code(options: CodeOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'code', label: options.label, content: options.content, lang: options.lang, phase: 'runtime' }, children);\n },\n\n table(options: TableOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'table', label: options.label, columns: options.columns, rows: options.rows, phase: 'runtime' }, children);\n },\n\n link(options: LinkOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'link', label: options.label, url: options.url, phase: 'runtime' }, children);\n },\n\n section(options: SectionOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'section', title: options.title, markdown: options.markdown, phase: 'runtime' }, children);\n },\n\n mermaid(options: MermaidOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'mermaid', code: options.code, title: options.title, phase: 'runtime' }, children);\n },\n\n screenshot(options: ScreenshotOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'screenshot', path: options.path, alt: options.alt, phase: 'runtime' }, children);\n },\n\n custom(options: CustomOptions, children?: DocEntry[]): DocEntry {\n return attachDoc({ kind: 'custom', type: options.type, data: options.data, phase: 'runtime' }, children);\n },\n\n // Attachments\n attach: playwrightAttach,\n\n // OTel span attachment\n attachSpans(spans: ReadonlyArray<Record<string, unknown>>): void {\n if (!activeTestInfo) return;\n const existing = activeTestInfo.annotations.find(\n (a) => a.type === 'story-otel-spans',\n );\n const description = JSON.stringify(spans);\n if (existing) {\n existing.description = description;\n } else {\n activeTestInfo.annotations.push({\n type: 'story-otel-spans',\n description,\n });\n }\n },\n\n // Step timing\n startTimer(): number {\n const ctx = getContext();\n const token = ctx.timerCounter++;\n const stepIndex = ctx.currentStep\n ? ctx.meta.steps.indexOf(ctx.currentStep)\n : undefined;\n ctx.activeTimers.set(token, {\n start: performance.now(),\n stepIndex: stepIndex !== undefined && stepIndex >= 0 ? stepIndex : undefined,\n stepId: ctx.currentStep?.id,\n consumed: false,\n });\n syncAnnotationToTest();\n return token;\n },\n\n endTimer(token: number): void {\n const ctx = getContext();\n const entry = ctx.activeTimers.get(token);\n if (!entry || entry.consumed) return;\n\n entry.consumed = true;\n const durationMs = performance.now() - entry.start;\n\n let step: StoryStep | undefined;\n if (entry.stepId) {\n step = ctx.meta.steps.find((s) => s.id === entry.stepId);\n }\n if (!step && entry.stepIndex !== undefined) {\n step = ctx.meta.steps[entry.stepIndex];\n }\n\n if (step) {\n step.durationMs = durationMs;\n }\n syncAnnotationToTest();\n },\n\n // Step wrappers\n fn,\n expect: storyExpect,\n};\n\nexport type Story = typeof story;\n","/**\n * Playwright Executable Stories\n *\n * BDD-style executable documentation for Playwright Test.\n *\n * @example\n * ```ts\n * import { test, expect } from '@playwright/test';\n * import { story } from 'executable-stories-playwright';\n *\n * test.describe('Calculator', () => {\n * test('adds two numbers', async ({ page }, testInfo) => {\n * story.init(testInfo);\n *\n * story.given('two numbers 5 and 3');\n * const a = 5;\n * const b = 3;\n *\n * story.when('I add them together');\n * const result = a + b;\n *\n * story.then('the result is 8');\n * expect(result).toBe(8);\n * });\n * });\n * ```\n */\n\n// Story API\nimport { story } from './story-api';\nexport { story };\nexport type { Story } from './story-api';\n\n// Top-level step helpers (framework contract)\nexport const given = story.given;\nexport const when = story.when;\nexport const then = story.then;\nexport const and = story.and;\nexport const but = story.but;\n\n// Re-export types from local types module\nexport type {\n StoryMeta,\n StoryStep,\n DocEntry,\n StepKeyword,\n StoryDocs,\n StoryOptions,\n TicketInput,\n NormalizedTicket,\n KvOptions,\n JsonOptions,\n CodeOptions,\n TableOptions,\n LinkOptions,\n SectionOptions,\n MermaidOptions,\n ScreenshotOptions,\n CustomOptions,\n} from './types';\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "executable-stories-playwright",
3
- "version": "8.1.0",
3
+ "version": "8.1.3",
4
4
  "description": "BDD-style executable stories for Playwright Test with documentation generation",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -26,7 +26,6 @@
26
26
  ],
27
27
  "peerDependencies": {
28
28
  "@playwright/test": ">=1.58.2",
29
- "executable-stories-formatters": "^0.7.0",
30
29
  "autotel": ">=2.24.0"
31
30
  },
32
31
  "peerDependenciesMeta": {
@@ -34,14 +33,15 @@
34
33
  "optional": true
35
34
  }
36
35
  },
37
- "dependencies": {},
36
+ "dependencies": {
37
+ "executable-stories-formatters": "0.7.2"
38
+ },
38
39
  "devDependencies": {
39
40
  "@opentelemetry/api": "^1.9.0",
40
41
  "@playwright/test": "^1.58.2",
41
42
  "@types/node": "^25.5.0",
42
43
  "tsup": "^8.5.1",
43
- "typescript": "~5.9.3",
44
- "executable-stories-formatters": "0.7.0"
44
+ "typescript": "~5.9.3"
45
45
  },
46
46
  "repository": {
47
47
  "type": "git",