executable-stories-formatters 0.7.0 → 0.7.1
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/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/cli.js +307 -98
- package/dist/cli.js.map +1 -1
- package/dist/{index-BndkylIV.d.cts → index-C0OOaaiK.d.cts} +19 -2
- package/dist/{index-BndkylIV.d.ts → index-C0OOaaiK.d.ts} +19 -2
- package/dist/index.cjs +242 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +240 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/raw-run.schema.json +35 -12
- package/skills/formatters-cli/SKILL.md +41 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import "fs";
|
|
3
|
-
import * as
|
|
3
|
+
import * as path6 from "path";
|
|
4
4
|
import * as fsPromises from "fs/promises";
|
|
5
5
|
|
|
6
6
|
// src/converters/acl/status.ts
|
|
@@ -252,6 +252,9 @@ function canonicalizeTestCase(raw, options, projectRoot) {
|
|
|
252
252
|
projectRoot
|
|
253
253
|
});
|
|
254
254
|
const tags = normalizeTags(story);
|
|
255
|
+
if (story.tickets) {
|
|
256
|
+
story.tickets = normalizeTickets(story.tickets);
|
|
257
|
+
}
|
|
255
258
|
const titlePath = buildTitlePath(raw, story);
|
|
256
259
|
return {
|
|
257
260
|
id,
|
|
@@ -275,6 +278,9 @@ function normalizeTags(story) {
|
|
|
275
278
|
const tags = story.tags ?? [];
|
|
276
279
|
return [...new Set(tags)].sort();
|
|
277
280
|
}
|
|
281
|
+
function normalizeTickets(raw) {
|
|
282
|
+
return raw.map((t) => typeof t === "string" ? { id: t } : t);
|
|
283
|
+
}
|
|
278
284
|
function buildTitlePath(raw, story) {
|
|
279
285
|
if (story.suitePath && story.suitePath.length > 0) {
|
|
280
286
|
return story.suitePath;
|
|
@@ -2593,6 +2599,16 @@ body {
|
|
|
2593
2599
|
background: none;
|
|
2594
2600
|
}
|
|
2595
2601
|
|
|
2602
|
+
/* ============================================================================
|
|
2603
|
+
Documentation Entries - Children
|
|
2604
|
+
============================================================================ */
|
|
2605
|
+
.doc-children {
|
|
2606
|
+
margin-left: 1rem;
|
|
2607
|
+
padding-left: 1rem;
|
|
2608
|
+
border-left: 2px solid var(--border);
|
|
2609
|
+
margin-top: 0.25rem;
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2596
2612
|
/* ============================================================================
|
|
2597
2613
|
Trace View - OTel span waterfall
|
|
2598
2614
|
============================================================================ */
|
|
@@ -12307,30 +12323,46 @@ function renderDocCustom(entry, deps) {
|
|
|
12307
12323
|
</div>`;
|
|
12308
12324
|
}
|
|
12309
12325
|
function renderDocEntry(entry, deps) {
|
|
12326
|
+
let html;
|
|
12310
12327
|
switch (entry.kind) {
|
|
12311
12328
|
case "note":
|
|
12312
|
-
|
|
12329
|
+
html = renderDocNote(entry, deps);
|
|
12330
|
+
break;
|
|
12313
12331
|
case "tag":
|
|
12314
|
-
|
|
12332
|
+
html = renderDocTag(entry, deps);
|
|
12333
|
+
break;
|
|
12315
12334
|
case "kv":
|
|
12316
|
-
|
|
12335
|
+
html = renderDocKv(entry, deps);
|
|
12336
|
+
break;
|
|
12317
12337
|
case "code":
|
|
12318
|
-
|
|
12338
|
+
html = renderDocCode(entry, deps);
|
|
12339
|
+
break;
|
|
12319
12340
|
case "table":
|
|
12320
|
-
|
|
12341
|
+
html = renderDocTable(entry, deps);
|
|
12342
|
+
break;
|
|
12321
12343
|
case "link":
|
|
12322
|
-
|
|
12344
|
+
html = renderDocLink(entry, deps);
|
|
12345
|
+
break;
|
|
12323
12346
|
case "section":
|
|
12324
|
-
|
|
12347
|
+
html = renderDocSection(entry, deps);
|
|
12348
|
+
break;
|
|
12325
12349
|
case "mermaid":
|
|
12326
|
-
|
|
12350
|
+
html = renderDocMermaid(entry, deps);
|
|
12351
|
+
break;
|
|
12327
12352
|
case "screenshot":
|
|
12328
|
-
|
|
12353
|
+
html = renderDocScreenshot(entry, deps);
|
|
12354
|
+
break;
|
|
12329
12355
|
case "custom":
|
|
12330
|
-
|
|
12356
|
+
html = renderDocCustom(entry, deps);
|
|
12357
|
+
break;
|
|
12331
12358
|
default:
|
|
12332
|
-
|
|
12359
|
+
html = "";
|
|
12360
|
+
}
|
|
12361
|
+
if (entry.children && entry.children.length > 0) {
|
|
12362
|
+
const childrenHtml = entry.children.map((child) => renderDocEntry(child, deps)).join("");
|
|
12363
|
+
html += `<div class="doc-children">${childrenHtml}</div>`;
|
|
12333
12364
|
}
|
|
12365
|
+
return html;
|
|
12334
12366
|
}
|
|
12335
12367
|
|
|
12336
12368
|
// src/formatters/html/renderers/steps.ts
|
|
@@ -12392,12 +12424,20 @@ function hasSufficientHistory(entries, min) {
|
|
|
12392
12424
|
}
|
|
12393
12425
|
|
|
12394
12426
|
// src/formatters/html/renderers/scenario.ts
|
|
12427
|
+
function renderTicket(ticket, template, escapeHtml3) {
|
|
12428
|
+
const url = ticket.url ?? (template ? template.replace("{ticket}", ticket.id) : void 0);
|
|
12429
|
+
if (url) {
|
|
12430
|
+
return `<a class="tag ticket-tag" href="${escapeHtml3(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml3(ticket.id)}</a>`;
|
|
12431
|
+
}
|
|
12432
|
+
return `<span class="tag ticket-tag">${escapeHtml3(ticket.id)}</span>`;
|
|
12433
|
+
}
|
|
12395
12434
|
function renderScenario(args, deps) {
|
|
12396
12435
|
const { tc } = args;
|
|
12397
12436
|
const statusIcon = deps.getStatusIcon(tc.status);
|
|
12398
12437
|
const statusClass = `status-${tc.status}`;
|
|
12399
12438
|
const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
|
|
12400
12439
|
const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
|
|
12440
|
+
const tickets = (tc.story.tickets ?? []).map((t) => renderTicket(t, deps.ticketUrlTemplate, deps.escapeHtml)).join("");
|
|
12401
12441
|
const otelMeta = tc.story.meta?.otel;
|
|
12402
12442
|
let traceBadge = "";
|
|
12403
12443
|
if (otelMeta?.traceId) {
|
|
@@ -12465,7 +12505,7 @@ function renderScenario(args, deps) {
|
|
|
12465
12505
|
<span class="status-icon ${statusClass}">${statusIcon}</span>
|
|
12466
12506
|
<span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
|
|
12467
12507
|
</div>
|
|
12468
|
-
<div class="scenario-meta">${tags}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
12508
|
+
<div class="scenario-meta">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
12469
12509
|
</div>
|
|
12470
12510
|
<span class="scenario-duration">${duration}</span>
|
|
12471
12511
|
</div>
|
|
@@ -12810,6 +12850,7 @@ function normalizeOptions(options = {}) {
|
|
|
12810
12850
|
mermaidEnabled: options.mermaidEnabled ?? true,
|
|
12811
12851
|
markdownEnabled: options.markdownEnabled ?? true,
|
|
12812
12852
|
permalinkBaseUrl: options.permalinkBaseUrl,
|
|
12853
|
+
ticketUrlTemplate: options.ticketUrlTemplate,
|
|
12813
12854
|
theme: options.theme ?? "default"
|
|
12814
12855
|
};
|
|
12815
12856
|
}
|
|
@@ -12842,7 +12883,8 @@ function createHtmlFormatter(options = {}) {
|
|
|
12842
12883
|
renderAttachments: (args, d) => renderAttachments(args, d),
|
|
12843
12884
|
renderTraceView: (args, d) => renderTraceView(args, d),
|
|
12844
12885
|
embedScreenshots: opts.embedScreenshots,
|
|
12845
|
-
permalinkBaseUrl: opts.permalinkBaseUrl
|
|
12886
|
+
permalinkBaseUrl: opts.permalinkBaseUrl,
|
|
12887
|
+
ticketUrlTemplate: opts.ticketUrlTemplate
|
|
12846
12888
|
};
|
|
12847
12889
|
const featureDeps = {
|
|
12848
12890
|
escapeHtml,
|
|
@@ -13355,14 +13397,16 @@ var MarkdownFormatter = class {
|
|
|
13355
13397
|
}
|
|
13356
13398
|
if (tc.story.tickets && tc.story.tickets.length > 0) {
|
|
13357
13399
|
const ticketTemplate = this.options.ticketUrlTemplate;
|
|
13358
|
-
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13400
|
+
const ticketLinks = tc.story.tickets.map((t) => {
|
|
13401
|
+
if (t.url) {
|
|
13402
|
+
return `[${t.id}](${t.url})`;
|
|
13403
|
+
}
|
|
13404
|
+
if (ticketTemplate) {
|
|
13405
|
+
return `[${t.id}](${ticketTemplate.replace("{ticket}", t.id)})`;
|
|
13406
|
+
}
|
|
13407
|
+
return `\`${t.id}\``;
|
|
13408
|
+
});
|
|
13409
|
+
meta.push(`Tickets: ${ticketLinks.join(", ")}`);
|
|
13366
13410
|
}
|
|
13367
13411
|
const otelMeta = tc.story.meta?.otel;
|
|
13368
13412
|
if (otelMeta?.traceId) {
|
|
@@ -13536,6 +13580,12 @@ var MarkdownFormatter = class {
|
|
|
13536
13580
|
lines.push(`${indent}`);
|
|
13537
13581
|
break;
|
|
13538
13582
|
}
|
|
13583
|
+
if (entry.children && entry.children.length > 0) {
|
|
13584
|
+
const childIndent = indent + " ";
|
|
13585
|
+
for (const child of entry.children) {
|
|
13586
|
+
this.renderDocEntry(lines, child, childIndent);
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13539
13589
|
}
|
|
13540
13590
|
/**
|
|
13541
13591
|
* Get status icon for a status.
|
|
@@ -13608,8 +13658,8 @@ function extractFeatureName(testCases, uri) {
|
|
|
13608
13658
|
return tc.titlePath[0];
|
|
13609
13659
|
}
|
|
13610
13660
|
}
|
|
13611
|
-
const
|
|
13612
|
-
return
|
|
13661
|
+
const basename3 = uri.replace(/^.*[\\/]/, "").replace(/\.[^.]+$/, "");
|
|
13662
|
+
return basename3.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
13613
13663
|
}
|
|
13614
13664
|
function synthesizeFeature(uri, testCases) {
|
|
13615
13665
|
const featureName = extractFeatureName(testCases, uri);
|
|
@@ -14221,8 +14271,8 @@ function extractDocAttachments(step) {
|
|
|
14221
14271
|
}
|
|
14222
14272
|
return attachments;
|
|
14223
14273
|
}
|
|
14224
|
-
function guessMediaType(
|
|
14225
|
-
const lower =
|
|
14274
|
+
function guessMediaType(path7) {
|
|
14275
|
+
const lower = path7.toLowerCase();
|
|
14226
14276
|
if (lower.endsWith(".png")) return "image/png";
|
|
14227
14277
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
14228
14278
|
if (lower.endsWith(".gif")) return "image/gif";
|
|
@@ -14363,11 +14413,11 @@ var CucumberHtmlFormatter = class {
|
|
|
14363
14413
|
for (const envelope of envelopes) {
|
|
14364
14414
|
const accepted = htmlStream.write(envelope);
|
|
14365
14415
|
if (!accepted) {
|
|
14366
|
-
await new Promise((
|
|
14416
|
+
await new Promise((resolve5) => htmlStream.once("drain", resolve5));
|
|
14367
14417
|
}
|
|
14368
14418
|
}
|
|
14369
|
-
await new Promise((
|
|
14370
|
-
collector.on("finish",
|
|
14419
|
+
await new Promise((resolve5, reject) => {
|
|
14420
|
+
collector.on("finish", resolve5);
|
|
14371
14421
|
collector.on("error", reject);
|
|
14372
14422
|
htmlStream.end();
|
|
14373
14423
|
});
|
|
@@ -14485,7 +14535,7 @@ function buildFlags(baseline, current) {
|
|
|
14485
14535
|
steps: stableJson(baseline.story.steps) !== stableJson(current.story.steps),
|
|
14486
14536
|
docs: stableJson(baselineDocs) !== stableJson(currentDocs),
|
|
14487
14537
|
tags: !compareStringArrays(baseline.tags, current.tags),
|
|
14488
|
-
tickets:
|
|
14538
|
+
tickets: stableJson(baseline.story.tickets ?? []) !== stableJson(current.story.tickets ?? []),
|
|
14489
14539
|
source: baseline.sourceFile !== current.sourceFile || baseline.sourceLine !== current.sourceLine,
|
|
14490
14540
|
duration: baseline.durationMs !== current.durationMs,
|
|
14491
14541
|
attachments: stableJson(baseline.attachments) !== stableJson(current.attachments),
|
|
@@ -14708,7 +14758,7 @@ function renderScenarioCard(scenario) {
|
|
|
14708
14758
|
<dd>${escapeHtml2(before.errorMessage ?? "") || " "}</dd>
|
|
14709
14759
|
${scenario.flags.steps ? `<dt>Steps</dt><dd>${formatSteps(before.steps)}</dd>` : ""}
|
|
14710
14760
|
${scenario.flags.docs ? `<dt>Docs</dt><dd>${formatDocs(before.docs)}</dd>` : ""}
|
|
14711
|
-
${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml2(before.tickets.join(", ")) || " "}</dd>` : ""}
|
|
14761
|
+
${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml2(before.tickets.map((t) => t.id).join(", ")) || " "}</dd>` : ""}
|
|
14712
14762
|
</dl>
|
|
14713
14763
|
</section>
|
|
14714
14764
|
<section>
|
|
@@ -14722,7 +14772,7 @@ function renderScenarioCard(scenario) {
|
|
|
14722
14772
|
<dd>${escapeHtml2(after.errorMessage ?? "") || " "}</dd>
|
|
14723
14773
|
${scenario.flags.steps ? `<dt>Steps</dt><dd>${formatSteps(after.steps)}</dd>` : ""}
|
|
14724
14774
|
${scenario.flags.docs ? `<dt>Docs</dt><dd>${formatDocs(after.docs)}</dd>` : ""}
|
|
14725
|
-
${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml2(after.tickets.join(", ")) || " "}</dd>` : ""}
|
|
14775
|
+
${scenario.flags.tickets ? `<dt>Tickets</dt><dd>${escapeHtml2(after.tickets.map((t) => t.id).join(", ")) || " "}</dd>` : ""}
|
|
14726
14776
|
</dl>
|
|
14727
14777
|
</section>
|
|
14728
14778
|
</div>` : (() => {
|
|
@@ -14736,7 +14786,7 @@ function renderScenarioCard(scenario) {
|
|
|
14736
14786
|
return `<div class="snapshot-detail">
|
|
14737
14787
|
<dl>
|
|
14738
14788
|
${hasTags ? `<dt>Tags</dt><dd>${escapeHtml2(snapshot.tags.join(", "))}</dd>` : ""}
|
|
14739
|
-
${hasTickets ? `<dt>Tickets</dt><dd>${escapeHtml2(snapshot.tickets.join(", "))}</dd>` : ""}
|
|
14789
|
+
${hasTickets ? `<dt>Tickets</dt><dd>${escapeHtml2(snapshot.tickets.map((t) => t.id).join(", "))}</dd>` : ""}
|
|
14740
14790
|
${hasSteps ? `<dt>Steps</dt><dd>${formatSteps(snapshot.steps)}</dd>` : ""}
|
|
14741
14791
|
${hasDocs ? `<dt>Docs</dt><dd>${formatDocs(snapshot.docs)}</dd>` : ""}
|
|
14742
14792
|
</dl>
|
|
@@ -15051,7 +15101,7 @@ function renderScenario2(lines, scenario) {
|
|
|
15051
15101
|
lines.push(`| Docs | ${escapeCell(formatDocs2(before.docs))} | ${escapeCell(formatDocs2(after.docs))} |`);
|
|
15052
15102
|
}
|
|
15053
15103
|
if (scenario.flags.tickets) {
|
|
15054
|
-
lines.push(`| Tickets | ${escapeCell(before.tickets.join(", "))} | ${escapeCell(after.tickets.join(", "))} |`);
|
|
15104
|
+
lines.push(`| Tickets | ${escapeCell(before.tickets.map((t) => t.id).join(", "))} | ${escapeCell(after.tickets.map((t) => t.id).join(", "))} |`);
|
|
15055
15105
|
}
|
|
15056
15106
|
lines.push("");
|
|
15057
15107
|
} else {
|
|
@@ -15067,7 +15117,7 @@ function renderSnapshotDetail(lines, snapshot) {
|
|
|
15067
15117
|
lines.push("");
|
|
15068
15118
|
}
|
|
15069
15119
|
if (snapshot.tickets.length > 0) {
|
|
15070
|
-
lines.push(`**Tickets:** ${snapshot.tickets.join(", ")}`);
|
|
15120
|
+
lines.push(`**Tickets:** ${snapshot.tickets.map((t) => t.id).join(", ")}`);
|
|
15071
15121
|
lines.push("");
|
|
15072
15122
|
}
|
|
15073
15123
|
if (snapshot.steps.length > 0) {
|
|
@@ -15265,6 +15315,110 @@ function selectTestCases(args, deps) {
|
|
|
15265
15315
|
return sortTestCases(selected, sortMode);
|
|
15266
15316
|
}
|
|
15267
15317
|
|
|
15318
|
+
// src/bundler/bundle-assets.ts
|
|
15319
|
+
import * as fs3 from "fs";
|
|
15320
|
+
import * as path3 from "path";
|
|
15321
|
+
|
|
15322
|
+
// src/bundler/scan-html-assets.ts
|
|
15323
|
+
function scanHtmlAssets(html) {
|
|
15324
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15325
|
+
const patterns = [
|
|
15326
|
+
/<(?:img|video)\b[^>]*?\bsrc=["']([^"']+)["']/g,
|
|
15327
|
+
/<a\b[^>]*?\bclass=["']attachment["'][^>]*?\bhref=["']([^"']+)["']/g,
|
|
15328
|
+
/<a\b[^>]*?\bhref=["']([^"']+)["'][^>]*?\bclass=["']attachment["']/g
|
|
15329
|
+
];
|
|
15330
|
+
for (const pattern of patterns) {
|
|
15331
|
+
let match;
|
|
15332
|
+
while ((match = pattern.exec(html)) !== null) {
|
|
15333
|
+
const ref = match[1];
|
|
15334
|
+
if (isLocalAssetRef(ref) && !seen.has(ref)) {
|
|
15335
|
+
seen.add(ref);
|
|
15336
|
+
}
|
|
15337
|
+
}
|
|
15338
|
+
}
|
|
15339
|
+
return [...seen];
|
|
15340
|
+
}
|
|
15341
|
+
function isLocalAssetRef(ref) {
|
|
15342
|
+
if (!ref) return false;
|
|
15343
|
+
if (ref.startsWith("data:")) return false;
|
|
15344
|
+
if (ref.startsWith("http://") || ref.startsWith("https://")) return false;
|
|
15345
|
+
if (ref.startsWith("#")) return false;
|
|
15346
|
+
return true;
|
|
15347
|
+
}
|
|
15348
|
+
|
|
15349
|
+
// src/bundler/copy-asset.ts
|
|
15350
|
+
import * as fs2 from "fs";
|
|
15351
|
+
import * as path2 from "path";
|
|
15352
|
+
import * as crypto from "crypto";
|
|
15353
|
+
function copyAsset(sourcePath, assetsDir) {
|
|
15354
|
+
if (!fs2.existsSync(assetsDir)) {
|
|
15355
|
+
fs2.mkdirSync(assetsDir, { recursive: true });
|
|
15356
|
+
}
|
|
15357
|
+
const content = fs2.readFileSync(sourcePath);
|
|
15358
|
+
const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
15359
|
+
const ext = path2.extname(sourcePath);
|
|
15360
|
+
const baseName = sanitize(path2.basename(sourcePath, ext));
|
|
15361
|
+
const destName = `${baseName}-${hash}${ext}`;
|
|
15362
|
+
const destPath = path2.join(assetsDir, destName);
|
|
15363
|
+
if (!fs2.existsSync(destPath)) {
|
|
15364
|
+
fs2.copyFileSync(sourcePath, destPath);
|
|
15365
|
+
}
|
|
15366
|
+
return `assets/${destName}`;
|
|
15367
|
+
}
|
|
15368
|
+
function sanitize(name) {
|
|
15369
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
15370
|
+
}
|
|
15371
|
+
|
|
15372
|
+
// src/bundler/bundle-assets.ts
|
|
15373
|
+
function bundleAssets(htmlPath, options = {}) {
|
|
15374
|
+
const htmlDir = path3.dirname(htmlPath);
|
|
15375
|
+
const assetsDir = path3.join(htmlDir, "assets");
|
|
15376
|
+
let html = fs3.readFileSync(htmlPath, "utf8");
|
|
15377
|
+
const refs = scanHtmlAssets(html);
|
|
15378
|
+
let copiedCount = 0;
|
|
15379
|
+
const missing = [];
|
|
15380
|
+
for (const ref of refs) {
|
|
15381
|
+
const absolutePath = path3.resolve(htmlDir, ref);
|
|
15382
|
+
if (!fs3.existsSync(absolutePath)) {
|
|
15383
|
+
missing.push(ref);
|
|
15384
|
+
continue;
|
|
15385
|
+
}
|
|
15386
|
+
const newRelPath = copyAsset(absolutePath, assetsDir);
|
|
15387
|
+
html = replaceAssetRef(html, ref, newRelPath);
|
|
15388
|
+
copiedCount++;
|
|
15389
|
+
}
|
|
15390
|
+
if (missing.length > 0 && !options.allowMissing) {
|
|
15391
|
+
throw new Error(
|
|
15392
|
+
`Missing asset${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
|
|
15393
|
+
);
|
|
15394
|
+
}
|
|
15395
|
+
fs3.writeFileSync(htmlPath, html, "utf8");
|
|
15396
|
+
return {
|
|
15397
|
+
copiedCount,
|
|
15398
|
+
missingCount: missing.length,
|
|
15399
|
+
missing
|
|
15400
|
+
};
|
|
15401
|
+
}
|
|
15402
|
+
function replaceAssetRef(html, original, replacement) {
|
|
15403
|
+
const escaped = original.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
15404
|
+
const srcPattern = new RegExp(
|
|
15405
|
+
`(<(?:img|video)\\b[^>]*?\\bsrc=["'])${escaped}(["'])`,
|
|
15406
|
+
"g"
|
|
15407
|
+
);
|
|
15408
|
+
html = html.replace(srcPattern, `$1${replacement}$2`);
|
|
15409
|
+
const hrefClassFirst = new RegExp(
|
|
15410
|
+
`(<a\\b[^>]*?\\bclass=["']attachment["'][^>]*?\\bhref=["'])${escaped}(["'])`,
|
|
15411
|
+
"g"
|
|
15412
|
+
);
|
|
15413
|
+
html = html.replace(hrefClassFirst, `$1${replacement}$2`);
|
|
15414
|
+
const hrefHrefFirst = new RegExp(
|
|
15415
|
+
`(<a\\b[^>]*?\\bhref=["'])${escaped}(["'][^>]*?\\bclass=["']attachment["'])`,
|
|
15416
|
+
"g"
|
|
15417
|
+
);
|
|
15418
|
+
html = html.replace(hrefHrefFirst, `$1${replacement}$2`);
|
|
15419
|
+
return html;
|
|
15420
|
+
}
|
|
15421
|
+
|
|
15268
15422
|
// src/converters/adapters/jest.ts
|
|
15269
15423
|
function mapJestStatus(status) {
|
|
15270
15424
|
switch (status) {
|
|
@@ -16032,27 +16186,27 @@ function pickleStepArgumentToDocs(ps) {
|
|
|
16032
16186
|
}
|
|
16033
16187
|
|
|
16034
16188
|
// src/utils/git-info.ts
|
|
16035
|
-
import * as
|
|
16036
|
-
import * as
|
|
16189
|
+
import * as fs4 from "fs";
|
|
16190
|
+
import * as path4 from "path";
|
|
16037
16191
|
function readGitSha(cwd = process.cwd()) {
|
|
16038
16192
|
const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;
|
|
16039
16193
|
if (envSha) return envSha;
|
|
16040
16194
|
const gitDir = findGitDir(cwd);
|
|
16041
16195
|
if (!gitDir) return void 0;
|
|
16042
16196
|
try {
|
|
16043
|
-
const headPath =
|
|
16044
|
-
const head =
|
|
16197
|
+
const headPath = path4.join(gitDir, "HEAD");
|
|
16198
|
+
const head = fs4.readFileSync(headPath, "utf8").trim();
|
|
16045
16199
|
if (!head.startsWith("ref:")) {
|
|
16046
16200
|
return head;
|
|
16047
16201
|
}
|
|
16048
16202
|
const refPath = head.replace("ref:", "").trim();
|
|
16049
|
-
const refFile =
|
|
16050
|
-
if (
|
|
16051
|
-
return
|
|
16203
|
+
const refFile = path4.join(gitDir, refPath);
|
|
16204
|
+
if (fs4.existsSync(refFile)) {
|
|
16205
|
+
return fs4.readFileSync(refFile, "utf8").trim();
|
|
16052
16206
|
}
|
|
16053
|
-
const packedRefs =
|
|
16054
|
-
if (
|
|
16055
|
-
const content =
|
|
16207
|
+
const packedRefs = path4.join(gitDir, "packed-refs");
|
|
16208
|
+
if (fs4.existsSync(packedRefs)) {
|
|
16209
|
+
const content = fs4.readFileSync(packedRefs, "utf8");
|
|
16056
16210
|
for (const line of content.split("\n")) {
|
|
16057
16211
|
if (!line || line.startsWith("#") || line.startsWith("^")) continue;
|
|
16058
16212
|
const [sha, ref] = line.split(" ");
|
|
@@ -16067,19 +16221,19 @@ function readGitSha(cwd = process.cwd()) {
|
|
|
16067
16221
|
function findGitDir(start) {
|
|
16068
16222
|
let current = start;
|
|
16069
16223
|
while (true) {
|
|
16070
|
-
const candidate =
|
|
16071
|
-
if (
|
|
16072
|
-
const stat =
|
|
16224
|
+
const candidate = path4.join(current, ".git");
|
|
16225
|
+
if (fs4.existsSync(candidate)) {
|
|
16226
|
+
const stat = fs4.statSync(candidate);
|
|
16073
16227
|
if (stat.isFile()) {
|
|
16074
|
-
const content =
|
|
16228
|
+
const content = fs4.readFileSync(candidate, "utf8").trim();
|
|
16075
16229
|
const match = content.match(/^gitdir: (.+)$/);
|
|
16076
16230
|
if (match) {
|
|
16077
|
-
return
|
|
16231
|
+
return path4.resolve(current, match[1]);
|
|
16078
16232
|
}
|
|
16079
16233
|
}
|
|
16080
16234
|
return candidate;
|
|
16081
16235
|
}
|
|
16082
|
-
const parent =
|
|
16236
|
+
const parent = path4.dirname(current);
|
|
16083
16237
|
if (parent === current) return void 0;
|
|
16084
16238
|
current = parent;
|
|
16085
16239
|
}
|
|
@@ -16090,8 +16244,8 @@ function readBranchName(cwd = process.cwd()) {
|
|
|
16090
16244
|
const gitDir = findGitDir(cwd);
|
|
16091
16245
|
if (!gitDir) return void 0;
|
|
16092
16246
|
try {
|
|
16093
|
-
const headPath =
|
|
16094
|
-
const head =
|
|
16247
|
+
const headPath = path4.join(gitDir, "HEAD");
|
|
16248
|
+
const head = fs4.readFileSync(headPath, "utf8").trim();
|
|
16095
16249
|
if (head.startsWith("ref:")) {
|
|
16096
16250
|
const refPath = head.replace("ref:", "").trim();
|
|
16097
16251
|
const match = refPath.match(/^refs\/heads\/(.+)$/);
|
|
@@ -16128,8 +16282,8 @@ function nanosecondsToMs(ns) {
|
|
|
16128
16282
|
}
|
|
16129
16283
|
|
|
16130
16284
|
// src/utils/metadata.ts
|
|
16131
|
-
import * as
|
|
16132
|
-
import * as
|
|
16285
|
+
import * as fs5 from "fs";
|
|
16286
|
+
import * as path5 from "path";
|
|
16133
16287
|
var versionCache = /* @__PURE__ */ new Map();
|
|
16134
16288
|
function readPackageVersion(root) {
|
|
16135
16289
|
if (versionCache.has(root)) {
|
|
@@ -16140,18 +16294,18 @@ function readPackageVersion(root) {
|
|
|
16140
16294
|
return version;
|
|
16141
16295
|
}
|
|
16142
16296
|
function findPackageVersion(startDir) {
|
|
16143
|
-
let current =
|
|
16297
|
+
let current = path5.resolve(startDir);
|
|
16144
16298
|
while (true) {
|
|
16145
|
-
const pkgPath =
|
|
16299
|
+
const pkgPath = path5.join(current, "package.json");
|
|
16146
16300
|
try {
|
|
16147
|
-
if (
|
|
16148
|
-
const raw =
|
|
16301
|
+
if (fs5.existsSync(pkgPath)) {
|
|
16302
|
+
const raw = fs5.readFileSync(pkgPath, "utf8");
|
|
16149
16303
|
const parsed = JSON.parse(raw);
|
|
16150
16304
|
return parsed.version;
|
|
16151
16305
|
}
|
|
16152
16306
|
} catch {
|
|
16153
16307
|
}
|
|
16154
|
-
const parent =
|
|
16308
|
+
const parent = path5.dirname(current);
|
|
16155
16309
|
if (parent === current) {
|
|
16156
16310
|
return void 0;
|
|
16157
16311
|
}
|
|
@@ -17108,11 +17262,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
17108
17262
|
const ext = FORMAT_EXTENSIONS[format];
|
|
17109
17263
|
const effectiveName = outputName + (outputNameSuffix ?? "");
|
|
17110
17264
|
if (mode === "aggregated") {
|
|
17111
|
-
return toPosix(
|
|
17265
|
+
return toPosix(path6.join(baseOutputDir, `${effectiveName}${ext}`));
|
|
17112
17266
|
}
|
|
17113
17267
|
const normalizedSource = toPosix(sourceFile);
|
|
17114
|
-
const dirOfSource =
|
|
17115
|
-
let baseName =
|
|
17268
|
+
const dirOfSource = path6.posix.dirname(normalizedSource);
|
|
17269
|
+
let baseName = path6.posix.basename(normalizedSource);
|
|
17116
17270
|
for (const testExt of TEST_EXTENSIONS) {
|
|
17117
17271
|
if (baseName.endsWith(testExt)) {
|
|
17118
17272
|
baseName = baseName.slice(0, -testExt.length);
|
|
@@ -17121,9 +17275,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
17121
17275
|
}
|
|
17122
17276
|
const fileName = `${baseName}.${effectiveName}${ext}`;
|
|
17123
17277
|
if (colocatedStyle === "adjacent") {
|
|
17124
|
-
return toPosix(
|
|
17278
|
+
return toPosix(path6.posix.join(dirOfSource, fileName));
|
|
17125
17279
|
}
|
|
17126
|
-
return toPosix(
|
|
17280
|
+
return toPosix(path6.posix.join(baseOutputDir, dirOfSource, fileName));
|
|
17127
17281
|
}
|
|
17128
17282
|
function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
|
|
17129
17283
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -17222,6 +17376,7 @@ var ReportGenerator = class {
|
|
|
17222
17376
|
mermaidEnabled: options.html?.mermaidEnabled ?? true,
|
|
17223
17377
|
markdownEnabled: options.html?.markdownEnabled ?? true,
|
|
17224
17378
|
permalinkBaseUrl: options.html?.permalinkBaseUrl,
|
|
17379
|
+
ticketUrlTemplate: options.html?.ticketUrlTemplate,
|
|
17225
17380
|
theme: options.html?.theme ?? "default"
|
|
17226
17381
|
},
|
|
17227
17382
|
junit: {
|
|
@@ -17245,7 +17400,9 @@ var ReportGenerator = class {
|
|
|
17245
17400
|
traceUrlTemplate: options.markdown?.traceUrlTemplate,
|
|
17246
17401
|
includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
|
|
17247
17402
|
customRenderers: options.markdown?.customRenderers
|
|
17248
|
-
}
|
|
17403
|
+
},
|
|
17404
|
+
assetMode: options.assetMode ?? "none",
|
|
17405
|
+
allowMissingAssets: options.allowMissingAssets ?? false
|
|
17249
17406
|
};
|
|
17250
17407
|
}
|
|
17251
17408
|
/**
|
|
@@ -17272,6 +17429,16 @@ var ReportGenerator = class {
|
|
|
17272
17429
|
const paths = await this.generateFormat(filteredRun, format);
|
|
17273
17430
|
results.set(format, paths);
|
|
17274
17431
|
}
|
|
17432
|
+
if (this.options.assetMode === "copy") {
|
|
17433
|
+
const htmlPaths = results.get("html");
|
|
17434
|
+
if (htmlPaths) {
|
|
17435
|
+
for (const htmlPath of htmlPaths) {
|
|
17436
|
+
bundleAssets(htmlPath, {
|
|
17437
|
+
allowMissing: this.options.allowMissingAssets
|
|
17438
|
+
});
|
|
17439
|
+
}
|
|
17440
|
+
}
|
|
17441
|
+
}
|
|
17275
17442
|
return results;
|
|
17276
17443
|
}
|
|
17277
17444
|
/**
|
|
@@ -17289,9 +17456,9 @@ var ReportGenerator = class {
|
|
|
17289
17456
|
if (groups.size === 0 && this.options.output.mode === "aggregated") {
|
|
17290
17457
|
const ext = FORMAT_EXTENSIONS[format];
|
|
17291
17458
|
const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
|
|
17292
|
-
const outputPath = toPosix(
|
|
17459
|
+
const outputPath = toPosix(path6.join(this.options.outputDir, `${effectiveName}${ext}`));
|
|
17293
17460
|
const content = await this.formatContent(run, format);
|
|
17294
|
-
const dir =
|
|
17461
|
+
const dir = path6.dirname(outputPath);
|
|
17295
17462
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
17296
17463
|
await this.deps.writeFile(outputPath, content);
|
|
17297
17464
|
return [outputPath];
|
|
@@ -17303,7 +17470,7 @@ var ReportGenerator = class {
|
|
|
17303
17470
|
testCases
|
|
17304
17471
|
};
|
|
17305
17472
|
const content = await this.formatContent(groupRun, format);
|
|
17306
|
-
const dir =
|
|
17473
|
+
const dir = path6.dirname(outputPath);
|
|
17307
17474
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
17308
17475
|
await this.deps.writeFile(outputPath, content);
|
|
17309
17476
|
writtenPaths.push(outputPath);
|
|
@@ -17332,7 +17499,8 @@ var ReportGenerator = class {
|
|
|
17332
17499
|
syntaxHighlighting: this.options.html.syntaxHighlighting,
|
|
17333
17500
|
mermaidEnabled: this.options.html.mermaidEnabled,
|
|
17334
17501
|
markdownEnabled: this.options.html.markdownEnabled,
|
|
17335
|
-
permalinkBaseUrl: this.options.html.permalinkBaseUrl
|
|
17502
|
+
permalinkBaseUrl: this.options.html.permalinkBaseUrl,
|
|
17503
|
+
ticketUrlTemplate: this.options.html.ticketUrlTemplate
|
|
17336
17504
|
});
|
|
17337
17505
|
return formatter.format(run);
|
|
17338
17506
|
}
|
|
@@ -17400,7 +17568,7 @@ async function generateRunComparison(args) {
|
|
|
17400
17568
|
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
17401
17569
|
for (const format of args.formats) {
|
|
17402
17570
|
const ext = format === "html" ? ".html" : ".md";
|
|
17403
|
-
const outputPath = toPosix(
|
|
17571
|
+
const outputPath = toPosix(path6.join(outputDir, `${outputName}${ext}`));
|
|
17404
17572
|
const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
|
|
17405
17573
|
await fsPromises.writeFile(outputPath, content, "utf8");
|
|
17406
17574
|
files.push(outputPath);
|
|
@@ -17437,6 +17605,7 @@ export {
|
|
|
17437
17605
|
adaptPlaywrightRun,
|
|
17438
17606
|
adaptVitestRun,
|
|
17439
17607
|
assertValidRun,
|
|
17608
|
+
bundleAssets,
|
|
17440
17609
|
calculateFlakiness,
|
|
17441
17610
|
calculateStability,
|
|
17442
17611
|
canonicalizeRun,
|