executable-stories-cypress 8.5.1 → 8.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/reporter.cjs +27 -1
- package/dist/reporter.cjs.map +1 -1
- package/dist/reporter.d.cts +4 -0
- package/dist/reporter.d.ts +4 -0
- package/dist/reporter.js +27 -1
- package/dist/reporter.js.map +1 -1
- package/package.json +2 -2
package/dist/reporter.cjs
CHANGED
|
@@ -58,6 +58,9 @@ function clearStore() {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// src/reporter.ts
|
|
61
|
+
function isPlannedTest(test) {
|
|
62
|
+
return test.pending === true && test.fn === void 0;
|
|
63
|
+
}
|
|
61
64
|
function getTitlePath(test) {
|
|
62
65
|
const path2 = [];
|
|
63
66
|
let current = test;
|
|
@@ -103,10 +106,14 @@ function createReporter(runner, options = {}) {
|
|
|
103
106
|
const all = getAllMeta();
|
|
104
107
|
return all.length > 0 ? all[0].specRelative : "unknown";
|
|
105
108
|
})();
|
|
109
|
+
const plannedTests = [];
|
|
106
110
|
for (const test of tests) {
|
|
107
111
|
const titlePath = getTitlePath(test);
|
|
108
112
|
const meta = getMeta(effectiveSpecPath, titlePath);
|
|
109
|
-
if (!meta)
|
|
113
|
+
if (!meta) {
|
|
114
|
+
if (isPlannedTest(test)) plannedTests.push(test);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
110
117
|
const status = mapCypressStateToRaw(test.state);
|
|
111
118
|
const storedAttachments = getAttachments(effectiveSpecPath, titlePath);
|
|
112
119
|
const attachments = storedAttachments?.map((a) => ({
|
|
@@ -144,6 +151,25 @@ function createReporter(runner, options = {}) {
|
|
|
144
151
|
clearStore();
|
|
145
152
|
return;
|
|
146
153
|
}
|
|
154
|
+
for (const test of plannedTests) {
|
|
155
|
+
const titlePath = getTitlePath(test);
|
|
156
|
+
const suitePath = titlePath.slice(0, -1);
|
|
157
|
+
rawTestCases.push({
|
|
158
|
+
title: test.title,
|
|
159
|
+
titlePath,
|
|
160
|
+
story: {
|
|
161
|
+
scenario: test.title,
|
|
162
|
+
steps: [],
|
|
163
|
+
...suitePath.length > 0 ? { suitePath } : {}
|
|
164
|
+
},
|
|
165
|
+
sourceFile: effectiveSpecPath,
|
|
166
|
+
sourceLine: 1,
|
|
167
|
+
status: "todo",
|
|
168
|
+
durationMs: 0,
|
|
169
|
+
retry: 0,
|
|
170
|
+
retries: 0
|
|
171
|
+
});
|
|
172
|
+
}
|
|
147
173
|
const includeMetadata = opts.markdown?.includeMetadata ?? true;
|
|
148
174
|
const rawRun = {
|
|
149
175
|
testCases: rawTestCases,
|
package/dist/reporter.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter.ts","../src/store.ts"],"sourcesContent":["/**\n * Cypress/Mocha reporter for executable-stories.\n * Builds RawRun (formatters schema) from run results + stored story meta, then generates reports.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { StoryMeta } from \"executable-stories-formatters\";\n\nimport {\n ReportGenerator,\n canonicalizeRun,\n readGitSha,\n readPackageVersion,\n detectCI,\n type RawRun,\n type RawTestCase,\n type RawAttachment,\n type RawStepEvent,\n type RawStatus,\n type FormatterOptions,\n} from \"executable-stories-formatters\";\n\nimport { getMeta, getAttachments, getAllMeta, clearStore } from \"./store\";\n\n// Re-export types from formatters\nexport type {\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n FormatterOptions,\n} from \"executable-stories-formatters\";\n\n// ============================================================================\n// Reporter Options\n// ============================================================================\n\nexport interface StoryReporterOptions extends FormatterOptions {\n /** If set, write raw run JSON (schemaVersion 1) to this path for CLI/binary */\n rawRunPath?: string;\n /** Spec file path (relative to projectRoot) for this run. If omitted, inferred from stored meta when possible. */\n specPath?: string;\n /** Project root. Defaults to process.cwd(). */\n projectRoot?: string;\n}\n\n// ============================================================================\n// Mocha Runner / Test types (minimal for reporter)\n// ============================================================================\n\ninterface MochaSuite {\n title: string;\n suites: MochaSuite[];\n tests: MochaTest[];\n parent?: MochaSuite;\n}\n\ninterface MochaTest {\n title: string;\n state?: string;\n duration?: number;\n err?: Error & { message?: string; stack?: string };\n parent?: MochaSuite;\n}\n\ninterface MochaRunner {\n suite: MochaSuite;\n startTime?: number;\n on?(event: string, callback: () => void): void;\n}\n\nfunction getTitlePath(test: MochaTest): string[] {\n const path: string[] = [];\n let current: MochaSuite | MochaTest | undefined = test;\n while (current) {\n const title = \"title\" in current ? current.title : \"\";\n if (title) path.unshift(title);\n current = \"parent\" in current ? current.parent : undefined;\n }\n return path;\n}\n\nfunction collectTests(suite: MochaSuite): MochaTest[] {\n const tests = [...suite.tests];\n for (const s of suite.suites) {\n tests.push(...collectTests(s));\n }\n return tests;\n}\n\nfunction mapCypressStateToRaw(state: string | undefined): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"skip\";\n default:\n return \"unknown\";\n }\n}\n\n// ============================================================================\n// Reporter Implementation\n// ============================================================================\n\nconst DEFAULT_OPTIONS: Partial<StoryReporterOptions> = {\n projectRoot: process.cwd(),\n};\n\n/**\n * Create a Mocha reporter function that Cypress can use (--reporter path/to/reporter).\n * Cypress passes (runner, reporterOptions) with reporterOptions as the second argument directly.\n * Also supports options.reporterOptions for wrapped usage.\n */\nfunction createReporter(\n runner: MochaRunner,\n options: StoryReporterOptions | { reporterOptions?: StoryReporterOptions } = {}\n) {\n const reporterOpts =\n \"reporterOptions\" in options && options.reporterOptions\n ? options.reporterOptions\n : (options as StoryReporterOptions);\n const opts: StoryReporterOptions = { ...DEFAULT_OPTIONS, ...reporterOpts };\n const projectRoot = opts.projectRoot ?? process.cwd();\n const specPath = opts.specPath ?? \"unknown\";\n const startTime = runner.startTime ?? Date.now();\n\n runner.suite && runner.on?.(\"end\", () => {\n const tests = collectTests(runner.suite);\n const rawTestCases: RawTestCase[] = [];\n\n // If specPath not provided, infer from first stored meta (all stored entries are for this spec in a single-spec run)\n const effectiveSpecPath =\n specPath !== \"unknown\"\n ? specPath\n : (() => {\n const all = getAllMeta();\n return all.length > 0 ? all[0].specRelative : \"unknown\";\n })();\n\n for (const test of tests) {\n const titlePath = getTitlePath(test);\n const meta = getMeta(effectiveSpecPath, titlePath);\n if (!meta) continue; // only document tests that used story.init()\n\n const status = mapCypressStateToRaw(test.state);\n\n // Map stored attachments to RawAttachment[]\n const storedAttachments = getAttachments(effectiveSpecPath, titlePath);\n const attachments: RawAttachment[] | undefined = storedAttachments?.map((a) => ({\n name: a.name,\n mediaType: a.mediaType,\n path: a.path,\n body: typeof a.body === 'string' ? a.body : a.body instanceof Buffer ? a.body.toString('base64') : undefined,\n encoding: a.encoding ?? (a.body instanceof Buffer ? 'BASE64' : undefined),\n charset: a.charset,\n fileName: a.fileName,\n stepIndex: a.stepIndex,\n stepId: a.stepId,\n }));\n\n // Extract step events (timing)\n const stepEvents: RawStepEvent[] = meta.steps\n .filter((s: { durationMs?: number }) => s.durationMs !== undefined)\n .map((s: { durationMs?: number; text: string }, i: number) => ({\n index: i,\n title: s.text,\n durationMs: s.durationMs,\n }));\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: effectiveSpecPath,\n sourceLine: 1,\n status,\n durationMs: test.duration,\n error: test.err\n ? { message: test.err.message, stack: test.err.stack }\n : undefined,\n attachments: attachments && attachments.length > 0 ? attachments : undefined,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n retry: 0,\n retries: 0,\n });\n }\n\n if (rawTestCases.length === 0) {\n clearStore();\n return;\n }\n\n const includeMetadata = opts.markdown?.includeMetadata ?? true;\n const rawRun: RawRun = {\n testCases: rawTestCases,\n startedAtMs: startTime,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n\n const rawRunPath = opts.rawRunPath;\n if (rawRunPath) {\n const absolutePath = path.isAbsolute(rawRunPath)\n ? rawRunPath\n : path.join(projectRoot, rawRunPath);\n const dir = path.dirname(absolutePath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(absolutePath, JSON.stringify({ schemaVersion: 1, ...rawRun }, null, 2), \"utf8\");\n }\n\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(opts);\n generator.generate(canonicalRun).catch((err) => {\n console.error(\"Failed to generate reports:\", err);\n });\n\n clearStore();\n });\n}\n\n/**\n * Alternative: build RawRun from Cypress Module API results + stored meta.\n * Use this when not using the Mocha reporter (e.g. in after:run plugin).\n * Call recordMeta via plugin during the run, then call this with cypress.run() result.\n */\nexport interface CypressRunResultTest {\n title: string[];\n state?: string;\n displayError?: string;\n duration?: number;\n attempts?: Array<{\n state?: string;\n duration?: number;\n error?: { message?: string; stack?: string };\n }>;\n}\n\nexport interface CypressRunResultRun {\n spec?: { relative?: string; absolute?: string };\n tests?: CypressRunResultTest[];\n}\n\nexport interface CypressRunResult {\n runs?: CypressRunResultRun[];\n config?: { projectRoot?: string };\n}\n\nexport function buildRawRunFromCypressResult(\n result: CypressRunResult,\n options: StoryReporterOptions = {}\n): RawRun {\n const projectRoot = options.projectRoot ?? result.config?.projectRoot ?? process.cwd();\n const rawTestCases: RawTestCase[] = [];\n\n for (const run of result.runs ?? []) {\n const specRelative = run.spec?.relative ?? \"unknown\";\n for (const test of run.tests ?? []) {\n const titlePath = test.title ?? [];\n const meta = getMeta(specRelative, titlePath);\n if (!meta) continue;\n\n const lastAttempt = test.attempts?.length\n ? test.attempts[test.attempts.length - 1]\n : undefined;\n const state = lastAttempt?.state ?? test.state;\n const err = lastAttempt?.error ?? (test.displayError ? { message: test.displayError } : undefined);\n const duration = lastAttempt?.duration ?? test.duration;\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: specRelative,\n sourceLine: 1,\n status: mapCypressStateToRaw(state),\n durationMs: duration,\n error: err ? { message: err.message, stack: err.stack } : undefined,\n });\n }\n }\n\n const includeMetadata = options.markdown?.includeMetadata ?? true;\n return {\n testCases: rawTestCases,\n startedAtMs: undefined,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n}\n\n/**\n * Generate reports from a RawRun (e.g. after buildRawRunFromCypressResult).\n */\nexport async function generateReportsFromRawRun(\n rawRun: RawRun,\n options: FormatterOptions\n): Promise<void> {\n if (rawRun.testCases.length === 0) return;\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(options);\n await generator.generate(canonicalRun);\n}\n\nexport default createReporter;\n","/**\n * Node-only store for story meta sent from the browser via cy.task.\n * Plugin writes; reporter reads. Keyed by spec + titlePath for merging with run results.\n */\n\nimport type { StoryMeta, ScopedAttachment, RecordMetaPayload } from \"./types\";\n\nexport interface StoredMeta {\n specRelative: string;\n titlePath: string[];\n meta: StoryMeta;\n attachments?: ScopedAttachment[];\n}\n\nfunction key(specRelative: string, titlePath: string[]): string {\n return `${specRelative}\\0${titlePath.join(\"\\0\")}`;\n}\n\nconst store = new Map<string, StoredMeta>();\n\nexport function recordMeta(payload: RecordMetaPayload): null {\n store.set(key(payload.specRelative, payload.titlePath), {\n specRelative: payload.specRelative,\n titlePath: payload.titlePath,\n meta: payload.meta,\n attachments: payload.attachments,\n });\n return null;\n}\n\nexport function getAttachments(specRelative: string, titlePath: string[]): ScopedAttachment[] | undefined {\n return store.get(key(specRelative, titlePath))?.attachments;\n}\n\nexport function getMeta(specRelative: string, titlePath: string[]): StoryMeta | undefined {\n return store.get(key(specRelative, titlePath))?.meta;\n}\n\nexport function getAllMeta(): StoredMeta[] {\n return Array.from(store.values());\n}\n\nexport function clearStore(): void {\n store.clear();\n}\n"],"mappings":"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","names":["path"]}
|
|
1
|
+
{"version":3,"sources":["../src/reporter.ts","../src/store.ts"],"sourcesContent":["/**\n * Cypress/Mocha reporter for executable-stories.\n * Builds RawRun (formatters schema) from run results + stored story meta, then generates reports.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { StoryMeta } from \"executable-stories-formatters\";\n\nimport {\n ReportGenerator,\n canonicalizeRun,\n readGitSha,\n readPackageVersion,\n detectCI,\n type RawRun,\n type RawTestCase,\n type RawAttachment,\n type RawStepEvent,\n type RawStatus,\n type FormatterOptions,\n} from \"executable-stories-formatters\";\n\nimport { getMeta, getAttachments, getAllMeta, clearStore } from \"./store\";\n\n// Re-export types from formatters\nexport type {\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n FormatterOptions,\n} from \"executable-stories-formatters\";\n\n// ============================================================================\n// Reporter Options\n// ============================================================================\n\nexport interface StoryReporterOptions extends FormatterOptions {\n /** If set, write raw run JSON (schemaVersion 1) to this path for CLI/binary */\n rawRunPath?: string;\n /** Spec file path (relative to projectRoot) for this run. If omitted, inferred from stored meta when possible. */\n specPath?: string;\n /** Project root. Defaults to process.cwd(). */\n projectRoot?: string;\n}\n\n// ============================================================================\n// Mocha Runner / Test types (minimal for reporter)\n// ============================================================================\n\ninterface MochaSuite {\n title: string;\n suites: MochaSuite[];\n tests: MochaTest[];\n parent?: MochaSuite;\n}\n\ninterface MochaTest {\n title: string;\n state?: string;\n duration?: number;\n err?: Error & { message?: string; stack?: string };\n parent?: MochaSuite;\n /** Mocha marks both `it.skip(...)` and a bodyless `it(\"title\")` as pending. */\n pending?: boolean;\n /** Absent only when the test was declared with no callback at all. */\n fn?: unknown;\n}\n\n/**\n * A bodyless `it(\"title\")` is Mocha's way of writing down behaviour that does\n * not exist yet, which is what a planned scenario is. `it.skip(title, fn)` is\n * also pending but keeps its body: that means \"do not run this now\", a\n * different claim, so it stays a skip.\n */\nfunction isPlannedTest(test: MochaTest): boolean {\n return test.pending === true && test.fn === undefined;\n}\n\ninterface MochaRunner {\n suite: MochaSuite;\n startTime?: number;\n on?(event: string, callback: () => void): void;\n}\n\nfunction getTitlePath(test: MochaTest): string[] {\n const path: string[] = [];\n let current: MochaSuite | MochaTest | undefined = test;\n while (current) {\n const title = \"title\" in current ? current.title : \"\";\n if (title) path.unshift(title);\n current = \"parent\" in current ? current.parent : undefined;\n }\n return path;\n}\n\nfunction collectTests(suite: MochaSuite): MochaTest[] {\n const tests = [...suite.tests];\n for (const s of suite.suites) {\n tests.push(...collectTests(s));\n }\n return tests;\n}\n\nfunction mapCypressStateToRaw(state: string | undefined): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"skip\";\n default:\n return \"unknown\";\n }\n}\n\n// ============================================================================\n// Reporter Implementation\n// ============================================================================\n\nconst DEFAULT_OPTIONS: Partial<StoryReporterOptions> = {\n projectRoot: process.cwd(),\n};\n\n/**\n * Create a Mocha reporter function that Cypress can use (--reporter path/to/reporter).\n * Cypress passes (runner, reporterOptions) with reporterOptions as the second argument directly.\n * Also supports options.reporterOptions for wrapped usage.\n */\nfunction createReporter(\n runner: MochaRunner,\n options: StoryReporterOptions | { reporterOptions?: StoryReporterOptions } = {}\n) {\n const reporterOpts =\n \"reporterOptions\" in options && options.reporterOptions\n ? options.reporterOptions\n : (options as StoryReporterOptions);\n const opts: StoryReporterOptions = { ...DEFAULT_OPTIONS, ...reporterOpts };\n const projectRoot = opts.projectRoot ?? process.cwd();\n const specPath = opts.specPath ?? \"unknown\";\n const startTime = runner.startTime ?? Date.now();\n\n runner.suite && runner.on?.(\"end\", () => {\n const tests = collectTests(runner.suite);\n const rawTestCases: RawTestCase[] = [];\n\n // If specPath not provided, infer from first stored meta (all stored entries are for this spec in a single-spec run)\n const effectiveSpecPath =\n specPath !== \"unknown\"\n ? specPath\n : (() => {\n const all = getAllMeta();\n return all.length > 0 ? all[0].specRelative : \"unknown\";\n })();\n\n // Bodyless tests never run story.init(), so they are collected separately\n // and only kept when the spec has real story tests to sit alongside.\n const plannedTests: MochaTest[] = [];\n\n for (const test of tests) {\n const titlePath = getTitlePath(test);\n const meta = getMeta(effectiveSpecPath, titlePath);\n if (!meta) {\n if (isPlannedTest(test)) plannedTests.push(test);\n continue; // only document tests that used story.init()\n }\n\n const status = mapCypressStateToRaw(test.state);\n\n // Map stored attachments to RawAttachment[]\n const storedAttachments = getAttachments(effectiveSpecPath, titlePath);\n const attachments: RawAttachment[] | undefined = storedAttachments?.map((a) => ({\n name: a.name,\n mediaType: a.mediaType,\n path: a.path,\n body: typeof a.body === 'string' ? a.body : a.body instanceof Buffer ? a.body.toString('base64') : undefined,\n encoding: a.encoding ?? (a.body instanceof Buffer ? 'BASE64' : undefined),\n charset: a.charset,\n fileName: a.fileName,\n stepIndex: a.stepIndex,\n stepId: a.stepId,\n }));\n\n // Extract step events (timing)\n const stepEvents: RawStepEvent[] = meta.steps\n .filter((s: { durationMs?: number }) => s.durationMs !== undefined)\n .map((s: { durationMs?: number; text: string }, i: number) => ({\n index: i,\n title: s.text,\n durationMs: s.durationMs,\n }));\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: effectiveSpecPath,\n sourceLine: 1,\n status,\n durationMs: test.duration,\n error: test.err\n ? { message: test.err.message, stack: test.err.stack }\n : undefined,\n attachments: attachments && attachments.length > 0 ? attachments : undefined,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n retry: 0,\n retries: 0,\n });\n }\n\n if (rawTestCases.length === 0) {\n clearStore();\n return;\n }\n\n for (const test of plannedTests) {\n const titlePath = getTitlePath(test);\n const suitePath = titlePath.slice(0, -1);\n rawTestCases.push({\n title: test.title,\n titlePath,\n story: {\n scenario: test.title,\n steps: [],\n ...(suitePath.length > 0 ? { suitePath } : {}),\n },\n sourceFile: effectiveSpecPath,\n sourceLine: 1,\n status: \"todo\",\n durationMs: 0,\n retry: 0,\n retries: 0,\n });\n }\n\n const includeMetadata = opts.markdown?.includeMetadata ?? true;\n const rawRun: RawRun = {\n testCases: rawTestCases,\n startedAtMs: startTime,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n\n const rawRunPath = opts.rawRunPath;\n if (rawRunPath) {\n const absolutePath = path.isAbsolute(rawRunPath)\n ? rawRunPath\n : path.join(projectRoot, rawRunPath);\n const dir = path.dirname(absolutePath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(absolutePath, JSON.stringify({ schemaVersion: 1, ...rawRun }, null, 2), \"utf8\");\n }\n\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(opts);\n generator.generate(canonicalRun).catch((err) => {\n console.error(\"Failed to generate reports:\", err);\n });\n\n clearStore();\n });\n}\n\n/**\n * Alternative: build RawRun from Cypress Module API results + stored meta.\n * Use this when not using the Mocha reporter (e.g. in after:run plugin).\n * Call recordMeta via plugin during the run, then call this with cypress.run() result.\n */\nexport interface CypressRunResultTest {\n title: string[];\n state?: string;\n displayError?: string;\n duration?: number;\n attempts?: Array<{\n state?: string;\n duration?: number;\n error?: { message?: string; stack?: string };\n }>;\n}\n\nexport interface CypressRunResultRun {\n spec?: { relative?: string; absolute?: string };\n tests?: CypressRunResultTest[];\n}\n\nexport interface CypressRunResult {\n runs?: CypressRunResultRun[];\n config?: { projectRoot?: string };\n}\n\nexport function buildRawRunFromCypressResult(\n result: CypressRunResult,\n options: StoryReporterOptions = {}\n): RawRun {\n const projectRoot = options.projectRoot ?? result.config?.projectRoot ?? process.cwd();\n const rawTestCases: RawTestCase[] = [];\n\n for (const run of result.runs ?? []) {\n const specRelative = run.spec?.relative ?? \"unknown\";\n for (const test of run.tests ?? []) {\n const titlePath = test.title ?? [];\n const meta = getMeta(specRelative, titlePath);\n if (!meta) continue;\n\n const lastAttempt = test.attempts?.length\n ? test.attempts[test.attempts.length - 1]\n : undefined;\n const state = lastAttempt?.state ?? test.state;\n const err = lastAttempt?.error ?? (test.displayError ? { message: test.displayError } : undefined);\n const duration = lastAttempt?.duration ?? test.duration;\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: specRelative,\n sourceLine: 1,\n status: mapCypressStateToRaw(state),\n durationMs: duration,\n error: err ? { message: err.message, stack: err.stack } : undefined,\n });\n }\n }\n\n const includeMetadata = options.markdown?.includeMetadata ?? true;\n return {\n testCases: rawTestCases,\n startedAtMs: undefined,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n}\n\n/**\n * Generate reports from a RawRun (e.g. after buildRawRunFromCypressResult).\n */\nexport async function generateReportsFromRawRun(\n rawRun: RawRun,\n options: FormatterOptions\n): Promise<void> {\n if (rawRun.testCases.length === 0) return;\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(options);\n await generator.generate(canonicalRun);\n}\n\nexport default createReporter;\n","/**\n * Node-only store for story meta sent from the browser via cy.task.\n * Plugin writes; reporter reads. Keyed by spec + titlePath for merging with run results.\n */\n\nimport type { StoryMeta, ScopedAttachment, RecordMetaPayload } from \"./types\";\n\nexport interface StoredMeta {\n specRelative: string;\n titlePath: string[];\n meta: StoryMeta;\n attachments?: ScopedAttachment[];\n}\n\nfunction key(specRelative: string, titlePath: string[]): string {\n return `${specRelative}\\0${titlePath.join(\"\\0\")}`;\n}\n\nconst store = new Map<string, StoredMeta>();\n\nexport function recordMeta(payload: RecordMetaPayload): null {\n store.set(key(payload.specRelative, payload.titlePath), {\n specRelative: payload.specRelative,\n titlePath: payload.titlePath,\n meta: payload.meta,\n attachments: payload.attachments,\n });\n return null;\n}\n\nexport function getAttachments(specRelative: string, titlePath: string[]): ScopedAttachment[] | undefined {\n return store.get(key(specRelative, titlePath))?.attachments;\n}\n\nexport function getMeta(specRelative: string, titlePath: string[]): StoryMeta | undefined {\n return store.get(key(specRelative, titlePath))?.meta;\n}\n\nexport function getAllMeta(): StoredMeta[] {\n return Array.from(store.values());\n}\n\nexport function clearStore(): void {\n store.clear();\n}\n"],"mappings":"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","names":["path"]}
|
package/dist/reporter.d.cts
CHANGED
|
@@ -29,6 +29,10 @@ interface MochaTest {
|
|
|
29
29
|
stack?: string;
|
|
30
30
|
};
|
|
31
31
|
parent?: MochaSuite;
|
|
32
|
+
/** Mocha marks both `it.skip(...)` and a bodyless `it("title")` as pending. */
|
|
33
|
+
pending?: boolean;
|
|
34
|
+
/** Absent only when the test was declared with no callback at all. */
|
|
35
|
+
fn?: unknown;
|
|
32
36
|
}
|
|
33
37
|
interface MochaRunner {
|
|
34
38
|
suite: MochaSuite;
|
package/dist/reporter.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ interface MochaTest {
|
|
|
29
29
|
stack?: string;
|
|
30
30
|
};
|
|
31
31
|
parent?: MochaSuite;
|
|
32
|
+
/** Mocha marks both `it.skip(...)` and a bodyless `it("title")` as pending. */
|
|
33
|
+
pending?: boolean;
|
|
34
|
+
/** Absent only when the test was declared with no callback at all. */
|
|
35
|
+
fn?: unknown;
|
|
32
36
|
}
|
|
33
37
|
interface MochaRunner {
|
|
34
38
|
suite: MochaSuite;
|
package/dist/reporter.js
CHANGED
|
@@ -28,6 +28,9 @@ function clearStore() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// src/reporter.ts
|
|
31
|
+
function isPlannedTest(test) {
|
|
32
|
+
return test.pending === true && test.fn === void 0;
|
|
33
|
+
}
|
|
31
34
|
function getTitlePath(test) {
|
|
32
35
|
const path2 = [];
|
|
33
36
|
let current = test;
|
|
@@ -73,10 +76,14 @@ function createReporter(runner, options = {}) {
|
|
|
73
76
|
const all = getAllMeta();
|
|
74
77
|
return all.length > 0 ? all[0].specRelative : "unknown";
|
|
75
78
|
})();
|
|
79
|
+
const plannedTests = [];
|
|
76
80
|
for (const test of tests) {
|
|
77
81
|
const titlePath = getTitlePath(test);
|
|
78
82
|
const meta = getMeta(effectiveSpecPath, titlePath);
|
|
79
|
-
if (!meta)
|
|
83
|
+
if (!meta) {
|
|
84
|
+
if (isPlannedTest(test)) plannedTests.push(test);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
80
87
|
const status = mapCypressStateToRaw(test.state);
|
|
81
88
|
const storedAttachments = getAttachments(effectiveSpecPath, titlePath);
|
|
82
89
|
const attachments = storedAttachments?.map((a) => ({
|
|
@@ -114,6 +121,25 @@ function createReporter(runner, options = {}) {
|
|
|
114
121
|
clearStore();
|
|
115
122
|
return;
|
|
116
123
|
}
|
|
124
|
+
for (const test of plannedTests) {
|
|
125
|
+
const titlePath = getTitlePath(test);
|
|
126
|
+
const suitePath = titlePath.slice(0, -1);
|
|
127
|
+
rawTestCases.push({
|
|
128
|
+
title: test.title,
|
|
129
|
+
titlePath,
|
|
130
|
+
story: {
|
|
131
|
+
scenario: test.title,
|
|
132
|
+
steps: [],
|
|
133
|
+
...suitePath.length > 0 ? { suitePath } : {}
|
|
134
|
+
},
|
|
135
|
+
sourceFile: effectiveSpecPath,
|
|
136
|
+
sourceLine: 1,
|
|
137
|
+
status: "todo",
|
|
138
|
+
durationMs: 0,
|
|
139
|
+
retry: 0,
|
|
140
|
+
retries: 0
|
|
141
|
+
});
|
|
142
|
+
}
|
|
117
143
|
const includeMetadata = opts.markdown?.includeMetadata ?? true;
|
|
118
144
|
const rawRun = {
|
|
119
145
|
testCases: rawTestCases,
|
package/dist/reporter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter.ts","../src/store.ts"],"sourcesContent":["/**\n * Cypress/Mocha reporter for executable-stories.\n * Builds RawRun (formatters schema) from run results + stored story meta, then generates reports.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { StoryMeta } from \"executable-stories-formatters\";\n\nimport {\n ReportGenerator,\n canonicalizeRun,\n readGitSha,\n readPackageVersion,\n detectCI,\n type RawRun,\n type RawTestCase,\n type RawAttachment,\n type RawStepEvent,\n type RawStatus,\n type FormatterOptions,\n} from \"executable-stories-formatters\";\n\nimport { getMeta, getAttachments, getAllMeta, clearStore } from \"./store\";\n\n// Re-export types from formatters\nexport type {\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n FormatterOptions,\n} from \"executable-stories-formatters\";\n\n// ============================================================================\n// Reporter Options\n// ============================================================================\n\nexport interface StoryReporterOptions extends FormatterOptions {\n /** If set, write raw run JSON (schemaVersion 1) to this path for CLI/binary */\n rawRunPath?: string;\n /** Spec file path (relative to projectRoot) for this run. If omitted, inferred from stored meta when possible. */\n specPath?: string;\n /** Project root. Defaults to process.cwd(). */\n projectRoot?: string;\n}\n\n// ============================================================================\n// Mocha Runner / Test types (minimal for reporter)\n// ============================================================================\n\ninterface MochaSuite {\n title: string;\n suites: MochaSuite[];\n tests: MochaTest[];\n parent?: MochaSuite;\n}\n\ninterface MochaTest {\n title: string;\n state?: string;\n duration?: number;\n err?: Error & { message?: string; stack?: string };\n parent?: MochaSuite;\n}\n\ninterface MochaRunner {\n suite: MochaSuite;\n startTime?: number;\n on?(event: string, callback: () => void): void;\n}\n\nfunction getTitlePath(test: MochaTest): string[] {\n const path: string[] = [];\n let current: MochaSuite | MochaTest | undefined = test;\n while (current) {\n const title = \"title\" in current ? current.title : \"\";\n if (title) path.unshift(title);\n current = \"parent\" in current ? current.parent : undefined;\n }\n return path;\n}\n\nfunction collectTests(suite: MochaSuite): MochaTest[] {\n const tests = [...suite.tests];\n for (const s of suite.suites) {\n tests.push(...collectTests(s));\n }\n return tests;\n}\n\nfunction mapCypressStateToRaw(state: string | undefined): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"skip\";\n default:\n return \"unknown\";\n }\n}\n\n// ============================================================================\n// Reporter Implementation\n// ============================================================================\n\nconst DEFAULT_OPTIONS: Partial<StoryReporterOptions> = {\n projectRoot: process.cwd(),\n};\n\n/**\n * Create a Mocha reporter function that Cypress can use (--reporter path/to/reporter).\n * Cypress passes (runner, reporterOptions) with reporterOptions as the second argument directly.\n * Also supports options.reporterOptions for wrapped usage.\n */\nfunction createReporter(\n runner: MochaRunner,\n options: StoryReporterOptions | { reporterOptions?: StoryReporterOptions } = {}\n) {\n const reporterOpts =\n \"reporterOptions\" in options && options.reporterOptions\n ? options.reporterOptions\n : (options as StoryReporterOptions);\n const opts: StoryReporterOptions = { ...DEFAULT_OPTIONS, ...reporterOpts };\n const projectRoot = opts.projectRoot ?? process.cwd();\n const specPath = opts.specPath ?? \"unknown\";\n const startTime = runner.startTime ?? Date.now();\n\n runner.suite && runner.on?.(\"end\", () => {\n const tests = collectTests(runner.suite);\n const rawTestCases: RawTestCase[] = [];\n\n // If specPath not provided, infer from first stored meta (all stored entries are for this spec in a single-spec run)\n const effectiveSpecPath =\n specPath !== \"unknown\"\n ? specPath\n : (() => {\n const all = getAllMeta();\n return all.length > 0 ? all[0].specRelative : \"unknown\";\n })();\n\n for (const test of tests) {\n const titlePath = getTitlePath(test);\n const meta = getMeta(effectiveSpecPath, titlePath);\n if (!meta) continue; // only document tests that used story.init()\n\n const status = mapCypressStateToRaw(test.state);\n\n // Map stored attachments to RawAttachment[]\n const storedAttachments = getAttachments(effectiveSpecPath, titlePath);\n const attachments: RawAttachment[] | undefined = storedAttachments?.map((a) => ({\n name: a.name,\n mediaType: a.mediaType,\n path: a.path,\n body: typeof a.body === 'string' ? a.body : a.body instanceof Buffer ? a.body.toString('base64') : undefined,\n encoding: a.encoding ?? (a.body instanceof Buffer ? 'BASE64' : undefined),\n charset: a.charset,\n fileName: a.fileName,\n stepIndex: a.stepIndex,\n stepId: a.stepId,\n }));\n\n // Extract step events (timing)\n const stepEvents: RawStepEvent[] = meta.steps\n .filter((s: { durationMs?: number }) => s.durationMs !== undefined)\n .map((s: { durationMs?: number; text: string }, i: number) => ({\n index: i,\n title: s.text,\n durationMs: s.durationMs,\n }));\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: effectiveSpecPath,\n sourceLine: 1,\n status,\n durationMs: test.duration,\n error: test.err\n ? { message: test.err.message, stack: test.err.stack }\n : undefined,\n attachments: attachments && attachments.length > 0 ? attachments : undefined,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n retry: 0,\n retries: 0,\n });\n }\n\n if (rawTestCases.length === 0) {\n clearStore();\n return;\n }\n\n const includeMetadata = opts.markdown?.includeMetadata ?? true;\n const rawRun: RawRun = {\n testCases: rawTestCases,\n startedAtMs: startTime,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n\n const rawRunPath = opts.rawRunPath;\n if (rawRunPath) {\n const absolutePath = path.isAbsolute(rawRunPath)\n ? rawRunPath\n : path.join(projectRoot, rawRunPath);\n const dir = path.dirname(absolutePath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(absolutePath, JSON.stringify({ schemaVersion: 1, ...rawRun }, null, 2), \"utf8\");\n }\n\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(opts);\n generator.generate(canonicalRun).catch((err) => {\n console.error(\"Failed to generate reports:\", err);\n });\n\n clearStore();\n });\n}\n\n/**\n * Alternative: build RawRun from Cypress Module API results + stored meta.\n * Use this when not using the Mocha reporter (e.g. in after:run plugin).\n * Call recordMeta via plugin during the run, then call this with cypress.run() result.\n */\nexport interface CypressRunResultTest {\n title: string[];\n state?: string;\n displayError?: string;\n duration?: number;\n attempts?: Array<{\n state?: string;\n duration?: number;\n error?: { message?: string; stack?: string };\n }>;\n}\n\nexport interface CypressRunResultRun {\n spec?: { relative?: string; absolute?: string };\n tests?: CypressRunResultTest[];\n}\n\nexport interface CypressRunResult {\n runs?: CypressRunResultRun[];\n config?: { projectRoot?: string };\n}\n\nexport function buildRawRunFromCypressResult(\n result: CypressRunResult,\n options: StoryReporterOptions = {}\n): RawRun {\n const projectRoot = options.projectRoot ?? result.config?.projectRoot ?? process.cwd();\n const rawTestCases: RawTestCase[] = [];\n\n for (const run of result.runs ?? []) {\n const specRelative = run.spec?.relative ?? \"unknown\";\n for (const test of run.tests ?? []) {\n const titlePath = test.title ?? [];\n const meta = getMeta(specRelative, titlePath);\n if (!meta) continue;\n\n const lastAttempt = test.attempts?.length\n ? test.attempts[test.attempts.length - 1]\n : undefined;\n const state = lastAttempt?.state ?? test.state;\n const err = lastAttempt?.error ?? (test.displayError ? { message: test.displayError } : undefined);\n const duration = lastAttempt?.duration ?? test.duration;\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: specRelative,\n sourceLine: 1,\n status: mapCypressStateToRaw(state),\n durationMs: duration,\n error: err ? { message: err.message, stack: err.stack } : undefined,\n });\n }\n }\n\n const includeMetadata = options.markdown?.includeMetadata ?? true;\n return {\n testCases: rawTestCases,\n startedAtMs: undefined,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n}\n\n/**\n * Generate reports from a RawRun (e.g. after buildRawRunFromCypressResult).\n */\nexport async function generateReportsFromRawRun(\n rawRun: RawRun,\n options: FormatterOptions\n): Promise<void> {\n if (rawRun.testCases.length === 0) return;\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(options);\n await generator.generate(canonicalRun);\n}\n\nexport default createReporter;\n","/**\n * Node-only store for story meta sent from the browser via cy.task.\n * Plugin writes; reporter reads. Keyed by spec + titlePath for merging with run results.\n */\n\nimport type { StoryMeta, ScopedAttachment, RecordMetaPayload } from \"./types\";\n\nexport interface StoredMeta {\n specRelative: string;\n titlePath: string[];\n meta: StoryMeta;\n attachments?: ScopedAttachment[];\n}\n\nfunction key(specRelative: string, titlePath: string[]): string {\n return `${specRelative}\\0${titlePath.join(\"\\0\")}`;\n}\n\nconst store = new Map<string, StoredMeta>();\n\nexport function recordMeta(payload: RecordMetaPayload): null {\n store.set(key(payload.specRelative, payload.titlePath), {\n specRelative: payload.specRelative,\n titlePath: payload.titlePath,\n meta: payload.meta,\n attachments: payload.attachments,\n });\n return null;\n}\n\nexport function getAttachments(specRelative: string, titlePath: string[]): ScopedAttachment[] | undefined {\n return store.get(key(specRelative, titlePath))?.attachments;\n}\n\nexport function getMeta(specRelative: string, titlePath: string[]): StoryMeta | undefined {\n return store.get(key(specRelative, titlePath))?.meta;\n}\n\nexport function getAllMeta(): StoredMeta[] {\n return Array.from(store.values());\n}\n\nexport function clearStore(): void {\n store.clear();\n}\n"],"mappings":"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","names":["path"]}
|
|
1
|
+
{"version":3,"sources":["../src/reporter.ts","../src/store.ts"],"sourcesContent":["/**\n * Cypress/Mocha reporter for executable-stories.\n * Builds RawRun (formatters schema) from run results + stored story meta, then generates reports.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { StoryMeta } from \"executable-stories-formatters\";\n\nimport {\n ReportGenerator,\n canonicalizeRun,\n readGitSha,\n readPackageVersion,\n detectCI,\n type RawRun,\n type RawTestCase,\n type RawAttachment,\n type RawStepEvent,\n type RawStatus,\n type FormatterOptions,\n} from \"executable-stories-formatters\";\n\nimport { getMeta, getAttachments, getAllMeta, clearStore } from \"./store\";\n\n// Re-export types from formatters\nexport type {\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n FormatterOptions,\n} from \"executable-stories-formatters\";\n\n// ============================================================================\n// Reporter Options\n// ============================================================================\n\nexport interface StoryReporterOptions extends FormatterOptions {\n /** If set, write raw run JSON (schemaVersion 1) to this path for CLI/binary */\n rawRunPath?: string;\n /** Spec file path (relative to projectRoot) for this run. If omitted, inferred from stored meta when possible. */\n specPath?: string;\n /** Project root. Defaults to process.cwd(). */\n projectRoot?: string;\n}\n\n// ============================================================================\n// Mocha Runner / Test types (minimal for reporter)\n// ============================================================================\n\ninterface MochaSuite {\n title: string;\n suites: MochaSuite[];\n tests: MochaTest[];\n parent?: MochaSuite;\n}\n\ninterface MochaTest {\n title: string;\n state?: string;\n duration?: number;\n err?: Error & { message?: string; stack?: string };\n parent?: MochaSuite;\n /** Mocha marks both `it.skip(...)` and a bodyless `it(\"title\")` as pending. */\n pending?: boolean;\n /** Absent only when the test was declared with no callback at all. */\n fn?: unknown;\n}\n\n/**\n * A bodyless `it(\"title\")` is Mocha's way of writing down behaviour that does\n * not exist yet, which is what a planned scenario is. `it.skip(title, fn)` is\n * also pending but keeps its body: that means \"do not run this now\", a\n * different claim, so it stays a skip.\n */\nfunction isPlannedTest(test: MochaTest): boolean {\n return test.pending === true && test.fn === undefined;\n}\n\ninterface MochaRunner {\n suite: MochaSuite;\n startTime?: number;\n on?(event: string, callback: () => void): void;\n}\n\nfunction getTitlePath(test: MochaTest): string[] {\n const path: string[] = [];\n let current: MochaSuite | MochaTest | undefined = test;\n while (current) {\n const title = \"title\" in current ? current.title : \"\";\n if (title) path.unshift(title);\n current = \"parent\" in current ? current.parent : undefined;\n }\n return path;\n}\n\nfunction collectTests(suite: MochaSuite): MochaTest[] {\n const tests = [...suite.tests];\n for (const s of suite.suites) {\n tests.push(...collectTests(s));\n }\n return tests;\n}\n\nfunction mapCypressStateToRaw(state: string | undefined): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"skip\";\n default:\n return \"unknown\";\n }\n}\n\n// ============================================================================\n// Reporter Implementation\n// ============================================================================\n\nconst DEFAULT_OPTIONS: Partial<StoryReporterOptions> = {\n projectRoot: process.cwd(),\n};\n\n/**\n * Create a Mocha reporter function that Cypress can use (--reporter path/to/reporter).\n * Cypress passes (runner, reporterOptions) with reporterOptions as the second argument directly.\n * Also supports options.reporterOptions for wrapped usage.\n */\nfunction createReporter(\n runner: MochaRunner,\n options: StoryReporterOptions | { reporterOptions?: StoryReporterOptions } = {}\n) {\n const reporterOpts =\n \"reporterOptions\" in options && options.reporterOptions\n ? options.reporterOptions\n : (options as StoryReporterOptions);\n const opts: StoryReporterOptions = { ...DEFAULT_OPTIONS, ...reporterOpts };\n const projectRoot = opts.projectRoot ?? process.cwd();\n const specPath = opts.specPath ?? \"unknown\";\n const startTime = runner.startTime ?? Date.now();\n\n runner.suite && runner.on?.(\"end\", () => {\n const tests = collectTests(runner.suite);\n const rawTestCases: RawTestCase[] = [];\n\n // If specPath not provided, infer from first stored meta (all stored entries are for this spec in a single-spec run)\n const effectiveSpecPath =\n specPath !== \"unknown\"\n ? specPath\n : (() => {\n const all = getAllMeta();\n return all.length > 0 ? all[0].specRelative : \"unknown\";\n })();\n\n // Bodyless tests never run story.init(), so they are collected separately\n // and only kept when the spec has real story tests to sit alongside.\n const plannedTests: MochaTest[] = [];\n\n for (const test of tests) {\n const titlePath = getTitlePath(test);\n const meta = getMeta(effectiveSpecPath, titlePath);\n if (!meta) {\n if (isPlannedTest(test)) plannedTests.push(test);\n continue; // only document tests that used story.init()\n }\n\n const status = mapCypressStateToRaw(test.state);\n\n // Map stored attachments to RawAttachment[]\n const storedAttachments = getAttachments(effectiveSpecPath, titlePath);\n const attachments: RawAttachment[] | undefined = storedAttachments?.map((a) => ({\n name: a.name,\n mediaType: a.mediaType,\n path: a.path,\n body: typeof a.body === 'string' ? a.body : a.body instanceof Buffer ? a.body.toString('base64') : undefined,\n encoding: a.encoding ?? (a.body instanceof Buffer ? 'BASE64' : undefined),\n charset: a.charset,\n fileName: a.fileName,\n stepIndex: a.stepIndex,\n stepId: a.stepId,\n }));\n\n // Extract step events (timing)\n const stepEvents: RawStepEvent[] = meta.steps\n .filter((s: { durationMs?: number }) => s.durationMs !== undefined)\n .map((s: { durationMs?: number; text: string }, i: number) => ({\n index: i,\n title: s.text,\n durationMs: s.durationMs,\n }));\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: effectiveSpecPath,\n sourceLine: 1,\n status,\n durationMs: test.duration,\n error: test.err\n ? { message: test.err.message, stack: test.err.stack }\n : undefined,\n attachments: attachments && attachments.length > 0 ? attachments : undefined,\n stepEvents: stepEvents.length > 0 ? stepEvents : undefined,\n retry: 0,\n retries: 0,\n });\n }\n\n if (rawTestCases.length === 0) {\n clearStore();\n return;\n }\n\n for (const test of plannedTests) {\n const titlePath = getTitlePath(test);\n const suitePath = titlePath.slice(0, -1);\n rawTestCases.push({\n title: test.title,\n titlePath,\n story: {\n scenario: test.title,\n steps: [],\n ...(suitePath.length > 0 ? { suitePath } : {}),\n },\n sourceFile: effectiveSpecPath,\n sourceLine: 1,\n status: \"todo\",\n durationMs: 0,\n retry: 0,\n retries: 0,\n });\n }\n\n const includeMetadata = opts.markdown?.includeMetadata ?? true;\n const rawRun: RawRun = {\n testCases: rawTestCases,\n startedAtMs: startTime,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n\n const rawRunPath = opts.rawRunPath;\n if (rawRunPath) {\n const absolutePath = path.isAbsolute(rawRunPath)\n ? rawRunPath\n : path.join(projectRoot, rawRunPath);\n const dir = path.dirname(absolutePath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(absolutePath, JSON.stringify({ schemaVersion: 1, ...rawRun }, null, 2), \"utf8\");\n }\n\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(opts);\n generator.generate(canonicalRun).catch((err) => {\n console.error(\"Failed to generate reports:\", err);\n });\n\n clearStore();\n });\n}\n\n/**\n * Alternative: build RawRun from Cypress Module API results + stored meta.\n * Use this when not using the Mocha reporter (e.g. in after:run plugin).\n * Call recordMeta via plugin during the run, then call this with cypress.run() result.\n */\nexport interface CypressRunResultTest {\n title: string[];\n state?: string;\n displayError?: string;\n duration?: number;\n attempts?: Array<{\n state?: string;\n duration?: number;\n error?: { message?: string; stack?: string };\n }>;\n}\n\nexport interface CypressRunResultRun {\n spec?: { relative?: string; absolute?: string };\n tests?: CypressRunResultTest[];\n}\n\nexport interface CypressRunResult {\n runs?: CypressRunResultRun[];\n config?: { projectRoot?: string };\n}\n\nexport function buildRawRunFromCypressResult(\n result: CypressRunResult,\n options: StoryReporterOptions = {}\n): RawRun {\n const projectRoot = options.projectRoot ?? result.config?.projectRoot ?? process.cwd();\n const rawTestCases: RawTestCase[] = [];\n\n for (const run of result.runs ?? []) {\n const specRelative = run.spec?.relative ?? \"unknown\";\n for (const test of run.tests ?? []) {\n const titlePath = test.title ?? [];\n const meta = getMeta(specRelative, titlePath);\n if (!meta) continue;\n\n const lastAttempt = test.attempts?.length\n ? test.attempts[test.attempts.length - 1]\n : undefined;\n const state = lastAttempt?.state ?? test.state;\n const err = lastAttempt?.error ?? (test.displayError ? { message: test.displayError } : undefined);\n const duration = lastAttempt?.duration ?? test.duration;\n\n rawTestCases.push({\n title: meta.scenario,\n titlePath: meta.suitePath ? [...meta.suitePath, meta.scenario] : [meta.scenario],\n story: meta,\n sourceFile: specRelative,\n sourceLine: 1,\n status: mapCypressStateToRaw(state),\n durationMs: duration,\n error: err ? { message: err.message, stack: err.stack } : undefined,\n });\n }\n }\n\n const includeMetadata = options.markdown?.includeMetadata ?? true;\n return {\n testCases: rawTestCases,\n startedAtMs: undefined,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: includeMetadata ? readPackageVersion(projectRoot) : undefined,\n gitSha: includeMetadata ? readGitSha(projectRoot) : undefined,\n ci: detectCI(),\n };\n}\n\n/**\n * Generate reports from a RawRun (e.g. after buildRawRunFromCypressResult).\n */\nexport async function generateReportsFromRawRun(\n rawRun: RawRun,\n options: FormatterOptions\n): Promise<void> {\n if (rawRun.testCases.length === 0) return;\n const canonicalRun = canonicalizeRun(rawRun);\n const generator = new ReportGenerator(options);\n await generator.generate(canonicalRun);\n}\n\nexport default createReporter;\n","/**\n * Node-only store for story meta sent from the browser via cy.task.\n * Plugin writes; reporter reads. Keyed by spec + titlePath for merging with run results.\n */\n\nimport type { StoryMeta, ScopedAttachment, RecordMetaPayload } from \"./types\";\n\nexport interface StoredMeta {\n specRelative: string;\n titlePath: string[];\n meta: StoryMeta;\n attachments?: ScopedAttachment[];\n}\n\nfunction key(specRelative: string, titlePath: string[]): string {\n return `${specRelative}\\0${titlePath.join(\"\\0\")}`;\n}\n\nconst store = new Map<string, StoredMeta>();\n\nexport function recordMeta(payload: RecordMetaPayload): null {\n store.set(key(payload.specRelative, payload.titlePath), {\n specRelative: payload.specRelative,\n titlePath: payload.titlePath,\n meta: payload.meta,\n attachments: payload.attachments,\n });\n return null;\n}\n\nexport function getAttachments(specRelative: string, titlePath: string[]): ScopedAttachment[] | undefined {\n return store.get(key(specRelative, titlePath))?.attachments;\n}\n\nexport function getMeta(specRelative: string, titlePath: string[]): StoryMeta | undefined {\n return store.get(key(specRelative, titlePath))?.meta;\n}\n\nexport function getAllMeta(): StoredMeta[] {\n return Array.from(store.values());\n}\n\nexport function clearStore(): void {\n store.clear();\n}\n"],"mappings":"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","names":["path"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "executable-stories-cypress",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "BDD-style executable stories for Cypress with documentation generation",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"cypress": ">=13.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"executable-stories-formatters": "1.
|
|
41
|
+
"executable-stories-formatters": "1.9.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"cypress": "^15.14.2",
|