executable-stories-formatters 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import "fs";
3
- import * as path6 from "path";
3
+ import * as path7 from "path";
4
4
  import * as fsPromises from "fs/promises";
5
5
 
6
6
  // src/converters/acl/status.ts
@@ -3351,6 +3351,12 @@ body {
3351
3351
  font-weight: 600;
3352
3352
  font-size: 0.875rem;
3353
3353
  color: var(--foreground);
3354
+ text-decoration: none;
3355
+ cursor: pointer;
3356
+ }
3357
+
3358
+ a.toc-title:hover {
3359
+ color: var(--primary);
3354
3360
  }
3355
3361
 
3356
3362
  .toc-feature {
@@ -3528,7 +3534,7 @@ function corporateBuildBody(args, deps) {
3528
3534
  const sidebar = `
3529
3535
  <nav class="toc">
3530
3536
  <div class="toc-header">
3531
- <div class="toc-title">Test Report</div>
3537
+ <a href="#" class="toc-title" onclick="window.scrollTo({top:0,behavior:'smooth'});return false;">Test Report</a>
3532
3538
  <div class="toc-stats">
3533
3539
  <div class="toc-stat-row">
3534
3540
  <span class="toc-stat-label">Total</span>
@@ -13156,7 +13162,7 @@ function renderScenario(args, deps) {
13156
13162
  <div class="scenario-meta">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>
13157
13163
  </div>
13158
13164
  <div class="scenario-actions">
13159
- <button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown">&#x2398;</button>
13165
+ <button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
13160
13166
  <button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
13161
13167
  <span class="scenario-duration">${duration}</span>
13162
13168
  </div>
@@ -13535,7 +13541,7 @@ function renderToc(args, deps) {
13535
13541
  }
13536
13542
  return `<nav class="toc-sidebar" aria-label="Table of contents">
13537
13543
  <div class="toc-header">
13538
- <span class="toc-title">Contents</span>
13544
+ <a href="#" class="toc-title" onclick="window.scrollTo({top:0,behavior:'smooth'});return false;">Contents</a>
13539
13545
  </div>
13540
13546
  <div class="toc-body">
13541
13547
  ${features.join("\n")}
@@ -14996,8 +15002,8 @@ function extractDocAttachments(step) {
14996
15002
  }
14997
15003
  return attachments;
14998
15004
  }
14999
- function guessMediaType(path7) {
15000
- const lower = path7.toLowerCase();
15005
+ function guessMediaType(path8) {
15006
+ const lower = path8.toLowerCase();
15001
15007
  if (lower.endsWith(".png")) return "image/png";
15002
15008
  if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
15003
15009
  if (lower.endsWith(".gif")) return "image/gif";
@@ -15138,11 +15144,11 @@ var CucumberHtmlFormatter = class {
15138
15144
  for (const envelope of envelopes) {
15139
15145
  const accepted = htmlStream.write(envelope);
15140
15146
  if (!accepted) {
15141
- await new Promise((resolve5) => htmlStream.once("drain", resolve5));
15147
+ await new Promise((resolve7) => htmlStream.once("drain", resolve7));
15142
15148
  }
15143
15149
  }
15144
- await new Promise((resolve5, reject) => {
15145
- collector.on("finish", resolve5);
15150
+ await new Promise((resolve7, reject) => {
15151
+ collector.on("finish", resolve7);
15146
15152
  collector.on("error", reject);
15147
15153
  htmlStream.end();
15148
15154
  });
@@ -16144,6 +16150,177 @@ function replaceAssetRef(html, original, replacement) {
16144
16150
  return html;
16145
16151
  }
16146
16152
 
16153
+ // src/formatters/astro.ts
16154
+ var AstroFormatter = class _AstroFormatter {
16155
+ markdownFormatter;
16156
+ title;
16157
+ constructor(options = {}) {
16158
+ this.title = options.markdown?.title ?? "User Stories";
16159
+ this.markdownFormatter = new MarkdownFormatter({
16160
+ ...options.markdown,
16161
+ title: this.title,
16162
+ stepStyle: "gherkin",
16163
+ includeFrontMatter: false,
16164
+ includeSummaryTable: false,
16165
+ includeMetadata: false
16166
+ });
16167
+ }
16168
+ format(run) {
16169
+ const markdown = this.markdownFormatter.format(run);
16170
+ const body = markdown.replace(/^# .+\n\n?/, "");
16171
+ const frontmatter = this.buildFrontmatter(run);
16172
+ return `${frontmatter}
16173
+ ${body}`;
16174
+ }
16175
+ buildFrontmatter(run) {
16176
+ const badge = _AstroFormatter.computeBadge(run.testCases);
16177
+ const count = run.testCases.length;
16178
+ const description = `${count} scenario${count !== 1 ? "s" : ""} \u2014 ${badge.text.toLowerCase()}`;
16179
+ const lines = [
16180
+ "---",
16181
+ `title: ${this.title}`,
16182
+ `description: ${description}`,
16183
+ "sidebar:",
16184
+ " badge:",
16185
+ ` text: ${badge.text}`,
16186
+ ` variant: ${badge.variant}`,
16187
+ "---"
16188
+ ];
16189
+ return lines.join("\n");
16190
+ }
16191
+ static computeBadge(testCases) {
16192
+ const statuses = new Set(testCases.map((tc) => tc.status));
16193
+ if (statuses.has("failed")) return { text: "Failed", variant: "danger" };
16194
+ if (statuses.has("pending")) return { text: "Pending", variant: "caution" };
16195
+ if (statuses.has("skipped") && !statuses.has("passed")) return { text: "Skipped", variant: "caution" };
16196
+ return { text: "Passed", variant: "success" };
16197
+ }
16198
+ };
16199
+
16200
+ // src/formatters/astro-assets.ts
16201
+ import * as fs4 from "fs";
16202
+ import * as path4 from "path";
16203
+ var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
16204
+ function isLocalPath(src) {
16205
+ const trimmed = src.trim();
16206
+ if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
16207
+ return false;
16208
+ }
16209
+ return !path4.posix.isAbsolute(trimmed) && !path4.win32.isAbsolute(trimmed);
16210
+ }
16211
+ function stripCodeContent(markdown) {
16212
+ let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
16213
+ result = result.replace(/(`+)(?:(?!\1).)+\1/g, "");
16214
+ result = result.replace(/<pre\b[^>]*>[\s\S]*?<\/pre>/gi, "");
16215
+ result = result.replace(/<code\b[^>]*>[\s\S]*?<\/code>/gi, "");
16216
+ return result;
16217
+ }
16218
+ function scanMarkdownAssets(markdown) {
16219
+ const found = /* @__PURE__ */ new Set();
16220
+ const stripped = stripCodeContent(markdown);
16221
+ const mdImageRe = /!\[[^\]]*\]\(([^)"'\s]+)(?:\s+["'][^"']*["'])?\s*\)/g;
16222
+ let match;
16223
+ while ((match = mdImageRe.exec(stripped)) !== null) {
16224
+ const src = match[1].trim();
16225
+ if (isLocalPath(src)) {
16226
+ found.add(src);
16227
+ }
16228
+ }
16229
+ const htmlSrcRe = /<(?:img|source|video)[^>]+\bsrc=["']([^"']+)["'][^>]*>/gi;
16230
+ while ((match = htmlSrcRe.exec(stripped)) !== null) {
16231
+ const src = match[1].trim();
16232
+ if (isLocalPath(src)) {
16233
+ found.add(src);
16234
+ }
16235
+ }
16236
+ return Array.from(found);
16237
+ }
16238
+ function splitByCode(markdown) {
16239
+ const codeRe = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$|<pre\b[^>]*>[\s\S]*?<\/pre>|<code\b[^>]*>[\s\S]*?<\/code>|(`+)(?:(?!\2).)+\2/gim;
16240
+ const segments = [];
16241
+ let lastIndex = 0;
16242
+ for (const match of markdown.matchAll(codeRe)) {
16243
+ if (match.index > lastIndex) {
16244
+ segments.push(markdown.slice(lastIndex, match.index));
16245
+ }
16246
+ segments.push(match[0]);
16247
+ lastIndex = match.index + match[0].length;
16248
+ }
16249
+ if (lastIndex < markdown.length) {
16250
+ segments.push(markdown.slice(lastIndex));
16251
+ }
16252
+ return segments;
16253
+ }
16254
+ function isCode(segment) {
16255
+ const trimmed = segment.trimStart();
16256
+ return trimmed.startsWith("`") || trimmed.startsWith("~") || trimmed.startsWith("<pre") || trimmed.startsWith("<code");
16257
+ }
16258
+ function rewriteProseSegment(prose, assetsBaseUrl, pathMap) {
16259
+ let result = prose;
16260
+ result = result.replace(
16261
+ /(!\[[^\]]*\]\()([^)"'\s]+)((?:\s+["'][^"']*["'])?\s*\))/g,
16262
+ (full, pre, src, post) => {
16263
+ const trimmed = src.trim();
16264
+ if (!isLocalPath(trimmed)) return full;
16265
+ if (pathMap) {
16266
+ const mapped = pathMap.get(trimmed);
16267
+ if (mapped === void 0) return full;
16268
+ return `${pre}${assetsBaseUrl}/${mapped}${post}`;
16269
+ }
16270
+ return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
16271
+ }
16272
+ );
16273
+ result = result.replace(
16274
+ /(<(?:img|source|video)[^>]+\bsrc=["'])([^"']+)(["'][^>]*>)/gi,
16275
+ (full, pre, src, post) => {
16276
+ const trimmed = src.trim();
16277
+ if (!isLocalPath(trimmed)) return full;
16278
+ if (pathMap) {
16279
+ const mapped = pathMap.get(trimmed);
16280
+ if (mapped === void 0) return full;
16281
+ return `${pre}${assetsBaseUrl}/${mapped}${post}`;
16282
+ }
16283
+ return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
16284
+ }
16285
+ );
16286
+ return result;
16287
+ }
16288
+ function rewriteAssetPaths(markdown, assetsBaseUrl, pathMap) {
16289
+ return splitByCode(markdown).map((seg) => isCode(seg) ? seg : rewriteProseSegment(seg, assetsBaseUrl, pathMap)).join("");
16290
+ }
16291
+ function copyMarkdownAssets(options) {
16292
+ const {
16293
+ markdown,
16294
+ markdownDir,
16295
+ assetsDir,
16296
+ assetsBaseUrl,
16297
+ allowMissing = false
16298
+ } = options;
16299
+ const refs = scanMarkdownAssets(markdown);
16300
+ const pathMap = /* @__PURE__ */ new Map();
16301
+ const missing = [];
16302
+ for (const ref of refs) {
16303
+ const absPath = path4.resolve(markdownDir, ref);
16304
+ if (!fs4.existsSync(absPath)) {
16305
+ if (!allowMissing) {
16306
+ throw new Error(`Asset not found: ${absPath}`);
16307
+ }
16308
+ missing.push(ref);
16309
+ continue;
16310
+ }
16311
+ const relativeCopied = copyAsset(absPath, assetsDir);
16312
+ const fileName = relativeCopied.replace(/^assets\//, "");
16313
+ pathMap.set(ref, fileName);
16314
+ }
16315
+ const rewritten = rewriteAssetPaths(markdown, assetsBaseUrl, pathMap);
16316
+ return {
16317
+ markdown: rewritten,
16318
+ copiedCount: pathMap.size,
16319
+ missingCount: missing.length,
16320
+ missing
16321
+ };
16322
+ }
16323
+
16147
16324
  // src/converters/adapters/jest.ts
16148
16325
  function mapJestStatus(status) {
16149
16326
  switch (status) {
@@ -16911,27 +17088,27 @@ function pickleStepArgumentToDocs(ps) {
16911
17088
  }
16912
17089
 
16913
17090
  // src/utils/git-info.ts
16914
- import * as fs4 from "fs";
16915
- import * as path4 from "path";
17091
+ import * as fs5 from "fs";
17092
+ import * as path5 from "path";
16916
17093
  function readGitSha(cwd = process.cwd()) {
16917
17094
  const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;
16918
17095
  if (envSha) return envSha;
16919
17096
  const gitDir = findGitDir(cwd);
16920
17097
  if (!gitDir) return void 0;
16921
17098
  try {
16922
- const headPath = path4.join(gitDir, "HEAD");
16923
- const head = fs4.readFileSync(headPath, "utf8").trim();
17099
+ const headPath = path5.join(gitDir, "HEAD");
17100
+ const head = fs5.readFileSync(headPath, "utf8").trim();
16924
17101
  if (!head.startsWith("ref:")) {
16925
17102
  return head;
16926
17103
  }
16927
17104
  const refPath = head.replace("ref:", "").trim();
16928
- const refFile = path4.join(gitDir, refPath);
16929
- if (fs4.existsSync(refFile)) {
16930
- return fs4.readFileSync(refFile, "utf8").trim();
17105
+ const refFile = path5.join(gitDir, refPath);
17106
+ if (fs5.existsSync(refFile)) {
17107
+ return fs5.readFileSync(refFile, "utf8").trim();
16931
17108
  }
16932
- const packedRefs = path4.join(gitDir, "packed-refs");
16933
- if (fs4.existsSync(packedRefs)) {
16934
- const content = fs4.readFileSync(packedRefs, "utf8");
17109
+ const packedRefs = path5.join(gitDir, "packed-refs");
17110
+ if (fs5.existsSync(packedRefs)) {
17111
+ const content = fs5.readFileSync(packedRefs, "utf8");
16935
17112
  for (const line of content.split("\n")) {
16936
17113
  if (!line || line.startsWith("#") || line.startsWith("^")) continue;
16937
17114
  const [sha, ref] = line.split(" ");
@@ -16946,19 +17123,19 @@ function readGitSha(cwd = process.cwd()) {
16946
17123
  function findGitDir(start) {
16947
17124
  let current = start;
16948
17125
  while (true) {
16949
- const candidate = path4.join(current, ".git");
16950
- if (fs4.existsSync(candidate)) {
16951
- const stat = fs4.statSync(candidate);
17126
+ const candidate = path5.join(current, ".git");
17127
+ if (fs5.existsSync(candidate)) {
17128
+ const stat = fs5.statSync(candidate);
16952
17129
  if (stat.isFile()) {
16953
- const content = fs4.readFileSync(candidate, "utf8").trim();
17130
+ const content = fs5.readFileSync(candidate, "utf8").trim();
16954
17131
  const match = content.match(/^gitdir: (.+)$/);
16955
17132
  if (match) {
16956
- return path4.resolve(current, match[1]);
17133
+ return path5.resolve(current, match[1]);
16957
17134
  }
16958
17135
  }
16959
17136
  return candidate;
16960
17137
  }
16961
- const parent = path4.dirname(current);
17138
+ const parent = path5.dirname(current);
16962
17139
  if (parent === current) return void 0;
16963
17140
  current = parent;
16964
17141
  }
@@ -16969,8 +17146,8 @@ function readBranchName(cwd = process.cwd()) {
16969
17146
  const gitDir = findGitDir(cwd);
16970
17147
  if (!gitDir) return void 0;
16971
17148
  try {
16972
- const headPath = path4.join(gitDir, "HEAD");
16973
- const head = fs4.readFileSync(headPath, "utf8").trim();
17149
+ const headPath = path5.join(gitDir, "HEAD");
17150
+ const head = fs5.readFileSync(headPath, "utf8").trim();
16974
17151
  if (head.startsWith("ref:")) {
16975
17152
  const refPath = head.replace("ref:", "").trim();
16976
17153
  const match = refPath.match(/^refs\/heads\/(.+)$/);
@@ -17007,8 +17184,8 @@ function nanosecondsToMs(ns) {
17007
17184
  }
17008
17185
 
17009
17186
  // src/utils/metadata.ts
17010
- import * as fs5 from "fs";
17011
- import * as path5 from "path";
17187
+ import * as fs6 from "fs";
17188
+ import * as path6 from "path";
17012
17189
  var versionCache = /* @__PURE__ */ new Map();
17013
17190
  function readPackageVersion(root) {
17014
17191
  if (versionCache.has(root)) {
@@ -17019,18 +17196,18 @@ function readPackageVersion(root) {
17019
17196
  return version;
17020
17197
  }
17021
17198
  function findPackageVersion(startDir) {
17022
- let current = path5.resolve(startDir);
17199
+ let current = path6.resolve(startDir);
17023
17200
  while (true) {
17024
- const pkgPath = path5.join(current, "package.json");
17201
+ const pkgPath = path6.join(current, "package.json");
17025
17202
  try {
17026
- if (fs5.existsSync(pkgPath)) {
17027
- const raw = fs5.readFileSync(pkgPath, "utf8");
17203
+ if (fs6.existsSync(pkgPath)) {
17204
+ const raw = fs6.readFileSync(pkgPath, "utf8");
17028
17205
  const parsed = JSON.parse(raw);
17029
17206
  return parsed.version;
17030
17207
  }
17031
17208
  } catch {
17032
17209
  }
17033
- const parent = path5.dirname(current);
17210
+ const parent = path6.dirname(current);
17034
17211
  if (parent === current) {
17035
17212
  return void 0;
17036
17213
  }
@@ -17955,6 +18132,7 @@ function listScenarios(args, _deps) {
17955
18132
 
17956
18133
  // src/index.ts
17957
18134
  var FORMAT_EXTENSIONS = {
18135
+ astro: ".md",
17958
18136
  markdown: ".md",
17959
18137
  html: ".html",
17960
18138
  "cucumber-html": ".cucumber.html",
@@ -17987,11 +18165,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
17987
18165
  const ext = FORMAT_EXTENSIONS[format];
17988
18166
  const effectiveName = outputName + (outputNameSuffix ?? "");
17989
18167
  if (mode === "aggregated") {
17990
- return toPosix(path6.join(baseOutputDir, `${effectiveName}${ext}`));
18168
+ return toPosix(path7.join(baseOutputDir, `${effectiveName}${ext}`));
17991
18169
  }
17992
18170
  const normalizedSource = toPosix(sourceFile);
17993
- const dirOfSource = path6.posix.dirname(normalizedSource);
17994
- let baseName = path6.posix.basename(normalizedSource);
18171
+ const dirOfSource = path7.posix.dirname(normalizedSource);
18172
+ let baseName = path7.posix.basename(normalizedSource);
17995
18173
  for (const testExt of TEST_EXTENSIONS) {
17996
18174
  if (baseName.endsWith(testExt)) {
17997
18175
  baseName = baseName.slice(0, -testExt.length);
@@ -18000,9 +18178,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
18000
18178
  }
18001
18179
  const fileName = `${baseName}.${effectiveName}${ext}`;
18002
18180
  if (colocatedStyle === "adjacent") {
18003
- return toPosix(path6.posix.join(dirOfSource, fileName));
18181
+ return toPosix(path7.posix.join(dirOfSource, fileName));
18004
18182
  }
18005
- return toPosix(path6.posix.join(baseOutputDir, dirOfSource, fileName));
18183
+ return toPosix(path7.posix.join(baseOutputDir, dirOfSource, fileName));
18006
18184
  }
18007
18185
  function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
18008
18186
  const groups = /* @__PURE__ */ new Map();
@@ -18073,7 +18251,7 @@ var ReportGenerator = class {
18073
18251
  excludeTags: options.excludeTags ?? [],
18074
18252
  formats: options.formats ?? ["cucumber-json"],
18075
18253
  outputDir: options.outputDir ?? "reports",
18076
- outputName: options.outputName ?? "test-results",
18254
+ outputName: options.outputName ?? "index",
18077
18255
  outputNameTimestamp: options.outputNameTimestamp ?? false,
18078
18256
  sortTestCases: options.sortTestCases ?? "none",
18079
18257
  output: {
@@ -18128,6 +18306,24 @@ var ReportGenerator = class {
18128
18306
  includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
18129
18307
  customRenderers: options.markdown?.customRenderers
18130
18308
  },
18309
+ astro: {
18310
+ assetsDir: options.astro?.assetsDir ?? "public/stories/assets",
18311
+ assetsBaseUrl: options.astro?.assetsBaseUrl ?? "/stories/assets",
18312
+ markdown: {
18313
+ title: options.astro?.markdown?.title ?? "User Stories",
18314
+ includeStatusIcons: options.astro?.markdown?.includeStatusIcons ?? true,
18315
+ includeErrors: options.astro?.markdown?.includeErrors ?? true,
18316
+ scenarioHeadingLevel: options.astro?.markdown?.scenarioHeadingLevel ?? 3,
18317
+ groupBy: options.astro?.markdown?.groupBy ?? "file",
18318
+ sortScenarios: options.astro?.markdown?.sortScenarios ?? "source",
18319
+ suiteSeparator: options.astro?.markdown?.suiteSeparator ?? " - ",
18320
+ includeSourceLinks: options.astro?.markdown?.includeSourceLinks ?? true,
18321
+ permalinkBaseUrl: options.astro?.markdown?.permalinkBaseUrl,
18322
+ ticketUrlTemplate: options.astro?.markdown?.ticketUrlTemplate,
18323
+ traceUrlTemplate: options.astro?.markdown?.traceUrlTemplate,
18324
+ customRenderers: options.astro?.markdown?.customRenderers
18325
+ }
18326
+ },
18131
18327
  assetMode: options.assetMode ?? "none",
18132
18328
  allowMissingAssets: options.allowMissingAssets ?? false
18133
18329
  };
@@ -18165,6 +18361,24 @@ var ReportGenerator = class {
18165
18361
  });
18166
18362
  }
18167
18363
  }
18364
+ const astroPaths = results.get("astro");
18365
+ if (astroPaths) {
18366
+ for (const mdPath of astroPaths) {
18367
+ const content = await fsPromises.readFile(mdPath, "utf8");
18368
+ const mdDir = path7.dirname(mdPath);
18369
+ const assetsDir = path7.resolve(this.options.astro.assetsDir);
18370
+ const result = copyMarkdownAssets({
18371
+ markdown: content,
18372
+ markdownDir: mdDir,
18373
+ assetsDir,
18374
+ assetsBaseUrl: this.options.astro.assetsBaseUrl,
18375
+ allowMissing: this.options.allowMissingAssets
18376
+ });
18377
+ if (result.copiedCount > 0 || result.missingCount > 0) {
18378
+ await this.deps.writeFile(mdPath, result.markdown);
18379
+ }
18380
+ }
18381
+ }
18168
18382
  }
18169
18383
  return results;
18170
18384
  }
@@ -18183,9 +18397,9 @@ var ReportGenerator = class {
18183
18397
  if (groups.size === 0 && this.options.output.mode === "aggregated") {
18184
18398
  const ext = FORMAT_EXTENSIONS[format];
18185
18399
  const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
18186
- const outputPath = toPosix(path6.join(this.options.outputDir, `${effectiveName}${ext}`));
18400
+ const outputPath = toPosix(path7.join(this.options.outputDir, `${effectiveName}${ext}`));
18187
18401
  const content = await this.formatContent(run, format);
18188
- const dir = path6.dirname(outputPath);
18402
+ const dir = path7.dirname(outputPath);
18189
18403
  await fsPromises.mkdir(dir, { recursive: true });
18190
18404
  await this.deps.writeFile(outputPath, content);
18191
18405
  return [outputPath];
@@ -18197,7 +18411,7 @@ var ReportGenerator = class {
18197
18411
  testCases
18198
18412
  };
18199
18413
  const content = await this.formatContent(groupRun, format);
18200
- const dir = path6.dirname(outputPath);
18414
+ const dir = path7.dirname(outputPath);
18201
18415
  await fsPromises.mkdir(dir, { recursive: true });
18202
18416
  await this.deps.writeFile(outputPath, content);
18203
18417
  writtenPaths.push(outputPath);
@@ -18260,6 +18474,13 @@ var ReportGenerator = class {
18260
18474
  });
18261
18475
  return formatter.formatToString(run);
18262
18476
  }
18477
+ case "astro": {
18478
+ const formatter = new AstroFormatter({
18479
+ assetsBaseUrl: this.options.astro.assetsBaseUrl,
18480
+ markdown: this.options.astro.markdown
18481
+ });
18482
+ return formatter.format(run);
18483
+ }
18263
18484
  case "markdown": {
18264
18485
  const formatter = new MarkdownFormatter({
18265
18486
  title: this.options.markdown.title,
@@ -18297,7 +18518,7 @@ async function generateRunComparison(args) {
18297
18518
  await fsPromises.mkdir(outputDir, { recursive: true });
18298
18519
  for (const format of args.formats) {
18299
18520
  const ext = format === "html" ? ".html" : ".md";
18300
- const outputPath = toPosix(path6.join(outputDir, `${outputName}${ext}`));
18521
+ const outputPath = toPosix(path7.join(outputDir, `${outputName}${ext}`));
18301
18522
  const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
18302
18523
  await fsPromises.writeFile(outputPath, content, "utf8");
18303
18524
  files.push(outputPath);
@@ -18317,6 +18538,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
18317
18538
  return canonicalizeRun(raw, canonicalizeOptions);
18318
18539
  }
18319
18540
  export {
18541
+ AstroFormatter,
18320
18542
  CucumberHtmlFormatter,
18321
18543
  CucumberJsonFormatter,
18322
18544
  CucumberMessagesFormatter,
@@ -18340,6 +18562,7 @@ export {
18340
18562
  canonicalizeRun,
18341
18563
  clearVersionCache,
18342
18564
  computeTestMetrics,
18565
+ copyMarkdownAssets,
18343
18566
  createPrCommentSummary,
18344
18567
  createReportGenerator,
18345
18568
  deriveStepResults,
@@ -18372,6 +18595,7 @@ export {
18372
18595
  resolveAttachments,
18373
18596
  resolveTheme,
18374
18597
  resolveTraceUrl,
18598
+ rewriteAssetPaths,
18375
18599
  saveHistory,
18376
18600
  sendNotifications,
18377
18601
  sendSlackNotification,