executable-stories-formatters 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/adapters.cjs +17 -0
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +3 -1
- package/dist/adapters.d.ts +3 -1
- package/dist/adapters.js +17 -0
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +12227 -11022
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +9553 -9066
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +310 -168
- package/dist/index.d.ts +310 -168
- package/dist/index.js +9392 -8898
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/schemas/README.md +4 -0
- package/schemas/raw-run.schema.json +100 -0
- package/templates/astro-thin/executable-stories.config.mjs +11 -6
- package/templates/astro-thin/src/content/docs/index.mdx +2 -2
package/README.md
CHANGED
|
@@ -47,6 +47,12 @@ const results = await generator.generate(run);
|
|
|
47
47
|
// results: Map<"html" | "markdown", string[]> (file paths written)
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
`generate(run)` updates canonical state under `reports/by-file/`, one report per
|
|
51
|
+
test source, then renders. Documentation formats use that accumulated suite. JUnit,
|
|
52
|
+
Cucumber, and `release-manifest` are execution evidence and contain only `run`. Use
|
|
53
|
+
`generate(run, { persist: false })` to render an already-assembled snapshot without
|
|
54
|
+
changing persistent state.
|
|
55
|
+
|
|
50
56
|
## Quick Start — CLI
|
|
51
57
|
|
|
52
58
|
```bash
|
|
@@ -59,6 +65,10 @@ executable-stories format run.json --format html --format markdown
|
|
|
59
65
|
# Validate a run file against the schema
|
|
60
66
|
executable-stories validate run.json
|
|
61
67
|
|
|
68
|
+
# Inspect or reset persistent per-source state
|
|
69
|
+
executable-stories runs status
|
|
70
|
+
executable-stories runs reset
|
|
71
|
+
|
|
62
72
|
# Backpressure summary for coding agents: compress passing, expand failing
|
|
63
73
|
executable-stories check .executable-stories/raw-run.json
|
|
64
74
|
|
package/dist/adapters.cjs
CHANGED
|
@@ -146,12 +146,16 @@ function mapJestStatus(status) {
|
|
|
146
146
|
}
|
|
147
147
|
function adaptJestRun(jestResults, storyReports, options = {}) {
|
|
148
148
|
const testCases = [];
|
|
149
|
+
const features = [];
|
|
149
150
|
const fileResultsMap = /* @__PURE__ */ new Map();
|
|
150
151
|
for (const fileResult of jestResults.testResults) {
|
|
151
152
|
fileResultsMap.set(fileResult.testFilePath, fileResult);
|
|
152
153
|
}
|
|
153
154
|
for (const report of storyReports) {
|
|
154
155
|
const fileResult = fileResultsMap.get(report.testFilePath);
|
|
156
|
+
if (report.feature?.title) {
|
|
157
|
+
features.push({ ...report.feature, sourceFile: report.testFilePath });
|
|
158
|
+
}
|
|
155
159
|
for (const meta of report.scenarios) {
|
|
156
160
|
if (!meta?.scenario) continue;
|
|
157
161
|
const matchingTest = findMatchingJestTest(fileResult, meta);
|
|
@@ -179,6 +183,7 @@ function adaptJestRun(jestResults, storyReports, options = {}) {
|
|
|
179
183
|
}
|
|
180
184
|
return {
|
|
181
185
|
testCases,
|
|
186
|
+
...features.length > 0 ? { features } : {},
|
|
182
187
|
startedAtMs: jestResults.startTime,
|
|
183
188
|
finishedAtMs: Date.now(),
|
|
184
189
|
projectRoot: options.projectRoot ?? process.cwd(),
|
|
@@ -288,10 +293,21 @@ function mapPlaywrightStatus(status) {
|
|
|
288
293
|
}
|
|
289
294
|
function adaptPlaywrightRun(testResults, options = {}) {
|
|
290
295
|
const testCases = [];
|
|
296
|
+
const featuresByFile = /* @__PURE__ */ new Map();
|
|
291
297
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
292
298
|
for (const [test, result] of testResults) {
|
|
293
299
|
const storyAnnotation = test.annotations.find((a) => a.type === "story-meta");
|
|
294
300
|
if (!storyAnnotation?.description) continue;
|
|
301
|
+
const featureAnnotation = test.annotations.find((a) => a.type === "story-feature");
|
|
302
|
+
if (featureAnnotation?.description && !featuresByFile.has(test.location.file)) {
|
|
303
|
+
try {
|
|
304
|
+
const declared = JSON.parse(featureAnnotation.description);
|
|
305
|
+
if (declared?.title) {
|
|
306
|
+
featuresByFile.set(test.location.file, { ...declared, sourceFile: test.location.file });
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
295
311
|
let story;
|
|
296
312
|
try {
|
|
297
313
|
story = JSON.parse(storyAnnotation.description);
|
|
@@ -335,6 +351,7 @@ function adaptPlaywrightRun(testResults, options = {}) {
|
|
|
335
351
|
}
|
|
336
352
|
return {
|
|
337
353
|
testCases,
|
|
354
|
+
...featuresByFile.size > 0 ? { features: [...featuresByFile.values()] } : {},
|
|
338
355
|
startedAtMs: options.startedAtMs,
|
|
339
356
|
finishedAtMs: Date.now(),
|
|
340
357
|
projectRoot,
|
package/dist/adapters.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/converters/adapters/index.ts","../src/utils/ci-detect.ts","../src/converters/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts"],"sourcesContent":["/**\n * Framework adapters - Layer 1.\n *\n * Transform framework-specific data to RawRun for ACL processing.\n */\n\nexport {\n adaptJestRun,\n type JestTestResult,\n type JestFileResult,\n type JestAggregatedResult,\n type StoryFileReport,\n type JestAdapterOptions,\n} from \"./jest\";\n\nexport {\n adaptVitestRun,\n type VitestState,\n type VitestSerializedError,\n type VitestTestResult,\n type VitestTestCase,\n type VitestTestModule,\n type VitestAdapterOptions,\n} from \"./vitest\";\n\nexport {\n adaptPlaywrightRun,\n type PlaywrightStatus,\n type PlaywrightError,\n type PlaywrightAttachment,\n type PlaywrightTestResult,\n type PlaywrightAnnotation,\n type PlaywrightLocation,\n type PlaywrightTestCase,\n type PlaywrightAdapterOptions,\n} from \"./playwright\";\n","/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables and populates\n * branch, commit SHA, PR number, build number, and URL metadata.\n *\n * Precedence (checked in order, first match wins):\n * 1. TF_BUILD=True -> azure\n * 2. BUILDKITE=true -> buildkite\n * 3. GITHUB_ACTIONS=true -> github\n * 4. GITLAB_CI=true -> gitlab\n * 5. CIRCLECI=true -> circleci\n * 6. JENKINS_URL defined -> jenkins\n * 7. TRAVIS=true -> travis\n * 8. CI=true -> unknown (generic fallback; note: fires in dev shells too)\n * 9. Nothing -> undefined (not in CI)\n */\n\nimport type { RawCIInfo } from \"executable-stories-core/types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env,\n): RawCIInfo | undefined {\n // 1. Azure DevOps (TF_BUILD is \"True\" with capital T)\n if (env.TF_BUILD === \"True\") {\n const branch = env.BUILD_SOURCEBRANCH?.replace(/^refs\\/heads\\//, \"\");\n\n // URL requires all three components\n const serverUri = env.SYSTEM_TEAMFOUNDATIONSERVERURI;\n const teamProject = env.SYSTEM_TEAMPROJECT;\n const buildId = env.BUILD_BUILDID;\n const url =\n serverUri && teamProject && buildId\n ? `${serverUri}${teamProject}/_build/results?buildId=${buildId}`\n : undefined;\n\n return {\n name: \"azure\",\n provider: \"azure\",\n buildNumber: buildId,\n url,\n branch,\n commitSha: env.BUILD_SOURCEVERSION,\n prNumber: env.SYSTEM_PULLREQUEST_PULLREQUESTID,\n };\n }\n\n // 2. Buildkite\n if (env.BUILDKITE === \"true\") {\n const prRaw = env.BUILDKITE_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"buildkite\",\n provider: \"buildkite\",\n buildNumber: env.BUILDKITE_BUILD_NUMBER,\n url: env.BUILDKITE_BUILD_URL,\n branch: env.BUILDKITE_BRANCH,\n commitSha: env.BUILDKITE_COMMIT,\n prNumber,\n };\n }\n\n // 3. GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n\n // GITHUB_HEAD_REF is set on pull_request events; GITHUB_REF_NAME is always set\n const branch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;\n\n // Parse PR number from GITHUB_REF: refs/pull/<n>/merge or refs/pull/<n>/head\n const prMatch = env.GITHUB_REF?.match(/^refs\\/pull\\/(\\d+)\\/(merge|head)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"github\",\n provider: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n branch,\n commitSha: env.GITHUB_SHA,\n prNumber,\n };\n }\n\n // 4. GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n provider: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n branch: env.CI_COMMIT_REF_NAME,\n commitSha: env.CI_COMMIT_SHA,\n prNumber: env.CI_MERGE_REQUEST_IID,\n };\n }\n\n // 5. CircleCI\n if (env.CIRCLECI === \"true\") {\n // Parse PR number from trailing digits of CIRCLE_PULL_REQUEST URL\n // e.g. https://github.com/org/repo/pull/42 -> \"42\"\n const prUrl = env.CIRCLE_PULL_REQUEST;\n const prMatch = prUrl?.match(/\\/(\\d+)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"circleci\",\n provider: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n branch: env.CIRCLE_BRANCH,\n commitSha: env.CIRCLE_SHA1,\n prNumber,\n };\n }\n\n // 6. Jenkins (detected by JENKINS_URL being defined, any value)\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n provider: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n branch: env.GIT_BRANCH,\n commitSha: env.GIT_COMMIT,\n };\n }\n\n // 7. Travis CI\n if (env.TRAVIS === \"true\") {\n const prRaw = env.TRAVIS_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"travis\",\n provider: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n branch: env.TRAVIS_BRANCH,\n commitSha: env.TRAVIS_COMMIT,\n prNumber,\n };\n }\n\n // 8. Generic CI fallback (note: CI=true fires in many dev shells too)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n provider: \"unknown\",\n };\n }\n\n // 9. Not in CI\n return undefined;\n}\n","/**\n * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus, RawAttachment } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n /** Optional step events if caller captures Playwright step timing. */\n stepEvents?: Array<{\n index?: number;\n stepId?: string;\n title?: string;\n status?: RawStatus;\n durationMs?: number;\n errorMessage?: string;\n }>;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n const stepEvents =\n result.stepEvents?.length\n ? result.stepEvents\n : story.steps\n .map((step, index) =>\n step.durationMs !== undefined\n ? {\n index,\n stepId: step.id,\n title: step.text,\n durationMs: step.durationMs,\n }\n : undefined,\n )\n .filter((e): e is NonNullable<typeof e> => e !== undefined);\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/converters/adapters/index.ts","../src/utils/ci-detect.ts","../src/converters/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts"],"sourcesContent":["/**\n * Framework adapters - Layer 1.\n *\n * Transform framework-specific data to RawRun for ACL processing.\n *\n * This file exists as the published `executable-stories-formatters/adapters`\n * entry point (see tsup.config.ts). Source inside this package imports the\n * concrete adapter modules instead of reaching through here.\n */\n\nexport {\n adaptJestRun,\n type JestTestResult,\n type JestFileResult,\n type JestAggregatedResult,\n type StoryFileReport,\n type JestAdapterOptions,\n} from \"./jest\";\n\nexport {\n adaptVitestRun,\n type VitestState,\n type VitestSerializedError,\n type VitestTestResult,\n type VitestTestCase,\n type VitestTestModule,\n type VitestAdapterOptions,\n} from \"./vitest\";\n\nexport {\n adaptPlaywrightRun,\n type PlaywrightStatus,\n type PlaywrightError,\n type PlaywrightAttachment,\n type PlaywrightTestResult,\n type PlaywrightAnnotation,\n type PlaywrightLocation,\n type PlaywrightTestCase,\n type PlaywrightAdapterOptions,\n} from \"./playwright\";\n","/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables and populates\n * branch, commit SHA, PR number, build number, and URL metadata.\n *\n * Precedence (checked in order, first match wins):\n * 1. TF_BUILD=True -> azure\n * 2. BUILDKITE=true -> buildkite\n * 3. GITHUB_ACTIONS=true -> github\n * 4. GITLAB_CI=true -> gitlab\n * 5. CIRCLECI=true -> circleci\n * 6. JENKINS_URL defined -> jenkins\n * 7. TRAVIS=true -> travis\n * 8. CI=true -> unknown (generic fallback; note: fires in dev shells too)\n * 9. Nothing -> undefined (not in CI)\n */\n\nimport type { RawCIInfo } from \"executable-stories-core/types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env,\n): RawCIInfo | undefined {\n // 1. Azure DevOps (TF_BUILD is \"True\" with capital T)\n if (env.TF_BUILD === \"True\") {\n const branch = env.BUILD_SOURCEBRANCH?.replace(/^refs\\/heads\\//, \"\");\n\n // URL requires all three components\n const serverUri = env.SYSTEM_TEAMFOUNDATIONSERVERURI;\n const teamProject = env.SYSTEM_TEAMPROJECT;\n const buildId = env.BUILD_BUILDID;\n const url =\n serverUri && teamProject && buildId\n ? `${serverUri}${teamProject}/_build/results?buildId=${buildId}`\n : undefined;\n\n return {\n name: \"azure\",\n provider: \"azure\",\n buildNumber: buildId,\n url,\n branch,\n commitSha: env.BUILD_SOURCEVERSION,\n prNumber: env.SYSTEM_PULLREQUEST_PULLREQUESTID,\n };\n }\n\n // 2. Buildkite\n if (env.BUILDKITE === \"true\") {\n const prRaw = env.BUILDKITE_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"buildkite\",\n provider: \"buildkite\",\n buildNumber: env.BUILDKITE_BUILD_NUMBER,\n url: env.BUILDKITE_BUILD_URL,\n branch: env.BUILDKITE_BRANCH,\n commitSha: env.BUILDKITE_COMMIT,\n prNumber,\n };\n }\n\n // 3. GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n\n // GITHUB_HEAD_REF is set on pull_request events; GITHUB_REF_NAME is always set\n const branch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;\n\n // Parse PR number from GITHUB_REF: refs/pull/<n>/merge or refs/pull/<n>/head\n const prMatch = env.GITHUB_REF?.match(/^refs\\/pull\\/(\\d+)\\/(merge|head)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"github\",\n provider: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n branch,\n commitSha: env.GITHUB_SHA,\n prNumber,\n };\n }\n\n // 4. GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n provider: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n branch: env.CI_COMMIT_REF_NAME,\n commitSha: env.CI_COMMIT_SHA,\n prNumber: env.CI_MERGE_REQUEST_IID,\n };\n }\n\n // 5. CircleCI\n if (env.CIRCLECI === \"true\") {\n // Parse PR number from trailing digits of CIRCLE_PULL_REQUEST URL\n // e.g. https://github.com/org/repo/pull/42 -> \"42\"\n const prUrl = env.CIRCLE_PULL_REQUEST;\n const prMatch = prUrl?.match(/\\/(\\d+)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"circleci\",\n provider: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n branch: env.CIRCLE_BRANCH,\n commitSha: env.CIRCLE_SHA1,\n prNumber,\n };\n }\n\n // 6. Jenkins (detected by JENKINS_URL being defined, any value)\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n provider: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n branch: env.GIT_BRANCH,\n commitSha: env.GIT_COMMIT,\n };\n }\n\n // 7. Travis CI\n if (env.TRAVIS === \"true\") {\n const prRaw = env.TRAVIS_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"travis\",\n provider: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n branch: env.TRAVIS_BRANCH,\n commitSha: env.TRAVIS_COMMIT,\n prNumber,\n };\n }\n\n // 8. Generic CI fallback (note: CI=true fires in many dev shells too)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n provider: \"unknown\",\n };\n }\n\n // 9. Not in CI\n return undefined;\n}\n","/**\n * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawFeature, RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n /** Feature declaration from story.feature(), when the file made one. */\n feature?: Omit<RawFeature, \"sourceFile\">;\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const features: RawFeature[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n if (report.feature?.title) {\n features.push({ ...report.feature, sourceFile: report.testFilePath });\n }\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n ...(features.length > 0 ? { features } : {}),\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawFeature, RawRun, RawTestCase, RawStatus, RawAttachment } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n /** Optional step events if caller captures Playwright step timing. */\n stepEvents?: Array<{\n index?: number;\n stepId?: string;\n title?: string;\n status?: RawStatus;\n durationMs?: number;\n errorMessage?: string;\n }>;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const featuresByFile = new Map<string, RawFeature>();\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n const featureAnnotation = test.annotations.find((a) => a.type === \"story-feature\");\n if (featureAnnotation?.description && !featuresByFile.has(test.location.file)) {\n try {\n const declared = JSON.parse(featureAnnotation.description) as RawFeature;\n if (declared?.title) {\n featuresByFile.set(test.location.file, { ...declared, sourceFile: test.location.file });\n }\n } catch {\n // A malformed annotation should not lose the run's test cases.\n }\n }\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n const stepEvents =\n result.stepEvents?.length\n ? result.stepEvents\n : story.steps\n .map((step, index) =>\n step.durationMs !== undefined\n ? {\n index,\n stepId: step.id,\n title: step.text,\n durationMs: step.durationMs,\n }\n : undefined,\n )\n .filter((e): e is NonNullable<typeof e> => e !== undefined);\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n ...(featuresByFile.size > 0 ? { features: [...featuresByFile.values()] } : {}),\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwBO,SAAS,SACd,MAA0C,QAAQ,KAC3B;AAEvB,MAAI,IAAI,aAAa,QAAQ;AAC3B,UAAM,SAAS,IAAI,oBAAoB,QAAQ,kBAAkB,EAAE;AAGnE,UAAM,YAAY,IAAI;AACtB,UAAM,cAAc,IAAI;AACxB,UAAM,UAAU,IAAI;AACpB,UAAM,MACJ,aAAa,eAAe,UACxB,GAAG,SAAS,GAAG,WAAW,2BAA2B,OAAO,KAC5D;AAEN,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,IAAI,cAAc,QAAQ;AAC5B,UAAM,QAAQ,IAAI;AAClB,UAAM,WAAW,SAAS,UAAU,UAAU,QAAQ;AAEtD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,mBAAmB,QAAQ;AACjC,UAAM,MACJ,IAAI,qBAAqB,IAAI,qBAAqB,IAAI,gBAClD,GAAG,IAAI,iBAAiB,IAAI,IAAI,iBAAiB,iBAAiB,IAAI,aAAa,KACnF;AAGN,UAAM,SAAS,IAAI,mBAAmB,IAAI;AAG1C,UAAM,UAAU,IAAI,YAAY,MAAM,mCAAmC;AACzE,UAAM,WAAW,UAAU,QAAQ,CAAC,IAAI;AAExC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB;AAAA,MACA;AAAA,MACA,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,cAAc,QAAQ;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,IAAI,aAAa,QAAQ;AAG3B,UAAM,QAAQ,IAAI;AAClB,UAAM,UAAU,OAAO,MAAM,UAAU;AACvC,UAAM,WAAW,UAAU,QAAQ,CAAC,IAAI;AAExC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,gBAAgB,QAAW;AACjC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAGA,MAAI,IAAI,WAAW,QAAQ;AACzB,UAAM,QAAQ,IAAI;AAClB,UAAM,WAAW,SAAS,UAAU,UAAU,QAAQ;AAEtD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,IAAI,OAAO,QAAQ;AACrB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAGA,SAAO;AACT;;;AC/GA,SAAS,cAAc,QAA6C;AAClE,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAUO,SAAS,aACd,aACA,cACA,UAA8B,CAAC,GACvB;AACR,QAAM,YAA2B,CAAC;AAClC,QAAM,WAAyB,CAAC;AAGhC,QAAM,iBAAiB,oBAAI,IAA4B;AACvD,aAAW,cAAc,YAAY,aAAa;AAChD,mBAAe,IAAI,WAAW,cAAc,UAAU;AAAA,EACxD;AAGA,aAAW,UAAU,cAAc;AACjC,UAAM,aAAa,eAAe,IAAI,OAAO,YAAY;AAEzD,QAAI,OAAO,SAAS,OAAO;AACzB,eAAS,KAAK,EAAE,GAAG,OAAO,SAAS,YAAY,OAAO,aAAa,CAAC;AAAA,IACtE;AAEA,eAAW,QAAQ,OAAO,WAAW;AACnC,UAAI,CAAC,MAAM,SAAU;AAGrB,YAAM,eAAe,qBAAqB,YAAY,IAAI;AAE1D,gBAAU,KAAK;AAAA,QACb,YAAY,cAAc;AAAA,QAC1B,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK,YACZ,CAAC,GAAG,KAAK,WAAW,KAAK,QAAQ,IACjC,CAAC,KAAK,QAAQ;AAAA,QAClB,OAAO;AAAA,QACP,YAAY,OAAO;AAAA,QACnB,YAAY;AAAA;AAAA,QACZ,QAAQ,eAAe,cAAc,aAAa,MAAM,IAAI;AAAA,QAC5D,YAAY,cAAc;AAAA,QAC1B,OAAO,cAAc,iBAAiB,SAClC,EAAE,SAAS,aAAa,gBAAgB,KAAK,IAAI,EAAE,IACnD;AAAA,QACJ,YAAY;AAAA;AAAA,QACZ,aAAa;AAAA;AAAA,QACb,MAAM,EAAE,YAAY,cAAc,OAAO;AAAA,QACzC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,GAAI,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,IAC1C,aAAa,YAAY;AAAA,IACzB,cAAc,KAAK,IAAI;AAAA,IACvB,aAAa,QAAQ,eAAe,QAAQ,IAAI;AAAA,IAChD,gBAAgB,QAAQ;AAAA,IACxB,QAAQ,QAAQ;AAAA,IAChB,IAAI,SAAS;AAAA,EACf;AACF;AAOA,SAAS,qBACP,YACA,MAC4B;AAC5B,MAAI,CAAC,WAAY,QAAO;AAGxB,QAAM,mBAAmB,KAAK,YAC1B,CAAC,GAAG,KAAK,WAAW,KAAK,QAAQ,EAAE,KAAK,KAAK,IAC7C,KAAK;AAET,SAAO,WAAW,YAAY,KAAK,CAAC,SAAS,KAAK,aAAa,gBAAgB;AACjF;;;ACjGA,SAAS,gBAAgB,OAAgC;AACvD,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AASO,SAAS,eACd,aACA,UAAgC,CAAC,GACzB;AACR,QAAM,YAA2B,CAAC;AAClC,QAAM,cAAc,QAAQ,eAAe,QAAQ,IAAI;AAEvD,aAAW,OAAO,aAAa;AAC7B,UAAM,aAAa,IAAI;AACvB,QAAI,CAAC,WAAY;AAGjB,UAAM,WAAW,IAAI,YAAY,IAAI,oBAAoB;AACzD,UAAM,aAAa,SAAS,WAAW,GAAG,IACtC,WACA,GAAG,WAAW,IAAI,QAAQ;AAE9B,eAAW,QAAQ,WAAW,SAAS,GAAG;AACxC,YAAM,OAAO,KAAK,KAAK;AACvB,YAAM,QAAQ,OAAO,OAAO;AAE5B,UAAI,CAAC,OAAO,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,EAAG;AAErD,YAAM,SAAS,KAAK,OAAO;AAC3B,YAAM,QAAQ,QAAQ;AACtB,YAAM,SAAS,UAAU,YAAY,SAAS,OAAO,SAAS;AAE9D,gBAAU,KAAK;AAAA,QACb,YAAY,KAAK;AAAA,QACjB,OAAO,MAAM;AAAA,QACb,WAAW,MAAM,YACb,CAAC,GAAG,MAAM,WAAW,MAAM,QAAQ,IACnC,CAAC,MAAM,QAAQ;AAAA,QACnB;AAAA,QACA,YAAY;AAAA,QACZ,YAAY;AAAA;AAAA,QACZ,QAAQ,gBAAgB,KAAK;AAAA,QAC7B,YAAY,QAAQ;AAAA,QACpB,OAAO,QAAQ,SACX;AAAA,UACE,SAAS,kBAAkB,OAAO,CAAC,CAAC;AAAA,UACpC,OAAO,OAAO,CAAC,EAAE;AAAA,QACnB,IACA;AAAA,QACJ,YAAY;AAAA;AAAA,QACZ,aAAa;AAAA;AAAA,QACb,MAAM,EAAE,aAAa,MAAM;AAAA,QAC3B,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,cAAc,KAAK,IAAI;AAAA,IACvB;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,QAAQ,QAAQ;AAAA,IAChB,IAAI,SAAS;AAAA,EACf;AACF;AAKA,SAAS,kBAAkB,OAAsC;AAC/D,QAAM,QAAkB,CAAC;AAEzB,MAAI,MAAM,SAAS;AACjB,UAAM,KAAK,MAAM,OAAO;AAAA,EAC1B;AAEA,MAAI,MAAM,MAAM;AACd,UAAM,KAAK,IAAI,MAAM,IAAI;AAAA,EAC3B;AAEA,SAAO,MAAM,KAAK,IAAI,KAAK;AAC7B;;;ACvEA,SAAS,oBAAoB,QAAqC;AAChE,UAAQ,QAAQ;AAAA,IACd,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,EACX;AACF;AASO,SAAS,mBACd,aACA,UAAoC,CAAC,GAC7B;AACR,QAAM,YAA2B,CAAC;AAClC,QAAM,iBAAiB,oBAAI,IAAwB;AACnD,QAAM,cAAc,QAAQ,eAAe,QAAQ,IAAI;AAEvD,aAAW,CAAC,MAAM,MAAM,KAAK,aAAa;AAExC,UAAM,kBAAkB,KAAK,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY;AAC5E,QAAI,CAAC,iBAAiB,YAAa;AAEnC,UAAM,oBAAoB,KAAK,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,eAAe;AACjF,QAAI,mBAAmB,eAAe,CAAC,eAAe,IAAI,KAAK,SAAS,IAAI,GAAG;AAC7E,UAAI;AACF,cAAM,WAAW,KAAK,MAAM,kBAAkB,WAAW;AACzD,YAAI,UAAU,OAAO;AACnB,yBAAe,IAAI,KAAK,SAAS,MAAM,EAAE,GAAG,UAAU,YAAY,KAAK,SAAS,KAAK,CAAC;AAAA,QACxF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,cAAQ,KAAK,MAAM,gBAAgB,WAAW;AAAA,IAChD,QAAQ;AACN;AAAA,IACF;AAEA,QAAI,CAAC,OAAO,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,EAAG;AAGrD,UAAM,cAAc,mBAAmB,OAAO,WAAW;AAGzD,UAAM,QAAQ,OAAO,QAAQ,SACzB;AAAA,MACE,SAAS,OAAO,OAAO,CAAC,EAAE;AAAA,MAC1B,OAAO,OAAO,OAAO,CAAC,EAAE;AAAA,IAC1B,IACA;AAEJ,UAAM,aACJ,OAAO,YAAY,SACf,OAAO,aACP,MAAM,MACH;AAAA,MAAI,CAAC,MAAM,UACV,KAAK,eAAe,SAChB;AAAA,QACE;AAAA,QACA,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,YAAY,KAAK;AAAA,MACnB,IACA;AAAA,IACN,EACC,OAAO,CAAC,MAAkC,MAAM,MAAS;AAElE,cAAU,KAAK;AAAA,MACb,YAAY,KAAK,UAAU,EAAE,KAAK,KAAK;AAAA,MACvC,OAAO,MAAM;AAAA,MACb,WAAW,KAAK,UAAU;AAAA,MAC1B;AAAA,MACA,YAAY,KAAK,SAAS;AAAA,MAC1B,YAAY,KAAK,SAAS;AAAA,MAC1B,QAAQ,oBAAoB,OAAO,MAAM;AAAA,MACzC,YAAY,OAAO;AAAA,MACnB;AAAA,MACA,YAAY,WAAW,SAAS,IAAI,aAAa;AAAA,MACjD;AAAA,MACA,MAAM;AAAA,QACJ,kBAAkB,OAAO;AAAA,QACzB,QAAQ,KAAK,SAAS;AAAA,MACxB;AAAA,MACA,OAAO,OAAO;AAAA,MACd,SAAS,KAAK;AAAA,MACd,aAAa,QAAQ;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA,GAAI,eAAe,OAAO,IAAI,EAAE,UAAU,CAAC,GAAG,eAAe,OAAO,CAAC,EAAE,IAAI,CAAC;AAAA,IAC5E,aAAa,QAAQ;AAAA,IACrB,cAAc,KAAK,IAAI;AAAA,IACvB;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,QAAQ,QAAQ;AAAA,IAChB,IAAI,SAAS;AAAA,EACf;AACF;AAKA,SAAS,mBACP,aACiB;AACjB,SAAO,YAAY,IAAI,CAAC,SAAS;AAAA,IAC/B,MAAM,IAAI;AAAA,IACV,WAAW,IAAI;AAAA,IACf,MAAM,IAAI;AAAA,IACV,MAAM,IAAI,OAAO,IAAI,KAAK,SAAS,QAAQ,IAAI;AAAA,IAC/C,UAAU,IAAI,OAAO,WAAoB;AAAA,IACzC,YAAY,IAAI,MAAM;AAAA,EACxB,EAAE;AACJ;","names":[]}
|
package/dist/adapters.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StoryMeta } from 'executable-stories-core/types/story';
|
|
2
|
-
import { RawRun, RawStatus } from 'executable-stories-core/types/raw';
|
|
2
|
+
import { RawFeature, RawRun, RawStatus } from 'executable-stories-core/types/raw';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Jest Adapter - Layer 1.
|
|
@@ -28,6 +28,8 @@ interface JestAggregatedResult {
|
|
|
28
28
|
interface StoryFileReport {
|
|
29
29
|
testFilePath: string;
|
|
30
30
|
scenarios: StoryMeta[];
|
|
31
|
+
/** Feature declaration from story.feature(), when the file made one. */
|
|
32
|
+
feature?: Omit<RawFeature, "sourceFile">;
|
|
31
33
|
}
|
|
32
34
|
/** Options for Jest adapter */
|
|
33
35
|
interface JestAdapterOptions {
|
package/dist/adapters.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StoryMeta } from 'executable-stories-core/types/story';
|
|
2
|
-
import { RawRun, RawStatus } from 'executable-stories-core/types/raw';
|
|
2
|
+
import { RawFeature, RawRun, RawStatus } from 'executable-stories-core/types/raw';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Jest Adapter - Layer 1.
|
|
@@ -28,6 +28,8 @@ interface JestAggregatedResult {
|
|
|
28
28
|
interface StoryFileReport {
|
|
29
29
|
testFilePath: string;
|
|
30
30
|
scenarios: StoryMeta[];
|
|
31
|
+
/** Feature declaration from story.feature(), when the file made one. */
|
|
32
|
+
feature?: Omit<RawFeature, "sourceFile">;
|
|
31
33
|
}
|
|
32
34
|
/** Options for Jest adapter */
|
|
33
35
|
interface JestAdapterOptions {
|
package/dist/adapters.js
CHANGED
|
@@ -118,12 +118,16 @@ function mapJestStatus(status) {
|
|
|
118
118
|
}
|
|
119
119
|
function adaptJestRun(jestResults, storyReports, options = {}) {
|
|
120
120
|
const testCases = [];
|
|
121
|
+
const features = [];
|
|
121
122
|
const fileResultsMap = /* @__PURE__ */ new Map();
|
|
122
123
|
for (const fileResult of jestResults.testResults) {
|
|
123
124
|
fileResultsMap.set(fileResult.testFilePath, fileResult);
|
|
124
125
|
}
|
|
125
126
|
for (const report of storyReports) {
|
|
126
127
|
const fileResult = fileResultsMap.get(report.testFilePath);
|
|
128
|
+
if (report.feature?.title) {
|
|
129
|
+
features.push({ ...report.feature, sourceFile: report.testFilePath });
|
|
130
|
+
}
|
|
127
131
|
for (const meta of report.scenarios) {
|
|
128
132
|
if (!meta?.scenario) continue;
|
|
129
133
|
const matchingTest = findMatchingJestTest(fileResult, meta);
|
|
@@ -151,6 +155,7 @@ function adaptJestRun(jestResults, storyReports, options = {}) {
|
|
|
151
155
|
}
|
|
152
156
|
return {
|
|
153
157
|
testCases,
|
|
158
|
+
...features.length > 0 ? { features } : {},
|
|
154
159
|
startedAtMs: jestResults.startTime,
|
|
155
160
|
finishedAtMs: Date.now(),
|
|
156
161
|
projectRoot: options.projectRoot ?? process.cwd(),
|
|
@@ -260,10 +265,21 @@ function mapPlaywrightStatus(status) {
|
|
|
260
265
|
}
|
|
261
266
|
function adaptPlaywrightRun(testResults, options = {}) {
|
|
262
267
|
const testCases = [];
|
|
268
|
+
const featuresByFile = /* @__PURE__ */ new Map();
|
|
263
269
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
264
270
|
for (const [test, result] of testResults) {
|
|
265
271
|
const storyAnnotation = test.annotations.find((a) => a.type === "story-meta");
|
|
266
272
|
if (!storyAnnotation?.description) continue;
|
|
273
|
+
const featureAnnotation = test.annotations.find((a) => a.type === "story-feature");
|
|
274
|
+
if (featureAnnotation?.description && !featuresByFile.has(test.location.file)) {
|
|
275
|
+
try {
|
|
276
|
+
const declared = JSON.parse(featureAnnotation.description);
|
|
277
|
+
if (declared?.title) {
|
|
278
|
+
featuresByFile.set(test.location.file, { ...declared, sourceFile: test.location.file });
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
}
|
|
267
283
|
let story;
|
|
268
284
|
try {
|
|
269
285
|
story = JSON.parse(storyAnnotation.description);
|
|
@@ -307,6 +323,7 @@ function adaptPlaywrightRun(testResults, options = {}) {
|
|
|
307
323
|
}
|
|
308
324
|
return {
|
|
309
325
|
testCases,
|
|
326
|
+
...featuresByFile.size > 0 ? { features: [...featuresByFile.values()] } : {},
|
|
310
327
|
startedAtMs: options.startedAtMs,
|
|
311
328
|
finishedAtMs: Date.now(),
|
|
312
329
|
projectRoot,
|
package/dist/adapters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/ci-detect.ts","../src/converters/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts"],"sourcesContent":["/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables and populates\n * branch, commit SHA, PR number, build number, and URL metadata.\n *\n * Precedence (checked in order, first match wins):\n * 1. TF_BUILD=True -> azure\n * 2. BUILDKITE=true -> buildkite\n * 3. GITHUB_ACTIONS=true -> github\n * 4. GITLAB_CI=true -> gitlab\n * 5. CIRCLECI=true -> circleci\n * 6. JENKINS_URL defined -> jenkins\n * 7. TRAVIS=true -> travis\n * 8. CI=true -> unknown (generic fallback; note: fires in dev shells too)\n * 9. Nothing -> undefined (not in CI)\n */\n\nimport type { RawCIInfo } from \"executable-stories-core/types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env,\n): RawCIInfo | undefined {\n // 1. Azure DevOps (TF_BUILD is \"True\" with capital T)\n if (env.TF_BUILD === \"True\") {\n const branch = env.BUILD_SOURCEBRANCH?.replace(/^refs\\/heads\\//, \"\");\n\n // URL requires all three components\n const serverUri = env.SYSTEM_TEAMFOUNDATIONSERVERURI;\n const teamProject = env.SYSTEM_TEAMPROJECT;\n const buildId = env.BUILD_BUILDID;\n const url =\n serverUri && teamProject && buildId\n ? `${serverUri}${teamProject}/_build/results?buildId=${buildId}`\n : undefined;\n\n return {\n name: \"azure\",\n provider: \"azure\",\n buildNumber: buildId,\n url,\n branch,\n commitSha: env.BUILD_SOURCEVERSION,\n prNumber: env.SYSTEM_PULLREQUEST_PULLREQUESTID,\n };\n }\n\n // 2. Buildkite\n if (env.BUILDKITE === \"true\") {\n const prRaw = env.BUILDKITE_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"buildkite\",\n provider: \"buildkite\",\n buildNumber: env.BUILDKITE_BUILD_NUMBER,\n url: env.BUILDKITE_BUILD_URL,\n branch: env.BUILDKITE_BRANCH,\n commitSha: env.BUILDKITE_COMMIT,\n prNumber,\n };\n }\n\n // 3. GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n\n // GITHUB_HEAD_REF is set on pull_request events; GITHUB_REF_NAME is always set\n const branch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;\n\n // Parse PR number from GITHUB_REF: refs/pull/<n>/merge or refs/pull/<n>/head\n const prMatch = env.GITHUB_REF?.match(/^refs\\/pull\\/(\\d+)\\/(merge|head)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"github\",\n provider: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n branch,\n commitSha: env.GITHUB_SHA,\n prNumber,\n };\n }\n\n // 4. GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n provider: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n branch: env.CI_COMMIT_REF_NAME,\n commitSha: env.CI_COMMIT_SHA,\n prNumber: env.CI_MERGE_REQUEST_IID,\n };\n }\n\n // 5. CircleCI\n if (env.CIRCLECI === \"true\") {\n // Parse PR number from trailing digits of CIRCLE_PULL_REQUEST URL\n // e.g. https://github.com/org/repo/pull/42 -> \"42\"\n const prUrl = env.CIRCLE_PULL_REQUEST;\n const prMatch = prUrl?.match(/\\/(\\d+)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"circleci\",\n provider: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n branch: env.CIRCLE_BRANCH,\n commitSha: env.CIRCLE_SHA1,\n prNumber,\n };\n }\n\n // 6. Jenkins (detected by JENKINS_URL being defined, any value)\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n provider: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n branch: env.GIT_BRANCH,\n commitSha: env.GIT_COMMIT,\n };\n }\n\n // 7. Travis CI\n if (env.TRAVIS === \"true\") {\n const prRaw = env.TRAVIS_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"travis\",\n provider: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n branch: env.TRAVIS_BRANCH,\n commitSha: env.TRAVIS_COMMIT,\n prNumber,\n };\n }\n\n // 8. Generic CI fallback (note: CI=true fires in many dev shells too)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n provider: \"unknown\",\n };\n }\n\n // 9. Not in CI\n return undefined;\n}\n","/**\n * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus, RawAttachment } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n /** Optional step events if caller captures Playwright step timing. */\n stepEvents?: Array<{\n index?: number;\n stepId?: string;\n title?: string;\n status?: RawStatus;\n durationMs?: number;\n errorMessage?: string;\n }>;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n const stepEvents =\n result.stepEvents?.length\n ? result.stepEvents\n : story.steps\n .map((step, index) =>\n step.durationMs !== undefined\n ? {\n index,\n stepId: step.id,\n title: step.text,\n durationMs: step.durationMs,\n }\n : undefined,\n )\n .filter((e): e is NonNullable<typeof e> => e !== undefined);\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/ci-detect.ts","../src/converters/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts"],"sourcesContent":["/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables and populates\n * branch, commit SHA, PR number, build number, and URL metadata.\n *\n * Precedence (checked in order, first match wins):\n * 1. TF_BUILD=True -> azure\n * 2. BUILDKITE=true -> buildkite\n * 3. GITHUB_ACTIONS=true -> github\n * 4. GITLAB_CI=true -> gitlab\n * 5. CIRCLECI=true -> circleci\n * 6. JENKINS_URL defined -> jenkins\n * 7. TRAVIS=true -> travis\n * 8. CI=true -> unknown (generic fallback; note: fires in dev shells too)\n * 9. Nothing -> undefined (not in CI)\n */\n\nimport type { RawCIInfo } from \"executable-stories-core/types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env,\n): RawCIInfo | undefined {\n // 1. Azure DevOps (TF_BUILD is \"True\" with capital T)\n if (env.TF_BUILD === \"True\") {\n const branch = env.BUILD_SOURCEBRANCH?.replace(/^refs\\/heads\\//, \"\");\n\n // URL requires all three components\n const serverUri = env.SYSTEM_TEAMFOUNDATIONSERVERURI;\n const teamProject = env.SYSTEM_TEAMPROJECT;\n const buildId = env.BUILD_BUILDID;\n const url =\n serverUri && teamProject && buildId\n ? `${serverUri}${teamProject}/_build/results?buildId=${buildId}`\n : undefined;\n\n return {\n name: \"azure\",\n provider: \"azure\",\n buildNumber: buildId,\n url,\n branch,\n commitSha: env.BUILD_SOURCEVERSION,\n prNumber: env.SYSTEM_PULLREQUEST_PULLREQUESTID,\n };\n }\n\n // 2. Buildkite\n if (env.BUILDKITE === \"true\") {\n const prRaw = env.BUILDKITE_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"buildkite\",\n provider: \"buildkite\",\n buildNumber: env.BUILDKITE_BUILD_NUMBER,\n url: env.BUILDKITE_BUILD_URL,\n branch: env.BUILDKITE_BRANCH,\n commitSha: env.BUILDKITE_COMMIT,\n prNumber,\n };\n }\n\n // 3. GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n\n // GITHUB_HEAD_REF is set on pull_request events; GITHUB_REF_NAME is always set\n const branch = env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME;\n\n // Parse PR number from GITHUB_REF: refs/pull/<n>/merge or refs/pull/<n>/head\n const prMatch = env.GITHUB_REF?.match(/^refs\\/pull\\/(\\d+)\\/(merge|head)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"github\",\n provider: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n branch,\n commitSha: env.GITHUB_SHA,\n prNumber,\n };\n }\n\n // 4. GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n provider: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n branch: env.CI_COMMIT_REF_NAME,\n commitSha: env.CI_COMMIT_SHA,\n prNumber: env.CI_MERGE_REQUEST_IID,\n };\n }\n\n // 5. CircleCI\n if (env.CIRCLECI === \"true\") {\n // Parse PR number from trailing digits of CIRCLE_PULL_REQUEST URL\n // e.g. https://github.com/org/repo/pull/42 -> \"42\"\n const prUrl = env.CIRCLE_PULL_REQUEST;\n const prMatch = prUrl?.match(/\\/(\\d+)$/);\n const prNumber = prMatch ? prMatch[1] : undefined;\n\n return {\n name: \"circleci\",\n provider: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n branch: env.CIRCLE_BRANCH,\n commitSha: env.CIRCLE_SHA1,\n prNumber,\n };\n }\n\n // 6. Jenkins (detected by JENKINS_URL being defined, any value)\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n provider: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n branch: env.GIT_BRANCH,\n commitSha: env.GIT_COMMIT,\n };\n }\n\n // 7. Travis CI\n if (env.TRAVIS === \"true\") {\n const prRaw = env.TRAVIS_PULL_REQUEST;\n const prNumber = prRaw && prRaw !== \"false\" ? prRaw : undefined;\n\n return {\n name: \"travis\",\n provider: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n branch: env.TRAVIS_BRANCH,\n commitSha: env.TRAVIS_COMMIT,\n prNumber,\n };\n }\n\n // 8. Generic CI fallback (note: CI=true fires in many dev shells too)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n provider: \"unknown\",\n };\n }\n\n // 9. Not in CI\n return undefined;\n}\n","/**\n * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawFeature, RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n /** Feature declaration from story.feature(), when the file made one. */\n feature?: Omit<RawFeature, \"sourceFile\">;\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const features: RawFeature[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n if (report.feature?.title) {\n features.push({ ...report.feature, sourceFile: report.testFilePath });\n }\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n ...(features.length > 0 ? { features } : {}),\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"executable-stories-core/types/story\";\nimport type { RawFeature, RawRun, RawTestCase, RawStatus, RawAttachment } from \"executable-stories-core/types/raw\";\nimport { detectCI } from \"../../utils/ci-detect\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n /** Optional step events if caller captures Playwright step timing. */\n stepEvents?: Array<{\n index?: number;\n stepId?: string;\n title?: string;\n status?: RawStatus;\n durationMs?: number;\n errorMessage?: string;\n }>;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const featuresByFile = new Map<string, RawFeature>();\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n const featureAnnotation = test.annotations.find((a) => a.type === \"story-feature\");\n if (featureAnnotation?.description && !featuresByFile.has(test.location.file)) {\n try {\n const declared = JSON.parse(featureAnnotation.description) as RawFeature;\n if (declared?.title) {\n featuresByFile.set(test.location.file, { ...declared, sourceFile: test.location.file });\n }\n } catch {\n // A malformed annotation should not lose the run's test cases.\n }\n }\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n const stepEvents =\n result.stepEvents?.length\n ? result.stepEvents\n : story.steps\n .map((step, index) =>\n step.durationMs !== undefined\n ? {\n index,\n stepId: step.id,\n title: step.text,\n durationMs: step.durationMs,\n }\n : undefined,\n )\n .filter((e): e is NonNullable<typeof e> => e !== undefined);\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n ...(featuresByFile.size > 0 ? { features: [...featuresByFile.values()] } : {}),\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n"],"mappings":"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","names":[]}
|