executable-stories-e2e 0.0.0-stage → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +71 -2
- package/dist/index.cjs +543 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +76 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +505 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/phrase.ts","../src/story.ts"],"sourcesContent":["/**\n * executable-stories reporter for the `e2e` agentic test runner.\n *\n * The reporter reads the finished `report-1` document and writes the RawRun\n * JSON every other adapter writes. `executable-stories format` takes it from\n * there. Optional `story.given/when/then` markers in a test name the steps in\n * the test's own words; without them, step text is phrased from e2e's record.\n */\n\nimport { rmSync } from 'node:fs';\nimport { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { FinishedRun, Report, Reporter, ReporterSummary } from 'e2e';\nimport type {\n RawAttachment,\n RawRun,\n RawStatus,\n RawStepEvent,\n RawTestCase,\n} from 'executable-stories-core/types/raw';\nimport type {\n DocEntry,\n StepKeyword,\n StoryStep,\n} from 'executable-stories-core/types/story';\nimport { phraseStep } from './phrase';\nimport { MARKERS_DIR, type Marker } from './story';\n\nexport { story, type Marker, type MarkerKeyword } from './story';\nexport { phraseStep } from './phrase';\n\ntype ReportResult = Report['run']['results'][number];\ntype ReportAttempt = ReportResult['attempts'][number];\ntype ReportStep = ReportAttempt['steps'][number];\ntype ReportArtifact = ReportAttempt['artifacts'][number];\ntype ReportError = NonNullable<ReportAttempt['error']>;\n\nexport interface StoryReporterOptions {\n /** Where to write the RawRun JSON, relative to the project root. Default: `.executable-stories/raw-run.json`, where `executable-stories format` looks first. */\n rawRunPath?: string;\n}\n\n/**\n * Exhaustive over e2e's unions at compile time, forgiving at run time: a newer\n * e2e may report a kind or status this version has never seen, and its\n * contract says to ignore what you do not know rather than fail the reporter.\n */\nfunction unknownValue<T>(_value: never, fallback: T): T {\n return fallback;\n}\n\nfunction resultStatus(status: ReportResult['status']): RawStatus {\n switch (status) {\n case 'passed':\n case 'flaky': // the final attempt passed; `retry` records the failed ones\n return 'pass';\n case 'failed':\n return 'fail';\n case 'skipped':\n return 'skip';\n case 'timed-out':\n return 'timeout';\n case 'interrupted':\n return 'interrupted';\n default:\n return unknownValue(status, 'unknown');\n }\n}\n\nfunction stepStatus(status: ReportStep['status']): RawStatus {\n switch (status) {\n case 'passed':\n return 'pass';\n // Lossy: our step model has no \"blocked\" (the agent could not reach a\n // verdict) or \"timed-out\"; both stopped the scenario, so both read as fail.\n // The step's error message keeps the distinction.\n case 'failed':\n case 'blocked':\n case 'timed-out':\n return 'fail';\n // Lossy: a cancelled step never finished; it reads as not run.\n case 'cancelled':\n return 'skip';\n default:\n return unknownValue(status, 'unknown');\n }\n}\n\n/** Arrange, act or assert, by what the step did. Repeats become `And` in {@link toSteps}. */\nfunction primaryKeyword(step: ReportStep): 'Given' | 'When' | 'Then' {\n switch (step.kind) {\n case 'app':\n case 'session':\n case 'resource':\n return 'Given';\n case 'locator':\n case 'screen':\n return 'When';\n case 'assertion':\n return 'Then';\n case 'agent':\n return step.api === 'agent.assert' ? 'Then' : 'When';\n default:\n return unknownValue(step.kind, 'When');\n }\n}\n\n/** Agent steps are prose already; the rest are phrased from the API they called on what. */\nfunction stepText(step: ReportStep): string {\n if (step.kind === 'agent' && step.label) return step.label;\n return phraseStep(step.api, step.label);\n}\n\nfunction errorMessage(error: ReportError): string {\n return `${error.code}: ${error.message}`;\n}\n\n/**\n * One rendered step: a marker with the e2e steps that ran under it, or a\n * single e2e step when the test has no markers before it.\n */\ninterface Group {\n keyword: Marker['keyword'];\n text: string;\n marker: boolean;\n children: ReportStep[];\n}\n\nfunction toGroups(\n steps: readonly ReportStep[],\n markers: readonly Marker[],\n projectRoot: string,\n): Group[] {\n const groups: Group[] = [];\n let pending = 0;\n let current: Group | undefined;\n // A marker goes before a step that started after it. Timestamps are whole\n // milliseconds, so on a tie the source position decides: a marker written\n // before the step in the same file, on an earlier line or earlier on the\n // same line, came first. Elsewhere, the marker wins.\n const before = (m: Marker, step: ReportStep | undefined): boolean => {\n if (step === undefined) return true;\n const at = Date.parse(step.startedAt);\n if (m.at !== at) return m.at < at;\n if (m.file !== path.resolve(projectRoot, step.source.file)) return true;\n return (\n m.line < step.source.line ||\n (m.line === step.source.line && (m.column ?? 0) < step.source.column)\n );\n };\n const takeMarkersBefore = (step: ReportStep | undefined): void => {\n while (pending < markers.length && before(markers[pending]!, step)) {\n const m = markers[pending++]!;\n current = {\n keyword: m.keyword,\n text: m.text,\n marker: true,\n children: [],\n };\n groups.push(current);\n }\n };\n for (const step of steps) {\n takeMarkersBefore(step);\n if (current) current.children.push(step);\n else\n groups.push({\n keyword: primaryKeyword(step),\n text: stepText(step),\n marker: false,\n children: [step],\n });\n }\n takeMarkersBefore(undefined);\n return groups;\n}\n\n/** A step that never reached a verdict (blocked, timed out, cancelled) asserted nothing we can see. */\nfunction isObservedClaim(step: ReportStep): boolean {\n return (\n primaryKeyword(step) === 'Then' &&\n (step.status === 'passed' || step.status === 'failed')\n );\n}\n\nfunction groupStatus(group: Group): RawStatus {\n const statuses = group.children.map((c) => stepStatus(c.status));\n for (const worst of ['fail', 'unknown', 'skip'] as const)\n if (statuses.includes(worst)) return worst;\n return 'pass';\n}\n\n/** The agent's explanation, unless the error the report shows already says it. */\nfunction agentExplanation(step: ReportStep): DocEntry[] {\n const why = step.explanation?.trim();\n if (!why || step.error?.message.includes(why)) return [];\n return [{ kind: 'note', text: `Agent: ${why}`, phase: 'runtime' }];\n}\n\nfunction toSteps(groups: readonly Group[]): StoryStep[] {\n const out: StoryStep[] = [];\n groups.forEach((group, index) => {\n const repeat =\n group.keyword !== 'And' &&\n group.keyword !== 'But' &&\n out.some((s) => s.keyword === group.keyword);\n const keyword: StepKeyword = repeat ? 'And' : group.keyword;\n const claims = group.children.filter(isObservedClaim).length;\n // Only recorded claims are visible: e2e's synchronous value matchers\n // (`expect(2).toBe(2)`) leave no step. So 0 seen is not 0 made, and the\n // count is omitted rather than graded as unasserted.\n const assertions = claims > 0 ? { assertions: claims } : {};\n // A marker lists the e2e steps under it; any step adds the agent's own\n // account of what it did or why it judged a claim true.\n const docs: DocEntry[] = group.children.flatMap((c): DocEntry[] => [\n ...(group.marker\n ? [\n {\n kind: 'note' as const,\n text: stepText(c),\n phase: 'runtime' as const,\n },\n ]\n : []),\n ...agentExplanation(c),\n ]);\n out.push({\n id: `step-${index}`,\n keyword,\n text: group.text,\n durationMs: group.children.reduce((sum, c) => sum + c.durationMs, 0),\n ...assertions,\n ...(docs.length > 0 ? { docs } : {}),\n });\n });\n return out;\n}\n\n/**\n * Artifacts become attachments, the shape the Playwright adapter gives\n * screenshots, videos and traces. A step-produced artifact is pinned to its\n * step. A hosted video has a URL and no file, so it becomes a video doc.\n */\nfunction toMedia(\n attempt: {\n artifacts: readonly ReportArtifact[];\n steps: readonly ReportStep[];\n },\n groups: readonly Group[],\n artifactsRoot: string,\n): { attachments: RawAttachment[]; docs: DocEntry[] } {\n const attachments: RawAttachment[] = [];\n const docs: DocEntry[] = [];\n for (const artifact of attempt.artifacts) {\n if (artifact.path === undefined) {\n if (\n artifact.kind === 'video' &&\n artifact.url !== undefined &&\n /^https?:\\/\\//.test(artifact.url)\n ) {\n docs.push({ kind: 'video', path: artifact.url, phase: 'runtime' });\n }\n continue;\n }\n const stepIndex = groups.findIndex((group) =>\n group.children.some(\n (step) =>\n step.artifacts.includes(artifact.id) ||\n (artifact.producer?.kind === 'step' &&\n artifact.producer.stepId === step.id),\n ),\n );\n attachments.push({\n name: artifact.kind,\n mediaType: artifact.mediaType,\n path: path.join(artifactsRoot, artifact.path),\n fileName: path.basename(artifact.path),\n ...(artifact.size !== undefined ? { byteLength: artifact.size } : {}),\n ...(stepIndex >= 0 ? { stepIndex, stepId: `step-${stepIndex}` } : {}),\n });\n }\n return { attachments, docs };\n}\n\n/** The final attempt, wherever the report keeps it: a serial member's lives on its group. */\nfunction finalAttempt(\n result: ReportResult,\n report: Report,\n): {\n attempt?: Pick<\n ReportAttempt,\n 'error' | 'startedAt' | 'durationMs' | 'artifacts' | 'steps'\n >;\n count: number;\n} {\n if (result.serialGroupId === undefined) {\n return { attempt: result.attempts.at(-1), count: result.attempts.length };\n }\n const attempts =\n report.run.serialGroups.find((g) => g.id === result.serialGroupId)\n ?.attempts ?? [];\n const last = attempts.at(-1);\n const member = last?.members.find((m) => m.testId === result.testId);\n if (last === undefined) return { count: 0 };\n return {\n attempt: {\n error: member?.error ?? last.error,\n startedAt: member?.startedAt ?? last.startedAt,\n durationMs: member?.durationMs ?? last.durationMs,\n artifacts: last.artifacts,\n steps: member?.steps ?? [],\n },\n count: attempts.length,\n };\n}\n\n/**\n * The markers one attempt recorded: called from the test's own file, below its\n * declaration and above the next test's, while the attempt ran.\n */\nfunction markersFor(\n result: ReportResult,\n attempt: Pick<ReportAttempt, 'startedAt' | 'durationMs'> | undefined,\n report: Report,\n markers: readonly Marker[],\n projectRoot: string,\n): Marker[] {\n if (attempt === undefined || markers.length === 0) return [];\n const file = path.resolve(projectRoot, result.file);\n const from = result.source.line;\n const to = Math.min(\n ...report.run.results\n .filter((r) => r.file === result.file && r.source.line > from)\n .map((r) => r.source.line),\n );\n const start = Date.parse(attempt.startedAt);\n const end = start + attempt.durationMs + 1000; // slack for clock reads on either side\n // ponytail: two repeats of one test running at the same moment in different\n // workers cannot be told apart; markers would need the worker pid in the report.\n return markers\n .filter(\n (m) =>\n m.file === file &&\n m.line >= from &&\n m.line < to &&\n m.at >= start &&\n m.at <= end,\n )\n .sort((a, b) => a.at - b.at);\n}\n\nfunction toTestCase(\n result: ReportResult,\n report: Report,\n artifactsRoot: string,\n markers: readonly Marker[],\n projectRoot: string,\n): RawTestCase {\n const scenario = result.titlePath.at(-1) ?? result.testId;\n const suitePath = result.titlePath.slice(0, -1);\n const { attempt, count } = finalAttempt(result, report);\n const steps = attempt?.steps ?? [];\n const groups = toGroups(\n steps,\n markersFor(result, attempt, report, markers, projectRoot),\n projectRoot,\n );\n const { attachments, docs } = toMedia(\n { artifacts: attempt?.artifacts ?? [], steps },\n groups,\n artifactsRoot,\n );\n const error = attempt?.error;\n const details = error?.details;\n const stepEvents: RawStepEvent[] = groups.map((group, index) => {\n const failed = group.children.find((c) => c.error !== undefined);\n return {\n index,\n stepId: `step-${index}`,\n title: group.text,\n status: groupStatus(group),\n durationMs: group.children.reduce((sum, c) => sum + c.durationMs, 0),\n ...(failed?.error ? { errorMessage: errorMessage(failed.error) } : {}),\n };\n });\n\n return {\n externalId: result.id,\n title: scenario,\n titlePath: [...result.titlePath],\n story: {\n scenario,\n steps: toSteps(groups),\n ...(result.tags.length > 0 ? { tags: [...result.tags] } : {}),\n ...(suitePath.length > 0 ? { suitePath } : {}),\n ...(docs.length > 0 ? { docs } : {}),\n },\n sourceFile: result.file,\n sourceLine: Math.max(1, result.source.line),\n status: resultStatus(result.status),\n durationMs: attempt?.durationMs ?? 0,\n ...(error\n ? {\n error: {\n message: errorMessage(error),\n ...(details?.expected !== undefined\n ? { expected: details.expected }\n : {}),\n ...(details?.observed !== undefined\n ? { actual: details.observed }\n : {}),\n },\n }\n : {}),\n ...(stepEvents.length > 0 ? { stepEvents } : {}),\n ...(attachments.length > 0 ? { attachments } : {}),\n retry: Math.max(0, count - 1),\n projectName: result.targetId,\n };\n}\n\n/**\n * Map an e2e `report-1` document to a RawRun. Pure: artifact paths resolve\n * against `artifactsRoot` and nothing is read from disk.\n */\nexport function toRawRun(args: {\n report: Report;\n projectRoot: string;\n artifactsRoot: string;\n /** Set when the run was `--last-failed`: it then covered only part of the suite. */\n lastRun?: Report;\n /** Step markers the run's tests recorded with `story.given/when/then`. */\n markers?: readonly Marker[];\n}): RawRun {\n const { report, projectRoot, artifactsRoot } = args;\n // Unselected results are report-only, as e2e's own JUnit reporter treats them.\n const results = report.run.results.filter((r) => r.selected !== false);\n const filtered =\n args.lastRun !== undefined || results.length < report.run.results.length;\n return {\n testCases: results.map((r) =>\n toTestCase(r, report, artifactsRoot, args.markers ?? [], projectRoot),\n ),\n startedAtMs: Date.parse(report.run.startedAt),\n finishedAtMs: Date.parse(report.run.finishedAt),\n projectRoot,\n // Only \"filtered\" is claimed: e2e can narrow a run in ways the report\n // does not record, so \"full\" (which retires scenarios) is never guessed.\n ...(filtered ? { runScope: 'filtered' as const } : {}),\n coveredSourceFiles: [...new Set(results.map((r) => r.file))].sort(),\n ...(report.run.vcs?.commit ? { gitSha: report.run.vcs.commit } : {}),\n };\n}\n\n/** An e2e reporter that writes the run as RawRun JSON for the executable-stories CLI. */\nasync function readMarkers(dir: string): Promise<Marker[]> {\n const files = await readdir(dir).catch(() => [] as string[]);\n const markers: Marker[] = [];\n for (const file of files.filter((f) => f.endsWith('.jsonl'))) {\n for (const line of (await readFile(path.join(dir, file), 'utf8')).split(\n '\\n',\n )) {\n if (!line) continue;\n try {\n markers.push(JSON.parse(line) as Marker);\n } catch {\n // a worker killed mid-write leaves a torn last line; skip it\n }\n }\n }\n return markers;\n}\n\nexport function storyReporter(options: StoryReporterOptions = {}): Reporter {\n return {\n name: 'executable-stories',\n onEvent(event) {\n // Markers from an earlier run must not leak into this one. Small and\n // synchronous, as onEvent must not block for long.\n if (event.type === 'run-started')\n rmSync(path.resolve(event.projectRoot, MARKERS_DIR), {\n recursive: true,\n force: true,\n });\n },\n async onRunFinished(run: FinishedRun): Promise<ReporterSummary> {\n const markersDir = path.resolve(run.projectRoot, MARKERS_DIR);\n // An `e2e explore` run has findings, not tests: unverified leads that\n // belong in a bug bash, not in living docs. Writing would also replace\n // the last test run's raw-run.json with an empty one.\n if (run.report.run.explore !== undefined) {\n await rm(markersDir, { recursive: true, force: true });\n return [{ label: 'Stories', text: 'skipped (explore run)' }];\n }\n const rawRun = toRawRun({\n report: run.report,\n projectRoot: run.projectRoot,\n artifactsRoot: run.artifactsRoot,\n markers: await readMarkers(markersDir),\n ...(run.lastRun ? { lastRun: run.lastRun } : {}),\n });\n await rm(markersDir, { recursive: true, force: true });\n // A run that never reached a test (a device that failed to boot, an\n // invalid config) has nothing to say; an empty file would only replace\n // the last real run.\n if (rawRun.testCases.length === 0) {\n return [{ label: 'Stories', text: 'skipped (no tests ran)' }];\n }\n const target = path.resolve(\n run.projectRoot,\n options.rawRunPath ?? '.executable-stories/raw-run.json',\n );\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(\n target,\n JSON.stringify({ schemaVersion: 1, ...rawRun }, null, 2),\n 'utf8',\n );\n return [\n { label: 'Stories', text: path.relative(run.projectRoot, target) },\n ];\n },\n };\n}\n","/**\n * Plain-language step text for e2e's non-agent steps.\n *\n * e2e records a step as `api` + `label`, where the label is the locator's own\n * description (`getByRole(\"button\", name: \"Increment\")`). That reads as code\n * in a living-docs page, so the common shapes are rephrased here. Anything\n * this does not recognise keeps the raw `api label`, so nothing is lost.\n */\n\nconst ROLE_NOUNS: Record<string, string> = {\n textbox: 'field',\n searchbox: 'search field',\n combobox: 'dropdown',\n spinbutton: 'number field',\n img: 'image',\n};\n\n/** `getByKind(\"value\"[, name: \"name\"])` with plain string arguments only; scopes, filters, regexes and nth() stay raw. */\nconst SIMPLE_QUERY =\n /^getBy(\\w+)\\((\"(?:[^\"\\\\]|\\\\.)*\")(?:, name: (\"(?:[^\"\\\\]|\\\\.)*\"))?\\)$/;\n\nfunction unquote(json: string): string {\n try {\n return JSON.parse(json) as string;\n } catch {\n return json;\n }\n}\n\n/** A locator description as a noun phrase, or `undefined` when it is not a simple query. */\nexport function describeTarget(label: string): string | undefined {\n const match = SIMPLE_QUERY.exec(label);\n if (!match) return undefined;\n const [, kind, rawValue, rawName] = match;\n const value = unquote(rawValue!);\n const name = rawName === undefined ? undefined : unquote(rawName);\n switch (kind) {\n case 'Role': {\n const noun = ROLE_NOUNS[value] ?? value;\n return name === undefined ? `the ${noun}` : `the \"${name}\" ${noun}`;\n }\n case 'Text':\n return `the text \"${value}\"`;\n case 'Label':\n case 'Placeholder':\n return `the \"${value}\" field`;\n case 'AltText':\n return `the \"${value}\" image`;\n case 'TestId':\n case 'Title':\n return `the \"${value}\" element`;\n default:\n return undefined;\n }\n}\n\nconst ACTIONS: Record<string, string> = {\n click: 'click',\n tap: 'tap',\n dblclick: 'double-click',\n hover: 'hover over',\n check: 'check',\n uncheck: 'uncheck',\n fill: 'fill in',\n clear: 'clear',\n press: 'press a key on',\n pressSequentially: 'type into',\n selectOption: 'choose an option in',\n longPress: 'long-press',\n swipe: 'swipe',\n focus: 'focus',\n};\n\n/** `expect.toX` → what it claims about the target. */\nconst CLAIMS: Record<string, [positive: string, negative: string]> = {\n toBeVisible: ['is visible', 'is not visible'],\n toBeHidden: ['is hidden', 'is not hidden'],\n toBeEnabled: ['is enabled', 'is not enabled'],\n toBeDisabled: ['is disabled', 'is not disabled'],\n toBeChecked: ['is checked', 'is not checked'],\n toBeFocused: ['is focused', 'is not focused'],\n toBeSelected: ['is selected', 'is not selected'],\n toBeExpanded: ['is expanded', 'is not expanded'],\n toBeAttached: ['is on the page', 'is not on the page'],\n toHaveText: ['shows the expected text', 'does not show the given text'],\n toContainText: [\n 'contains the expected text',\n 'does not contain the given text',\n ],\n toHaveValue: ['has the expected value', 'does not have the given value'],\n toHaveCount: [\n 'appears the expected number of times',\n 'does not appear the given number of times',\n ],\n toHaveAccessibleName: [\n 'has the expected accessible name',\n 'does not have the given accessible name',\n ],\n toHaveAttribute: [\n 'has the expected attribute',\n 'does not have the given attribute',\n ],\n};\n\nconst APP: Record<string, string> = {\n 'app.back': 'go back',\n 'app.restart': 'restart the app',\n 'app.clearState': 'clear the app state',\n 'session.launch': 'launch the app session',\n 'session.close': 'close the app session',\n};\n\n/** Step text for one non-agent step. */\nexport function phraseStep(api: string, label: string): string {\n const raw = label ? `${api} ${label}` : api;\n\n if (api === 'app.open') return label ? `open ${label}` : 'open the app';\n if (api === 'app.screenshot')\n return label ? `take a screenshot: ${label}` : 'take a screenshot';\n if (api === 'session.save')\n return label ? `save the session as ${label}` : 'save the session';\n if (APP[api]) return APP[api];\n\n const action = /^locator\\.(\\w+)$/.exec(api)?.[1];\n if (action !== undefined) {\n if (action === 'waitFor') {\n // `<target> → <state>`\n const [target, state] = label.split(' → ');\n const noun = target === undefined ? undefined : describeTarget(target);\n return noun && state ? `wait until ${noun} is ${state}` : raw;\n }\n const verb = ACTIONS[action];\n const noun = describeTarget(label);\n return verb && noun ? `${verb} ${noun}` : raw;\n }\n\n const claim = /^expect\\.(not\\.)?(\\w+)$/.exec(api);\n if (claim) {\n const words = CLAIMS[claim[2]!];\n const noun = describeTarget(label);\n return words && noun ? `${noun} ${words[claim[1] ? 1 : 0]}` : raw;\n }\n\n return raw;\n}\n","/**\n * Step markers for e2e tests.\n *\n * e2e has no step API of its own and no channel from a test back to a\n * reporter, so a marker records where it was called and when. The reporter\n * then groups the e2e steps that ran after a marker under its text:\n *\n * ```ts\n * test('incrementing shows the new count', async ({ app, screen }) => {\n * story.given('the counter page is open');\n * await app.open('/');\n * story.when('the user presses Increment twice');\n * await screen.getByRole('button', { name: 'Increment' }).click();\n * await screen.getByRole('button', { name: 'Increment' }).click();\n * story.then('the count reads 2');\n * await expect(screen.getByRole('status')).toHaveText('2');\n * });\n * ```\n */\n\nimport { appendFileSync, mkdirSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport type MarkerKeyword = 'Given' | 'When' | 'Then' | 'And' | 'But';\n\n/** One line of a markers file. */\nexport interface Marker {\n keyword: MarkerKeyword;\n text: string;\n /** Absolute path of the test file that called the marker. */\n file: string;\n line: number;\n /** Orders a marker before a step on its own line; absent reads as the line's start. */\n column?: number;\n /** Epoch milliseconds. */\n at: number;\n}\n\n/** Workers and the reporter both resolve this against the project root e2e runs from. */\nexport const MARKERS_DIR = '.executable-stories/e2e-markers';\n\nconst FRAME = /\\(?((?:file:\\/\\/)?[^\\s()]+?):(\\d+):(\\d+)\\)?$/;\n\n/** The first stack frame outside this module: the test line that called the marker. */\nfunction callSite():\n { file: string; line: number; column: number } | undefined {\n const frames = (new Error().stack ?? '').split('\\n').slice(1);\n let own: string | undefined;\n for (const frame of frames) {\n const match = FRAME.exec(frame.trim());\n if (!match) continue;\n const file = match[1]!.startsWith('file://')\n ? fileURLToPath(match[1]!)\n : match[1]!;\n own ??= file;\n if (file !== own)\n return { file, line: Number(match[2]), column: Number(match[3]) };\n }\n return undefined;\n}\n\nfunction mark(keyword: MarkerKeyword, text: string): void {\n const site = callSite();\n if (site === undefined) return;\n const marker: Marker = { keyword, text, ...site, at: Date.now() };\n const dir = path.resolve(MARKERS_DIR);\n mkdirSync(dir, { recursive: true });\n // One file per worker process; appends from one process stay in order.\n appendFileSync(\n path.join(dir, `${process.pid}.jsonl`),\n `${JSON.stringify(marker)}\\n`,\n );\n}\n\n/** Step markers. Call before the e2e steps the marker describes; they render under its text. */\nexport const story = {\n given: (text: string): void => mark('Given', text),\n when: (text: string): void => mark('When', text),\n then: (text: string): void => mark('Then', text),\n and: (text: string): void => mark('And', text),\n but: (text: string): void => mark('But', text),\n};\n"],"mappings":";AASA,SAAS,cAAc;AACvB,SAAS,OAAO,SAAS,UAAU,IAAI,iBAAiB;AACxD,YAAYA,WAAU;;;ACFtB,IAAM,aAAqC;AAAA,EACzC,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,KAAK;AACP;AAGA,IAAM,eACJ;AAEF,SAAS,QAAQ,MAAsB;AACrC,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,eAAe,OAAmC;AAChE,QAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,CAAC,EAAE,MAAM,UAAU,OAAO,IAAI;AACpC,QAAM,QAAQ,QAAQ,QAAS;AAC/B,QAAM,OAAO,YAAY,SAAY,SAAY,QAAQ,OAAO;AAChE,UAAQ,MAAM;AAAA,IACZ,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,KAAK,KAAK;AAClC,aAAO,SAAS,SAAY,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,IAAI;AAAA,IACnE;AAAA,IACA,KAAK;AACH,aAAO,aAAa,KAAK;AAAA,IAC3B,KAAK;AAAA,IACL,KAAK;AACH,aAAO,QAAQ,KAAK;AAAA,IACtB,KAAK;AACH,aAAO,QAAQ,KAAK;AAAA,IACtB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,QAAQ,KAAK;AAAA,IACtB;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAM,UAAkC;AAAA,EACtC,OAAO;AAAA,EACP,KAAK;AAAA,EACL,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,OAAO;AAAA,EACP,OAAO;AACT;AAGA,IAAM,SAA+D;AAAA,EACnE,aAAa,CAAC,cAAc,gBAAgB;AAAA,EAC5C,YAAY,CAAC,aAAa,eAAe;AAAA,EACzC,aAAa,CAAC,cAAc,gBAAgB;AAAA,EAC5C,cAAc,CAAC,eAAe,iBAAiB;AAAA,EAC/C,aAAa,CAAC,cAAc,gBAAgB;AAAA,EAC5C,aAAa,CAAC,cAAc,gBAAgB;AAAA,EAC5C,cAAc,CAAC,eAAe,iBAAiB;AAAA,EAC/C,cAAc,CAAC,eAAe,iBAAiB;AAAA,EAC/C,cAAc,CAAC,kBAAkB,oBAAoB;AAAA,EACrD,YAAY,CAAC,2BAA2B,8BAA8B;AAAA,EACtE,eAAe;AAAA,IACb;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa,CAAC,0BAA0B,+BAA+B;AAAA,EACvE,aAAa;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAAA,EACA,sBAAsB;AAAA,IACpB;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,MAA8B;AAAA,EAClC,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAiB;AACnB;AAGO,SAAS,WAAW,KAAa,OAAuB;AAC7D,QAAM,MAAM,QAAQ,GAAG,GAAG,IAAI,KAAK,KAAK;AAExC,MAAI,QAAQ,WAAY,QAAO,QAAQ,QAAQ,KAAK,KAAK;AACzD,MAAI,QAAQ;AACV,WAAO,QAAQ,sBAAsB,KAAK,KAAK;AACjD,MAAI,QAAQ;AACV,WAAO,QAAQ,uBAAuB,KAAK,KAAK;AAClD,MAAI,IAAI,GAAG,EAAG,QAAO,IAAI,GAAG;AAE5B,QAAM,SAAS,mBAAmB,KAAK,GAAG,IAAI,CAAC;AAC/C,MAAI,WAAW,QAAW;AACxB,QAAI,WAAW,WAAW;AAExB,YAAM,CAAC,QAAQ,KAAK,IAAI,MAAM,MAAM,UAAK;AACzC,YAAMC,QAAO,WAAW,SAAY,SAAY,eAAe,MAAM;AACrE,aAAOA,SAAQ,QAAQ,cAAcA,KAAI,OAAO,KAAK,KAAK;AAAA,IAC5D;AACA,UAAM,OAAO,QAAQ,MAAM;AAC3B,UAAM,OAAO,eAAe,KAAK;AACjC,WAAO,QAAQ,OAAO,GAAG,IAAI,IAAI,IAAI,KAAK;AAAA,EAC5C;AAEA,QAAM,QAAQ,0BAA0B,KAAK,GAAG;AAChD,MAAI,OAAO;AACT,UAAM,QAAQ,OAAO,MAAM,CAAC,CAAE;AAC9B,UAAM,OAAO,eAAe,KAAK;AACjC,WAAO,SAAS,OAAO,GAAG,IAAI,IAAI,MAAM,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK;AAAA,EAChE;AAEA,SAAO;AACT;;;AC5HA,SAAS,gBAAgB,iBAAiB;AAC1C,YAAY,UAAU;AACtB,SAAS,qBAAqB;AAkBvB,IAAM,cAAc;AAE3B,IAAM,QAAQ;AAGd,SAAS,WACoD;AAC3D,QAAM,UAAU,IAAI,MAAM,EAAE,SAAS,IAAI,MAAM,IAAI,EAAE,MAAM,CAAC;AAC5D,MAAI;AACJ,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,CAAC;AACrC,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,MAAM,CAAC,EAAG,WAAW,SAAS,IACvC,cAAc,MAAM,CAAC,CAAE,IACvB,MAAM,CAAC;AACX,YAAQ;AACR,QAAI,SAAS;AACX,aAAO,EAAE,MAAM,MAAM,OAAO,MAAM,CAAC,CAAC,GAAG,QAAQ,OAAO,MAAM,CAAC,CAAC,EAAE;AAAA,EACpE;AACA,SAAO;AACT;AAEA,SAAS,KAAK,SAAwB,MAAoB;AACxD,QAAM,OAAO,SAAS;AACtB,MAAI,SAAS,OAAW;AACxB,QAAM,SAAiB,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,KAAK,IAAI,EAAE;AAChE,QAAM,MAAW,aAAQ,WAAW;AACpC,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAElC;AAAA,IACO,UAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ;AAAA,IACrC,GAAG,KAAK,UAAU,MAAM,CAAC;AAAA;AAAA,EAC3B;AACF;AAGO,IAAM,QAAQ;AAAA,EACnB,OAAO,CAAC,SAAuB,KAAK,SAAS,IAAI;AAAA,EACjD,MAAM,CAAC,SAAuB,KAAK,QAAQ,IAAI;AAAA,EAC/C,MAAM,CAAC,SAAuB,KAAK,QAAQ,IAAI;AAAA,EAC/C,KAAK,CAAC,SAAuB,KAAK,OAAO,IAAI;AAAA,EAC7C,KAAK,CAAC,SAAuB,KAAK,OAAO,IAAI;AAC/C;;;AFnCA,SAAS,aAAgB,QAAe,UAAgB;AACtD,SAAO;AACT;AAEA,SAAS,aAAa,QAA2C;AAC/D,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO,aAAa,QAAQ,SAAS;AAAA,EACzC;AACF;AAEA,SAAS,WAAW,QAAyC;AAC3D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA,IAIT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA;AAAA,IAET,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO,aAAa,QAAQ,SAAS;AAAA,EACzC;AACF;AAGA,SAAS,eAAe,MAA6C;AACnE,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,KAAK,QAAQ,iBAAiB,SAAS;AAAA,IAChD;AACE,aAAO,aAAa,KAAK,MAAM,MAAM;AAAA,EACzC;AACF;AAGA,SAAS,SAAS,MAA0B;AAC1C,MAAI,KAAK,SAAS,WAAW,KAAK,MAAO,QAAO,KAAK;AACrD,SAAO,WAAW,KAAK,KAAK,KAAK,KAAK;AACxC;AAEA,SAAS,aAAa,OAA4B;AAChD,SAAO,GAAG,MAAM,IAAI,KAAK,MAAM,OAAO;AACxC;AAaA,SAAS,SACP,OACA,SACA,aACS;AACT,QAAM,SAAkB,CAAC;AACzB,MAAI,UAAU;AACd,MAAI;AAKJ,QAAM,SAAS,CAAC,GAAW,SAA0C;AACnE,QAAI,SAAS,OAAW,QAAO;AAC/B,UAAM,KAAK,KAAK,MAAM,KAAK,SAAS;AACpC,QAAI,EAAE,OAAO,GAAI,QAAO,EAAE,KAAK;AAC/B,QAAI,EAAE,SAAc,cAAQ,aAAa,KAAK,OAAO,IAAI,EAAG,QAAO;AACnE,WACE,EAAE,OAAO,KAAK,OAAO,QACpB,EAAE,SAAS,KAAK,OAAO,SAAS,EAAE,UAAU,KAAK,KAAK,OAAO;AAAA,EAElE;AACA,QAAM,oBAAoB,CAAC,SAAuC;AAChE,WAAO,UAAU,QAAQ,UAAU,OAAO,QAAQ,OAAO,GAAI,IAAI,GAAG;AAClE,YAAM,IAAI,QAAQ,SAAS;AAC3B,gBAAU;AAAA,QACR,SAAS,EAAE;AAAA,QACX,MAAM,EAAE;AAAA,QACR,QAAQ;AAAA,QACR,UAAU,CAAC;AAAA,MACb;AACA,aAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF;AACA,aAAW,QAAQ,OAAO;AACxB,sBAAkB,IAAI;AACtB,QAAI,QAAS,SAAQ,SAAS,KAAK,IAAI;AAAA;AAErC,aAAO,KAAK;AAAA,QACV,SAAS,eAAe,IAAI;AAAA,QAC5B,MAAM,SAAS,IAAI;AAAA,QACnB,QAAQ;AAAA,QACR,UAAU,CAAC,IAAI;AAAA,MACjB,CAAC;AAAA,EACL;AACA,oBAAkB,MAAS;AAC3B,SAAO;AACT;AAGA,SAAS,gBAAgB,MAA2B;AAClD,SACE,eAAe,IAAI,MAAM,WACxB,KAAK,WAAW,YAAY,KAAK,WAAW;AAEjD;AAEA,SAAS,YAAY,OAAyB;AAC5C,QAAM,WAAW,MAAM,SAAS,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,CAAC;AAC/D,aAAW,SAAS,CAAC,QAAQ,WAAW,MAAM;AAC5C,QAAI,SAAS,SAAS,KAAK,EAAG,QAAO;AACvC,SAAO;AACT;AAGA,SAAS,iBAAiB,MAA8B;AACtD,QAAM,MAAM,KAAK,aAAa,KAAK;AACnC,MAAI,CAAC,OAAO,KAAK,OAAO,QAAQ,SAAS,GAAG,EAAG,QAAO,CAAC;AACvD,SAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,GAAG,IAAI,OAAO,UAAU,CAAC;AACnE;AAEA,SAAS,QAAQ,QAAuC;AACtD,QAAM,MAAmB,CAAC;AAC1B,SAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,UAAM,SACJ,MAAM,YAAY,SAClB,MAAM,YAAY,SAClB,IAAI,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,OAAO;AAC7C,UAAM,UAAuB,SAAS,QAAQ,MAAM;AACpD,UAAM,SAAS,MAAM,SAAS,OAAO,eAAe,EAAE;AAItD,UAAM,aAAa,SAAS,IAAI,EAAE,YAAY,OAAO,IAAI,CAAC;AAG1D,UAAM,OAAmB,MAAM,SAAS,QAAQ,CAAC,MAAkB;AAAA,MACjE,GAAI,MAAM,SACN;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,MAAM,SAAS,CAAC;AAAA,UAChB,OAAO;AAAA,QACT;AAAA,MACF,IACA,CAAC;AAAA,MACL,GAAG,iBAAiB,CAAC;AAAA,IACvB,CAAC;AACD,QAAI,KAAK;AAAA,MACP,IAAI,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,YAAY,MAAM,SAAS,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC;AAAA,MACnE,GAAG;AAAA,MACH,GAAI,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AACT;AAOA,SAAS,QACP,SAIA,QACA,eACoD;AACpD,QAAM,cAA+B,CAAC;AACtC,QAAM,OAAmB,CAAC;AAC1B,aAAW,YAAY,QAAQ,WAAW;AACxC,QAAI,SAAS,SAAS,QAAW;AAC/B,UACE,SAAS,SAAS,WAClB,SAAS,QAAQ,UACjB,eAAe,KAAK,SAAS,GAAG,GAChC;AACA,aAAK,KAAK,EAAE,MAAM,SAAS,MAAM,SAAS,KAAK,OAAO,UAAU,CAAC;AAAA,MACnE;AACA;AAAA,IACF;AACA,UAAM,YAAY,OAAO;AAAA,MAAU,CAAC,UAClC,MAAM,SAAS;AAAA,QACb,CAAC,SACC,KAAK,UAAU,SAAS,SAAS,EAAE,KAClC,SAAS,UAAU,SAAS,UAC3B,SAAS,SAAS,WAAW,KAAK;AAAA,MACxC;AAAA,IACF;AACA,gBAAY,KAAK;AAAA,MACf,MAAM,SAAS;AAAA,MACf,WAAW,SAAS;AAAA,MACpB,MAAW,WAAK,eAAe,SAAS,IAAI;AAAA,MAC5C,UAAe,eAAS,SAAS,IAAI;AAAA,MACrC,GAAI,SAAS,SAAS,SAAY,EAAE,YAAY,SAAS,KAAK,IAAI,CAAC;AAAA,MACnE,GAAI,aAAa,IAAI,EAAE,WAAW,QAAQ,QAAQ,SAAS,GAAG,IAAI,CAAC;AAAA,IACrE,CAAC;AAAA,EACH;AACA,SAAO,EAAE,aAAa,KAAK;AAC7B;AAGA,SAAS,aACP,QACA,QAOA;AACA,MAAI,OAAO,kBAAkB,QAAW;AACtC,WAAO,EAAE,SAAS,OAAO,SAAS,GAAG,EAAE,GAAG,OAAO,OAAO,SAAS,OAAO;AAAA,EAC1E;AACA,QAAM,WACJ,OAAO,IAAI,aAAa,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,aAAa,GAC7D,YAAY,CAAC;AACnB,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,QAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO,MAAM;AACnE,MAAI,SAAS,OAAW,QAAO,EAAE,OAAO,EAAE;AAC1C,SAAO;AAAA,IACL,SAAS;AAAA,MACP,OAAO,QAAQ,SAAS,KAAK;AAAA,MAC7B,WAAW,QAAQ,aAAa,KAAK;AAAA,MACrC,YAAY,QAAQ,cAAc,KAAK;AAAA,MACvC,WAAW,KAAK;AAAA,MAChB,OAAO,QAAQ,SAAS,CAAC;AAAA,IAC3B;AAAA,IACA,OAAO,SAAS;AAAA,EAClB;AACF;AAMA,SAAS,WACP,QACA,SACA,QACA,SACA,aACU;AACV,MAAI,YAAY,UAAa,QAAQ,WAAW,EAAG,QAAO,CAAC;AAC3D,QAAM,OAAY,cAAQ,aAAa,OAAO,IAAI;AAClD,QAAM,OAAO,OAAO,OAAO;AAC3B,QAAM,KAAK,KAAK;AAAA,IACd,GAAG,OAAO,IAAI,QACX,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,QAAQ,EAAE,OAAO,OAAO,IAAI,EAC5D,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI;AAAA,EAC7B;AACA,QAAM,QAAQ,KAAK,MAAM,QAAQ,SAAS;AAC1C,QAAM,MAAM,QAAQ,QAAQ,aAAa;AAGzC,SAAO,QACJ;AAAA,IACC,CAAC,MACC,EAAE,SAAS,QACX,EAAE,QAAQ,QACV,EAAE,OAAO,MACT,EAAE,MAAM,SACR,EAAE,MAAM;AAAA,EACZ,EACC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;AAC/B;AAEA,SAAS,WACP,QACA,QACA,eACA,SACA,aACa;AACb,QAAM,WAAW,OAAO,UAAU,GAAG,EAAE,KAAK,OAAO;AACnD,QAAM,YAAY,OAAO,UAAU,MAAM,GAAG,EAAE;AAC9C,QAAM,EAAE,SAAS,MAAM,IAAI,aAAa,QAAQ,MAAM;AACtD,QAAM,QAAQ,SAAS,SAAS,CAAC;AACjC,QAAM,SAAS;AAAA,IACb;AAAA,IACA,WAAW,QAAQ,SAAS,QAAQ,SAAS,WAAW;AAAA,IACxD;AAAA,EACF;AACA,QAAM,EAAE,aAAa,KAAK,IAAI;AAAA,IAC5B,EAAE,WAAW,SAAS,aAAa,CAAC,GAAG,MAAM;AAAA,IAC7C;AAAA,IACA;AAAA,EACF;AACA,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,OAAO;AACvB,QAAM,aAA6B,OAAO,IAAI,CAAC,OAAO,UAAU;AAC9D,UAAM,SAAS,MAAM,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,MAAS;AAC/D,WAAO;AAAA,MACL;AAAA,MACA,QAAQ,QAAQ,KAAK;AAAA,MACrB,OAAO,MAAM;AAAA,MACb,QAAQ,YAAY,KAAK;AAAA,MACzB,YAAY,MAAM,SAAS,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC;AAAA,MACnE,GAAI,QAAQ,QAAQ,EAAE,cAAc,aAAa,OAAO,KAAK,EAAE,IAAI,CAAC;AAAA,IACtE;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB,OAAO;AAAA,IACP,WAAW,CAAC,GAAG,OAAO,SAAS;AAAA,IAC/B,OAAO;AAAA,MACL;AAAA,MACA,OAAO,QAAQ,MAAM;AAAA,MACrB,GAAI,OAAO,KAAK,SAAS,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,IAAI,EAAE,IAAI,CAAC;AAAA,MAC3D,GAAI,UAAU,SAAS,IAAI,EAAE,UAAU,IAAI,CAAC;AAAA,MAC5C,GAAI,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IACpC;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,YAAY,KAAK,IAAI,GAAG,OAAO,OAAO,IAAI;AAAA,IAC1C,QAAQ,aAAa,OAAO,MAAM;AAAA,IAClC,YAAY,SAAS,cAAc;AAAA,IACnC,GAAI,QACA;AAAA,MACE,OAAO;AAAA,QACL,SAAS,aAAa,KAAK;AAAA,QAC3B,GAAI,SAAS,aAAa,SACtB,EAAE,UAAU,QAAQ,SAAS,IAC7B,CAAC;AAAA,QACL,GAAI,SAAS,aAAa,SACtB,EAAE,QAAQ,QAAQ,SAAS,IAC3B,CAAC;AAAA,MACP;AAAA,IACF,IACA,CAAC;AAAA,IACL,GAAI,WAAW,SAAS,IAAI,EAAE,WAAW,IAAI,CAAC;AAAA,IAC9C,GAAI,YAAY,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,IAChD,OAAO,KAAK,IAAI,GAAG,QAAQ,CAAC;AAAA,IAC5B,aAAa,OAAO;AAAA,EACtB;AACF;AAMO,SAAS,SAAS,MAQd;AACT,QAAM,EAAE,QAAQ,aAAa,cAAc,IAAI;AAE/C,QAAM,UAAU,OAAO,IAAI,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK;AACrE,QAAM,WACJ,KAAK,YAAY,UAAa,QAAQ,SAAS,OAAO,IAAI,QAAQ;AACpE,SAAO;AAAA,IACL,WAAW,QAAQ;AAAA,MAAI,CAAC,MACtB,WAAW,GAAG,QAAQ,eAAe,KAAK,WAAW,CAAC,GAAG,WAAW;AAAA,IACtE;AAAA,IACA,aAAa,KAAK,MAAM,OAAO,IAAI,SAAS;AAAA,IAC5C,cAAc,KAAK,MAAM,OAAO,IAAI,UAAU;AAAA,IAC9C;AAAA;AAAA;AAAA,IAGA,GAAI,WAAW,EAAE,UAAU,WAAoB,IAAI,CAAC;AAAA,IACpD,oBAAoB,CAAC,GAAG,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,KAAK;AAAA,IAClE,GAAI,OAAO,IAAI,KAAK,SAAS,EAAE,QAAQ,OAAO,IAAI,IAAI,OAAO,IAAI,CAAC;AAAA,EACpE;AACF;AAGA,eAAe,YAAY,KAAgC;AACzD,QAAM,QAAQ,MAAM,QAAQ,GAAG,EAAE,MAAM,MAAM,CAAC,CAAa;AAC3D,QAAM,UAAoB,CAAC;AAC3B,aAAW,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,GAAG;AAC5D,eAAW,SAAS,MAAM,SAAc,WAAK,KAAK,IAAI,GAAG,MAAM,GAAG;AAAA,MAChE;AAAA,IACF,GAAG;AACD,UAAI,CAAC,KAAM;AACX,UAAI;AACF,gBAAQ,KAAK,KAAK,MAAM,IAAI,CAAW;AAAA,MACzC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,cAAc,UAAgC,CAAC,GAAa;AAC1E,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,OAAO;AAGb,UAAI,MAAM,SAAS;AACjB,eAAY,cAAQ,MAAM,aAAa,WAAW,GAAG;AAAA,UACnD,WAAW;AAAA,UACX,OAAO;AAAA,QACT,CAAC;AAAA,IACL;AAAA,IACA,MAAM,cAAc,KAA4C;AAC9D,YAAM,aAAkB,cAAQ,IAAI,aAAa,WAAW;AAI5D,UAAI,IAAI,OAAO,IAAI,YAAY,QAAW;AACxC,cAAM,GAAG,YAAY,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACrD,eAAO,CAAC,EAAE,OAAO,WAAW,MAAM,wBAAwB,CAAC;AAAA,MAC7D;AACA,YAAM,SAAS,SAAS;AAAA,QACtB,QAAQ,IAAI;AAAA,QACZ,aAAa,IAAI;AAAA,QACjB,eAAe,IAAI;AAAA,QACnB,SAAS,MAAM,YAAY,UAAU;AAAA,QACrC,GAAI,IAAI,UAAU,EAAE,SAAS,IAAI,QAAQ,IAAI,CAAC;AAAA,MAChD,CAAC;AACD,YAAM,GAAG,YAAY,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAIrD,UAAI,OAAO,UAAU,WAAW,GAAG;AACjC,eAAO,CAAC,EAAE,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAAA,MAC9D;AACA,YAAM,SAAc;AAAA,QAClB,IAAI;AAAA,QACJ,QAAQ,cAAc;AAAA,MACxB;AACA,YAAM,MAAW,cAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,YAAM;AAAA,QACJ;AAAA,QACA,KAAK,UAAU,EAAE,eAAe,GAAG,GAAG,OAAO,GAAG,MAAM,CAAC;AAAA,QACvD;AAAA,MACF;AACA,aAAO;AAAA,QACL,EAAE,OAAO,WAAW,MAAW,eAAS,IAAI,aAAa,MAAM,EAAE;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACF;","names":["path","noun"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "executable-stories-e2e",
|
|
3
|
-
"version": "0.0.0
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "executable-stories reporter for the e2e agentic test runner: turns its report-1 document into story docs",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"e2e": ">=0.15.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"executable-stories-core": "0.28.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^26.6.1",
|
|
30
|
+
"e2e": "0.15.1",
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"typescript": "~6.0.3",
|
|
33
|
+
"vitest": "^5.0.1",
|
|
34
|
+
"executable-stories-formatters": "1.22.0",
|
|
35
|
+
"executable-stories-vitest": "8.10.0",
|
|
36
|
+
"eslint-config-executable-stories": "0.2.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=22.12.0"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/jagreehal/executable-stories.git",
|
|
44
|
+
"directory": "packages/executable-stories-e2e"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"e2e",
|
|
48
|
+
"agentic-testing",
|
|
49
|
+
"bdd",
|
|
50
|
+
"documentation",
|
|
51
|
+
"executable-stories"
|
|
52
|
+
],
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"lint": "eslint .",
|
|
56
|
+
"type-check": "tsc --noEmit",
|
|
57
|
+
"test": "vitest run"
|
|
58
|
+
}
|
|
6
59
|
}
|