executable-stories-formatters 0.7.8 → 0.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +1010 -78
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +725 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +202 -5
- package/dist/index.d.ts +202 -5
- package/dist/index.js +722 -52
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
AstroFormatter: () => AstroFormatter,
|
|
34
|
+
ConfluenceFormatter: () => ConfluenceFormatter,
|
|
34
35
|
CucumberHtmlFormatter: () => CucumberHtmlFormatter,
|
|
35
36
|
CucumberJsonFormatter: () => CucumberJsonFormatter,
|
|
36
37
|
CucumberMessagesFormatter: () => CucumberMessagesFormatter,
|
|
@@ -62,7 +63,7 @@ __export(src_exports, {
|
|
|
62
63
|
detectPerformanceTrend: () => detectPerformanceTrend,
|
|
63
64
|
diffRuns: () => diffRuns,
|
|
64
65
|
findGitDir: () => findGitDir,
|
|
65
|
-
formatDuration: () =>
|
|
66
|
+
formatDuration: () => formatDuration3,
|
|
66
67
|
generateRunComparison: () => generateRunComparison,
|
|
67
68
|
generateRunId: () => generateRunId,
|
|
68
69
|
generateTestCaseId: () => generateTestCaseId,
|
|
@@ -80,6 +81,8 @@ __export(src_exports, {
|
|
|
80
81
|
normalizeVitestResults: () => normalizeVitestResults,
|
|
81
82
|
parseEnvelopes: () => parseEnvelopes,
|
|
82
83
|
parseNdjson: () => parseNdjson,
|
|
84
|
+
publishConfluencePage: () => publishConfluencePage,
|
|
85
|
+
publishJiraIssue: () => publishJiraIssue,
|
|
83
86
|
readBranchName: () => readBranchName,
|
|
84
87
|
readGitSha: () => readGitSha,
|
|
85
88
|
readPackageVersion: () => readPackageVersion,
|
|
@@ -136,8 +139,8 @@ function generateRunId(startedAtMs, projectRoot) {
|
|
|
136
139
|
const input = `${startedAtMs}::${projectRoot}`;
|
|
137
140
|
return (0, import_node_crypto.createHash)("sha1").update(input).digest("hex").slice(0, 16);
|
|
138
141
|
}
|
|
139
|
-
function slugify(
|
|
140
|
-
return
|
|
142
|
+
function slugify(text2) {
|
|
143
|
+
return text2.toLowerCase().replace(/[/\\]+/g, "-").replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
141
144
|
}
|
|
142
145
|
function generateFeatureId(uri) {
|
|
143
146
|
const pathWithoutExt = uri.replace(/\.[^.]+$/, "");
|
|
@@ -13126,7 +13129,7 @@ function renderDocEntry(entry, deps) {
|
|
|
13126
13129
|
// src/formatters/html/renderers/steps.ts
|
|
13127
13130
|
var CONTINUATION_KEYWORDS = ["And", "But", "*"];
|
|
13128
13131
|
function renderStep(step, stepResult, index, deps) {
|
|
13129
|
-
const
|
|
13132
|
+
const statusIcon2 = stepResult ? deps.getStatusIcon(stepResult.status) : "\u25CB";
|
|
13130
13133
|
const statusClass = stepResult ? `status-${stepResult.status}` : "";
|
|
13131
13134
|
const duration = stepResult && stepResult.durationMs > 0 ? `${stepResult.durationMs}ms` : "";
|
|
13132
13135
|
const keywordTrimmed = step.keyword.trim();
|
|
@@ -13135,7 +13138,7 @@ function renderStep(step, stepResult, index, deps) {
|
|
|
13135
13138
|
const stepDocs = deps.renderDocs(step.docs, "step-docs");
|
|
13136
13139
|
const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
|
|
13137
13140
|
return `<div class="${stepClass}" data-keyword="${deps.escapeHtml(keywordTrimmed)}" data-text="${deps.escapeHtml(step.text)}">
|
|
13138
|
-
<span class="step-status ${statusClass}">${
|
|
13141
|
+
<span class="step-status ${statusClass}">${statusIcon2}</span>
|
|
13139
13142
|
<span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
|
|
13140
13143
|
<span class="step-text">${textHtml}</span>
|
|
13141
13144
|
<span class="step-duration">${duration}</span>
|
|
@@ -13151,10 +13154,10 @@ function renderSteps(args, deps) {
|
|
|
13151
13154
|
|
|
13152
13155
|
// src/formatters/html/renderers/step-params.ts
|
|
13153
13156
|
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.\-])\d+(?:\.\d+)?(?![\w.\-])/g;
|
|
13154
|
-
function highlightStepParams(
|
|
13155
|
-
const matches = Array.from(
|
|
13157
|
+
function highlightStepParams(text2, deps) {
|
|
13158
|
+
const matches = Array.from(text2.matchAll(STEP_PARAM_PATTERN));
|
|
13156
13159
|
if (matches.length === 0) {
|
|
13157
|
-
return deps.escapeHtml(
|
|
13160
|
+
return deps.escapeHtml(text2);
|
|
13158
13161
|
}
|
|
13159
13162
|
let result = "";
|
|
13160
13163
|
let lastIndex = 0;
|
|
@@ -13162,13 +13165,13 @@ function highlightStepParams(text, deps) {
|
|
|
13162
13165
|
const matchStart = match.index;
|
|
13163
13166
|
const matchEnd = matchStart + match[0].length;
|
|
13164
13167
|
if (matchStart > lastIndex) {
|
|
13165
|
-
result += deps.escapeHtml(
|
|
13168
|
+
result += deps.escapeHtml(text2.slice(lastIndex, matchStart));
|
|
13166
13169
|
}
|
|
13167
13170
|
result += `<span class="step-param">${deps.escapeHtml(match[0])}</span>`;
|
|
13168
13171
|
lastIndex = matchEnd;
|
|
13169
13172
|
}
|
|
13170
|
-
if (lastIndex <
|
|
13171
|
-
result += deps.escapeHtml(
|
|
13173
|
+
if (lastIndex < text2.length) {
|
|
13174
|
+
result += deps.escapeHtml(text2.slice(lastIndex));
|
|
13172
13175
|
}
|
|
13173
13176
|
return result;
|
|
13174
13177
|
}
|
|
@@ -13191,7 +13194,7 @@ function renderTicket(ticket, template, escapeHtml3) {
|
|
|
13191
13194
|
}
|
|
13192
13195
|
function renderScenario(args, deps) {
|
|
13193
13196
|
const { tc } = args;
|
|
13194
|
-
const
|
|
13197
|
+
const statusIcon2 = deps.getStatusIcon(tc.status);
|
|
13195
13198
|
const statusClass = `status-${tc.status}`;
|
|
13196
13199
|
const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
|
|
13197
13200
|
const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
|
|
@@ -13260,7 +13263,7 @@ function renderScenario(args, deps) {
|
|
|
13260
13263
|
<div class="scenario-header" role="button" tabindex="0" aria-expanded="${ariaExpanded}">
|
|
13261
13264
|
<div class="scenario-info">
|
|
13262
13265
|
<div class="scenario-title">
|
|
13263
|
-
<span class="status-icon ${statusClass}">${
|
|
13266
|
+
<span class="status-icon ${statusClass}">${statusIcon2}</span>
|
|
13264
13267
|
<span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
|
|
13265
13268
|
</div>
|
|
13266
13269
|
<div class="scenario-meta">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
@@ -13399,11 +13402,11 @@ function buildTooltip(span, escapeHtml3) {
|
|
|
13399
13402
|
parts.push(`${key}=${formatted}`);
|
|
13400
13403
|
}
|
|
13401
13404
|
}
|
|
13402
|
-
let
|
|
13403
|
-
if (
|
|
13404
|
-
|
|
13405
|
+
let text2 = parts.join("\n");
|
|
13406
|
+
if (text2.length > TOOLTIP_MAX_LENGTH) {
|
|
13407
|
+
text2 = text2.slice(0, TOOLTIP_MAX_LENGTH - 3) + "...";
|
|
13405
13408
|
}
|
|
13406
|
-
return escapeHtml3(
|
|
13409
|
+
return escapeHtml3(text2);
|
|
13407
13410
|
}
|
|
13408
13411
|
function renderTraceView(args, deps) {
|
|
13409
13412
|
if (!args.spans || args.spans.length === 0) return "";
|
|
@@ -13626,11 +13629,11 @@ function renderToc(args, deps) {
|
|
|
13626
13629
|
const featureName = suitePaths.length > 0 && suitePaths[0].length > 0 ? suitePaths[0][0] : file.split("/").pop()?.replace(/\.[^.]+$/, "") ?? file;
|
|
13627
13630
|
const featureSlug = `feature-${slugify(file)}`;
|
|
13628
13631
|
const scenarios = testCases.map((tc) => {
|
|
13629
|
-
const
|
|
13632
|
+
const statusIcon2 = deps.getStatusIcon(tc.status);
|
|
13630
13633
|
const statusClass = `status-${tc.status}`;
|
|
13631
13634
|
const failedClass = tc.status === "failed" ? " toc-failed" : "";
|
|
13632
13635
|
return `<a class="toc-scenario${failedClass}" href="#scenario-${tc.id}">
|
|
13633
|
-
<span class="toc-status ${statusClass}">${
|
|
13636
|
+
<span class="toc-status ${statusClass}">${statusIcon2}</span>
|
|
13634
13637
|
${deps.escapeHtml(tc.story.scenario)}
|
|
13635
13638
|
</a>`;
|
|
13636
13639
|
}).join("\n");
|
|
@@ -13688,7 +13691,7 @@ function createHtmlFormatter(options = {}) {
|
|
|
13688
13691
|
escapeHtml,
|
|
13689
13692
|
getStatusIcon,
|
|
13690
13693
|
renderDocs,
|
|
13691
|
-
highlightStepParams: (
|
|
13694
|
+
highlightStepParams: (text2) => highlightStepParams(text2, { escapeHtml })
|
|
13692
13695
|
};
|
|
13693
13696
|
const scenarioDeps = {
|
|
13694
13697
|
escapeHtml,
|
|
@@ -14623,7 +14626,7 @@ function deterministicId(kind, salt, ...parts) {
|
|
|
14623
14626
|
|
|
14624
14627
|
// src/formatters/cucumber-messages/build-gherkin-document.ts
|
|
14625
14628
|
function buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt) {
|
|
14626
|
-
const { lineMap, featureName, featureTags, text } = synthesized;
|
|
14629
|
+
const { lineMap, featureName, featureTags, text: text2 } = synthesized;
|
|
14627
14630
|
const featureTagNodes = featureTags.map((tag, i) => ({
|
|
14628
14631
|
location: {
|
|
14629
14632
|
line: lineMap.featureTagLine ?? 1,
|
|
@@ -14692,7 +14695,7 @@ function buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt) {
|
|
|
14692
14695
|
sourceEnvelope: {
|
|
14693
14696
|
source: {
|
|
14694
14697
|
uri,
|
|
14695
|
-
data:
|
|
14698
|
+
data: text2,
|
|
14696
14699
|
mediaType: "text/x.cucumber.gherkin+plain"
|
|
14697
14700
|
}
|
|
14698
14701
|
},
|
|
@@ -14703,8 +14706,8 @@ function buildStepArguments(step, stepLine) {
|
|
|
14703
14706
|
if (!step.docs || step.docs.length === 0) return {};
|
|
14704
14707
|
const tableDocs = step.docs.filter((d) => d.kind === "table");
|
|
14705
14708
|
if (tableDocs.length > 0) {
|
|
14706
|
-
const
|
|
14707
|
-
return { dataTable: buildDataTable(
|
|
14709
|
+
const table2 = tableDocs[0];
|
|
14710
|
+
return { dataTable: buildDataTable(table2, stepLine + 1) };
|
|
14708
14711
|
}
|
|
14709
14712
|
for (const doc of step.docs) {
|
|
14710
14713
|
const ds = docEntryToDocString(doc, stepLine + 1);
|
|
@@ -14775,21 +14778,21 @@ function docEntryToDocString(doc, line) {
|
|
|
14775
14778
|
return void 0;
|
|
14776
14779
|
}
|
|
14777
14780
|
}
|
|
14778
|
-
function buildDataTable(
|
|
14781
|
+
function buildDataTable(table2, line) {
|
|
14779
14782
|
const rows = [];
|
|
14780
14783
|
rows.push({
|
|
14781
14784
|
location: { line },
|
|
14782
|
-
cells:
|
|
14785
|
+
cells: table2.columns.map((col) => ({
|
|
14783
14786
|
location: { line },
|
|
14784
14787
|
value: col
|
|
14785
14788
|
})),
|
|
14786
14789
|
id: ""
|
|
14787
14790
|
});
|
|
14788
|
-
for (let r = 0; r <
|
|
14791
|
+
for (let r = 0; r < table2.rows.length; r++) {
|
|
14789
14792
|
const rowLine = line + 1 + r;
|
|
14790
14793
|
rows.push({
|
|
14791
14794
|
location: { line: rowLine },
|
|
14792
|
-
cells:
|
|
14795
|
+
cells: table2.rows[r].map((cell) => ({
|
|
14793
14796
|
location: { line: rowLine },
|
|
14794
14797
|
value: cell
|
|
14795
14798
|
})),
|
|
@@ -14880,12 +14883,12 @@ function docEntryToPickleDocString(doc) {
|
|
|
14880
14883
|
return void 0;
|
|
14881
14884
|
}
|
|
14882
14885
|
}
|
|
14883
|
-
function buildPickleTable(
|
|
14886
|
+
function buildPickleTable(table2) {
|
|
14884
14887
|
const rows = [];
|
|
14885
14888
|
rows.push({
|
|
14886
|
-
cells:
|
|
14889
|
+
cells: table2.columns.map((col) => ({ value: col }))
|
|
14887
14890
|
});
|
|
14888
|
-
for (const row of
|
|
14891
|
+
for (const row of table2.rows) {
|
|
14889
14892
|
rows.push({
|
|
14890
14893
|
cells: row.map((cell) => ({ value: cell }))
|
|
14891
14894
|
});
|
|
@@ -16301,6 +16304,414 @@ ${body}`;
|
|
|
16301
16304
|
}
|
|
16302
16305
|
};
|
|
16303
16306
|
|
|
16307
|
+
// src/formatters/confluence.ts
|
|
16308
|
+
var ConfluenceFormatter = class {
|
|
16309
|
+
options;
|
|
16310
|
+
constructor(options = {}) {
|
|
16311
|
+
this.options = {
|
|
16312
|
+
title: options.title ?? "User Stories",
|
|
16313
|
+
includeStatusIcons: options.includeStatusIcons ?? true,
|
|
16314
|
+
includeMetadata: options.includeMetadata ?? true,
|
|
16315
|
+
includeSummaryTable: options.includeSummaryTable ?? true,
|
|
16316
|
+
includeErrors: options.includeErrors ?? true,
|
|
16317
|
+
scenarioHeadingLevel: options.scenarioHeadingLevel ?? 3,
|
|
16318
|
+
groupBy: options.groupBy ?? "file",
|
|
16319
|
+
sortScenarios: options.sortScenarios ?? "source",
|
|
16320
|
+
pretty: options.pretty ?? true,
|
|
16321
|
+
permalinkBaseUrl: options.permalinkBaseUrl,
|
|
16322
|
+
ticketUrlTemplate: options.ticketUrlTemplate
|
|
16323
|
+
};
|
|
16324
|
+
}
|
|
16325
|
+
/** Build the ADF document tree. Returns the JS object (not stringified). */
|
|
16326
|
+
formatToAdf(run) {
|
|
16327
|
+
const content = [];
|
|
16328
|
+
content.push(heading(1, [text(this.options.title)]));
|
|
16329
|
+
if (this.options.includeMetadata) {
|
|
16330
|
+
const metaTable = this.renderMetadataTable(run);
|
|
16331
|
+
if (metaTable) content.push(metaTable);
|
|
16332
|
+
}
|
|
16333
|
+
if (this.options.includeSummaryTable) {
|
|
16334
|
+
content.push(this.renderSummaryTable(run));
|
|
16335
|
+
}
|
|
16336
|
+
switch (this.options.groupBy) {
|
|
16337
|
+
case "none":
|
|
16338
|
+
this.renderFlat(content, run.testCases);
|
|
16339
|
+
break;
|
|
16340
|
+
case "suite":
|
|
16341
|
+
this.renderBySuite(content, run.testCases);
|
|
16342
|
+
break;
|
|
16343
|
+
case "file":
|
|
16344
|
+
default:
|
|
16345
|
+
this.renderByFile(content, run.testCases);
|
|
16346
|
+
break;
|
|
16347
|
+
}
|
|
16348
|
+
return { version: 1, type: "doc", content };
|
|
16349
|
+
}
|
|
16350
|
+
/** Format a test run as an ADF JSON string. */
|
|
16351
|
+
format(run) {
|
|
16352
|
+
const adf = this.formatToAdf(run);
|
|
16353
|
+
return this.options.pretty ? JSON.stringify(adf, null, 2) : JSON.stringify(adf);
|
|
16354
|
+
}
|
|
16355
|
+
// --------------------------------------------------------------------------
|
|
16356
|
+
// Metadata / summary tables
|
|
16357
|
+
// --------------------------------------------------------------------------
|
|
16358
|
+
renderMetadataTable(run) {
|
|
16359
|
+
const rows = [];
|
|
16360
|
+
rows.push(["Date", new Date(run.startedAtMs).toISOString()]);
|
|
16361
|
+
if (run.packageVersion) rows.push(["Version", run.packageVersion]);
|
|
16362
|
+
if (run.gitSha) {
|
|
16363
|
+
const shortSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;
|
|
16364
|
+
rows.push(["Git SHA", shortSha]);
|
|
16365
|
+
}
|
|
16366
|
+
if (rows.length === 0) return null;
|
|
16367
|
+
return table([
|
|
16368
|
+
tableRow([tableHeader("Key"), tableHeader("Value")]),
|
|
16369
|
+
...rows.map(([k, v]) => tableRow([tableCell(k), tableCell(v)]))
|
|
16370
|
+
]);
|
|
16371
|
+
}
|
|
16372
|
+
renderSummaryTable(run) {
|
|
16373
|
+
const total = run.testCases.length;
|
|
16374
|
+
const steps = run.testCases.reduce(
|
|
16375
|
+
(acc, tc) => acc + tc.story.steps.length,
|
|
16376
|
+
0
|
|
16377
|
+
);
|
|
16378
|
+
const passed = run.testCases.filter((tc) => tc.status === "passed").length;
|
|
16379
|
+
const failed = run.testCases.filter((tc) => tc.status === "failed").length;
|
|
16380
|
+
const skipped = run.testCases.filter((tc) => tc.status === "skipped").length;
|
|
16381
|
+
const pending = run.testCases.filter((tc) => tc.status === "pending").length;
|
|
16382
|
+
return table([
|
|
16383
|
+
tableRow([
|
|
16384
|
+
tableHeader("Scenarios"),
|
|
16385
|
+
tableHeader("Steps"),
|
|
16386
|
+
tableHeader("Passed"),
|
|
16387
|
+
tableHeader("Failed"),
|
|
16388
|
+
tableHeader("Skipped"),
|
|
16389
|
+
tableHeader("Pending"),
|
|
16390
|
+
tableHeader("Duration")
|
|
16391
|
+
]),
|
|
16392
|
+
tableRow([
|
|
16393
|
+
tableCell(String(total)),
|
|
16394
|
+
tableCell(String(steps)),
|
|
16395
|
+
tableCell(String(passed)),
|
|
16396
|
+
tableCell(String(failed)),
|
|
16397
|
+
tableCell(String(skipped)),
|
|
16398
|
+
tableCell(String(pending)),
|
|
16399
|
+
tableCell(formatDuration2(run.durationMs))
|
|
16400
|
+
])
|
|
16401
|
+
]);
|
|
16402
|
+
}
|
|
16403
|
+
// --------------------------------------------------------------------------
|
|
16404
|
+
// Grouping
|
|
16405
|
+
// --------------------------------------------------------------------------
|
|
16406
|
+
renderByFile(content, testCases) {
|
|
16407
|
+
const byFile = groupBy7(testCases, (tc) => tc.sourceFile);
|
|
16408
|
+
for (const [file, fileCases] of byFile) {
|
|
16409
|
+
content.push(heading(2, [codeInline(file)]));
|
|
16410
|
+
this.renderSuiteGroups(content, fileCases, 3);
|
|
16411
|
+
}
|
|
16412
|
+
}
|
|
16413
|
+
renderBySuite(content, testCases) {
|
|
16414
|
+
this.renderSuiteGroups(content, testCases, 2);
|
|
16415
|
+
}
|
|
16416
|
+
renderFlat(content, testCases) {
|
|
16417
|
+
const sorted = this.sortCases(testCases);
|
|
16418
|
+
for (const tc of sorted) this.renderScenario(content, tc);
|
|
16419
|
+
}
|
|
16420
|
+
renderSuiteGroups(content, testCases, baseLevel) {
|
|
16421
|
+
const bySuite = groupBy7(testCases, (tc) => tc.titlePath.join(" - "));
|
|
16422
|
+
const entries = this.sortSuiteGroups([...bySuite.entries()]);
|
|
16423
|
+
for (const [suitePath, cases] of entries) {
|
|
16424
|
+
if (suitePath) {
|
|
16425
|
+
content.push(
|
|
16426
|
+
heading(clampHeadingLevel(baseLevel), [text(suitePath)])
|
|
16427
|
+
);
|
|
16428
|
+
}
|
|
16429
|
+
for (const tc of this.sortCases(cases)) {
|
|
16430
|
+
this.renderScenario(content, tc);
|
|
16431
|
+
}
|
|
16432
|
+
}
|
|
16433
|
+
}
|
|
16434
|
+
// --------------------------------------------------------------------------
|
|
16435
|
+
// Scenario
|
|
16436
|
+
// --------------------------------------------------------------------------
|
|
16437
|
+
renderScenario(content, tc) {
|
|
16438
|
+
const level = clampHeadingLevel(this.options.scenarioHeadingLevel);
|
|
16439
|
+
const headingNodes = [];
|
|
16440
|
+
if (this.options.includeStatusIcons) {
|
|
16441
|
+
headingNodes.push(text(`${statusIcon(tc.status)} `));
|
|
16442
|
+
}
|
|
16443
|
+
headingNodes.push(text(tc.story.scenario));
|
|
16444
|
+
content.push(heading(level, headingNodes));
|
|
16445
|
+
const metaChildren = [];
|
|
16446
|
+
if (tc.tags.length > 0) {
|
|
16447
|
+
metaChildren.push(text("Tags: ", strong()));
|
|
16448
|
+
tc.tags.forEach((t, i) => {
|
|
16449
|
+
if (i > 0) metaChildren.push(text(", "));
|
|
16450
|
+
metaChildren.push(codeInline(t));
|
|
16451
|
+
});
|
|
16452
|
+
}
|
|
16453
|
+
if (tc.story.tickets && tc.story.tickets.length > 0) {
|
|
16454
|
+
if (metaChildren.length > 0) metaChildren.push(text(" | "));
|
|
16455
|
+
metaChildren.push(text("Tickets: ", strong()));
|
|
16456
|
+
tc.story.tickets.forEach((ticket, i) => {
|
|
16457
|
+
if (i > 0) metaChildren.push(text(", "));
|
|
16458
|
+
const url = ticket.url ?? (this.options.ticketUrlTemplate ? this.options.ticketUrlTemplate.replace("{ticket}", ticket.id) : void 0);
|
|
16459
|
+
metaChildren.push(
|
|
16460
|
+
url ? link(ticket.id, url) : codeInline(ticket.id)
|
|
16461
|
+
);
|
|
16462
|
+
});
|
|
16463
|
+
}
|
|
16464
|
+
if (this.options.permalinkBaseUrl && tc.sourceFile !== "unknown" && tc.sourceFile) {
|
|
16465
|
+
if (metaChildren.length > 0) metaChildren.push(text(" | "));
|
|
16466
|
+
metaChildren.push(text("Source: ", strong()));
|
|
16467
|
+
const base = this.options.permalinkBaseUrl.replace(/\/$/, "");
|
|
16468
|
+
const url = `${base}/${tc.sourceFile}${tc.sourceLine > 0 ? `#L${tc.sourceLine}` : ""}`;
|
|
16469
|
+
metaChildren.push(link(tc.sourceFile, url));
|
|
16470
|
+
}
|
|
16471
|
+
if (metaChildren.length > 0) {
|
|
16472
|
+
content.push(paragraph(metaChildren));
|
|
16473
|
+
}
|
|
16474
|
+
if (tc.story.docs && tc.story.docs.length > 0) {
|
|
16475
|
+
for (const doc of tc.story.docs) this.renderDocEntry(content, doc);
|
|
16476
|
+
}
|
|
16477
|
+
if (tc.story.steps.length > 0) {
|
|
16478
|
+
content.push(this.renderStepsList(tc.story.steps));
|
|
16479
|
+
for (const step of tc.story.steps) {
|
|
16480
|
+
if (step.docs && step.docs.length > 0) {
|
|
16481
|
+
for (const doc of step.docs) this.renderDocEntry(content, doc);
|
|
16482
|
+
}
|
|
16483
|
+
}
|
|
16484
|
+
}
|
|
16485
|
+
if (tc.status === "failed" && tc.errorMessage && this.options.includeErrors) {
|
|
16486
|
+
const errorContent = (tc.errorMessage ?? "") + (tc.errorStack ? `
|
|
16487
|
+
|
|
16488
|
+
${tc.errorStack}` : "");
|
|
16489
|
+
content.push(
|
|
16490
|
+
panel("warning", [paragraph([text("Failure", strong())])])
|
|
16491
|
+
);
|
|
16492
|
+
content.push(codeBlock(errorContent, "text"));
|
|
16493
|
+
}
|
|
16494
|
+
}
|
|
16495
|
+
renderStepsList(steps) {
|
|
16496
|
+
return {
|
|
16497
|
+
type: "bulletList",
|
|
16498
|
+
content: steps.map((step) => {
|
|
16499
|
+
const children = [text(`${step.keyword} `, strong()), text(step.text)];
|
|
16500
|
+
if (step.mode && step.mode !== "normal") {
|
|
16501
|
+
children.push(text(` (${step.mode})`, em()));
|
|
16502
|
+
}
|
|
16503
|
+
return {
|
|
16504
|
+
type: "listItem",
|
|
16505
|
+
content: [paragraph(children)]
|
|
16506
|
+
};
|
|
16507
|
+
})
|
|
16508
|
+
};
|
|
16509
|
+
}
|
|
16510
|
+
// --------------------------------------------------------------------------
|
|
16511
|
+
// Doc entries
|
|
16512
|
+
// --------------------------------------------------------------------------
|
|
16513
|
+
renderDocEntry(content, entry) {
|
|
16514
|
+
switch (entry.kind) {
|
|
16515
|
+
case "note":
|
|
16516
|
+
content.push(panel("info", [paragraph([text(entry.text)])]));
|
|
16517
|
+
break;
|
|
16518
|
+
case "tag": {
|
|
16519
|
+
const kids = [];
|
|
16520
|
+
entry.names.forEach((name, i) => {
|
|
16521
|
+
if (i > 0) kids.push(text(" "));
|
|
16522
|
+
kids.push(codeInline(name));
|
|
16523
|
+
});
|
|
16524
|
+
if (kids.length > 0) content.push(paragraph(kids));
|
|
16525
|
+
break;
|
|
16526
|
+
}
|
|
16527
|
+
case "kv": {
|
|
16528
|
+
const val = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
16529
|
+
content.push(
|
|
16530
|
+
paragraph([text(`${entry.label}: `, strong()), codeInline(val)])
|
|
16531
|
+
);
|
|
16532
|
+
break;
|
|
16533
|
+
}
|
|
16534
|
+
case "code":
|
|
16535
|
+
if (entry.label) {
|
|
16536
|
+
content.push(paragraph([text(entry.label, strong())]));
|
|
16537
|
+
}
|
|
16538
|
+
content.push(codeBlock(entry.content ?? "", entry.lang));
|
|
16539
|
+
break;
|
|
16540
|
+
case "table":
|
|
16541
|
+
if (entry.label) {
|
|
16542
|
+
content.push(paragraph([text(entry.label, strong())]));
|
|
16543
|
+
}
|
|
16544
|
+
content.push(
|
|
16545
|
+
table([
|
|
16546
|
+
tableRow(entry.columns.map((c) => tableHeader(c))),
|
|
16547
|
+
...entry.rows.map(
|
|
16548
|
+
(row) => tableRow(row.map((cell) => tableCell(cell)))
|
|
16549
|
+
)
|
|
16550
|
+
])
|
|
16551
|
+
);
|
|
16552
|
+
break;
|
|
16553
|
+
case "link":
|
|
16554
|
+
content.push(paragraph([link(entry.label, entry.url)]));
|
|
16555
|
+
break;
|
|
16556
|
+
case "section":
|
|
16557
|
+
if (entry.title) {
|
|
16558
|
+
content.push(paragraph([text(entry.title, strong())]));
|
|
16559
|
+
}
|
|
16560
|
+
if (entry.markdown) {
|
|
16561
|
+
for (const para of entry.markdown.split(/\n{2,}/)) {
|
|
16562
|
+
const trimmed = para.trim();
|
|
16563
|
+
if (trimmed) content.push(paragraph([text(trimmed)]));
|
|
16564
|
+
}
|
|
16565
|
+
}
|
|
16566
|
+
break;
|
|
16567
|
+
case "mermaid":
|
|
16568
|
+
if (entry.title) {
|
|
16569
|
+
content.push(paragraph([text(entry.title, strong())]));
|
|
16570
|
+
}
|
|
16571
|
+
content.push(codeBlock(entry.code ?? "", "mermaid"));
|
|
16572
|
+
break;
|
|
16573
|
+
case "screenshot":
|
|
16574
|
+
content.push(
|
|
16575
|
+
paragraph([
|
|
16576
|
+
text(entry.alt ?? "Screenshot", strong()),
|
|
16577
|
+
text(": "),
|
|
16578
|
+
link(entry.path, entry.path)
|
|
16579
|
+
])
|
|
16580
|
+
);
|
|
16581
|
+
break;
|
|
16582
|
+
case "custom":
|
|
16583
|
+
content.push(paragraph([text(`[${entry.type}]`, strong())]));
|
|
16584
|
+
content.push(codeBlock(JSON.stringify(entry.data ?? null, null, 2), "json"));
|
|
16585
|
+
break;
|
|
16586
|
+
}
|
|
16587
|
+
if (entry.children && entry.children.length > 0) {
|
|
16588
|
+
for (const child of entry.children) {
|
|
16589
|
+
this.renderDocEntry(content, child);
|
|
16590
|
+
}
|
|
16591
|
+
}
|
|
16592
|
+
}
|
|
16593
|
+
// --------------------------------------------------------------------------
|
|
16594
|
+
// Sorting
|
|
16595
|
+
// --------------------------------------------------------------------------
|
|
16596
|
+
sortCases(cases) {
|
|
16597
|
+
if (this.options.sortScenarios === "alpha") {
|
|
16598
|
+
return [...cases].sort(
|
|
16599
|
+
(a, b) => a.story.scenario.localeCompare(b.story.scenario)
|
|
16600
|
+
);
|
|
16601
|
+
}
|
|
16602
|
+
if (this.options.sortScenarios === "source") {
|
|
16603
|
+
return [...cases].sort(
|
|
16604
|
+
(a, b) => (a.story.sourceOrder ?? 0) - (b.story.sourceOrder ?? 0)
|
|
16605
|
+
);
|
|
16606
|
+
}
|
|
16607
|
+
return cases;
|
|
16608
|
+
}
|
|
16609
|
+
sortSuiteGroups(entries) {
|
|
16610
|
+
if (this.options.sortScenarios === "alpha") {
|
|
16611
|
+
return entries.sort(([a], [b]) => a.localeCompare(b));
|
|
16612
|
+
}
|
|
16613
|
+
if (this.options.sortScenarios === "source") {
|
|
16614
|
+
return entries.sort(([, a], [, b]) => {
|
|
16615
|
+
const minA = Math.min(...a.map((s) => s.story.sourceOrder ?? Infinity));
|
|
16616
|
+
const minB = Math.min(...b.map((s) => s.story.sourceOrder ?? Infinity));
|
|
16617
|
+
return minA - minB;
|
|
16618
|
+
});
|
|
16619
|
+
}
|
|
16620
|
+
return entries;
|
|
16621
|
+
}
|
|
16622
|
+
};
|
|
16623
|
+
function text(value, mark) {
|
|
16624
|
+
const node = { type: "text", text: value };
|
|
16625
|
+
if (mark) {
|
|
16626
|
+
node.marks = Array.isArray(mark) ? mark : [mark];
|
|
16627
|
+
}
|
|
16628
|
+
return node;
|
|
16629
|
+
}
|
|
16630
|
+
function strong() {
|
|
16631
|
+
return { type: "strong" };
|
|
16632
|
+
}
|
|
16633
|
+
function em() {
|
|
16634
|
+
return { type: "em" };
|
|
16635
|
+
}
|
|
16636
|
+
function codeMark() {
|
|
16637
|
+
return { type: "code" };
|
|
16638
|
+
}
|
|
16639
|
+
function codeInline(value) {
|
|
16640
|
+
return text(value, codeMark());
|
|
16641
|
+
}
|
|
16642
|
+
function link(label, href) {
|
|
16643
|
+
return text(label, { type: "link", attrs: { href } });
|
|
16644
|
+
}
|
|
16645
|
+
function paragraph(content) {
|
|
16646
|
+
return { type: "paragraph", content };
|
|
16647
|
+
}
|
|
16648
|
+
function heading(level, content) {
|
|
16649
|
+
return {
|
|
16650
|
+
type: "heading",
|
|
16651
|
+
attrs: { level: clampHeadingLevel(level) },
|
|
16652
|
+
content
|
|
16653
|
+
};
|
|
16654
|
+
}
|
|
16655
|
+
function codeBlock(content, lang) {
|
|
16656
|
+
return {
|
|
16657
|
+
type: "codeBlock",
|
|
16658
|
+
attrs: lang ? { language: lang } : {},
|
|
16659
|
+
content: content ? [{ type: "text", text: content }] : []
|
|
16660
|
+
};
|
|
16661
|
+
}
|
|
16662
|
+
function panel(panelType, content) {
|
|
16663
|
+
return { type: "panel", attrs: { panelType }, content };
|
|
16664
|
+
}
|
|
16665
|
+
function table(rows) {
|
|
16666
|
+
return {
|
|
16667
|
+
type: "table",
|
|
16668
|
+
attrs: { isNumberColumnEnabled: false, layout: "default" },
|
|
16669
|
+
content: rows
|
|
16670
|
+
};
|
|
16671
|
+
}
|
|
16672
|
+
function tableRow(cells) {
|
|
16673
|
+
return { type: "tableRow", content: cells };
|
|
16674
|
+
}
|
|
16675
|
+
function tableHeader(value) {
|
|
16676
|
+
return { type: "tableHeader", content: [paragraph([text(value)])] };
|
|
16677
|
+
}
|
|
16678
|
+
function tableCell(value) {
|
|
16679
|
+
return { type: "tableCell", content: [paragraph([text(value)])] };
|
|
16680
|
+
}
|
|
16681
|
+
function clampHeadingLevel(level) {
|
|
16682
|
+
if (level < 1) return 1;
|
|
16683
|
+
if (level > 6) return 6;
|
|
16684
|
+
return level;
|
|
16685
|
+
}
|
|
16686
|
+
function statusIcon(status) {
|
|
16687
|
+
switch (status) {
|
|
16688
|
+
case "passed":
|
|
16689
|
+
return "\u2705";
|
|
16690
|
+
case "failed":
|
|
16691
|
+
return "\u274C";
|
|
16692
|
+
case "skipped":
|
|
16693
|
+
return "\u23E9";
|
|
16694
|
+
case "pending":
|
|
16695
|
+
return "\u{1F4DD}";
|
|
16696
|
+
default:
|
|
16697
|
+
return "\u26A0\uFE0F";
|
|
16698
|
+
}
|
|
16699
|
+
}
|
|
16700
|
+
function formatDuration2(ms) {
|
|
16701
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
16702
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
16703
|
+
}
|
|
16704
|
+
function groupBy7(items, keyFn) {
|
|
16705
|
+
const map = /* @__PURE__ */ new Map();
|
|
16706
|
+
for (const item of items) {
|
|
16707
|
+
const key = keyFn(item);
|
|
16708
|
+
const existing = map.get(key);
|
|
16709
|
+
if (existing) existing.push(item);
|
|
16710
|
+
else map.set(key, [item]);
|
|
16711
|
+
}
|
|
16712
|
+
return map;
|
|
16713
|
+
}
|
|
16714
|
+
|
|
16304
16715
|
// src/formatters/astro-assets.ts
|
|
16305
16716
|
var fs4 = __toESM(require("fs"), 1);
|
|
16306
16717
|
var path4 = __toESM(require("path"), 1);
|
|
@@ -16826,6 +17237,235 @@ ${result.errors.join("\n")}`);
|
|
|
16826
17237
|
}
|
|
16827
17238
|
}
|
|
16828
17239
|
|
|
17240
|
+
// src/publishers/confluence.ts
|
|
17241
|
+
function parseAdf(adf) {
|
|
17242
|
+
let parsed;
|
|
17243
|
+
try {
|
|
17244
|
+
parsed = JSON.parse(adf);
|
|
17245
|
+
} catch (err) {
|
|
17246
|
+
throw new Error(
|
|
17247
|
+
`ADF payload is not valid JSON: ${err.message}`
|
|
17248
|
+
);
|
|
17249
|
+
}
|
|
17250
|
+
if (!parsed || typeof parsed !== "object" || parsed.type !== "doc" || !Array.isArray(parsed.content)) {
|
|
17251
|
+
throw new Error(
|
|
17252
|
+
`ADF payload must be an object with { version, type: "doc", content: [...] }`
|
|
17253
|
+
);
|
|
17254
|
+
}
|
|
17255
|
+
return parsed;
|
|
17256
|
+
}
|
|
17257
|
+
function basicAuthHeader(auth) {
|
|
17258
|
+
const raw = `${auth.email}:${auth.token}`;
|
|
17259
|
+
const encoded = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(raw);
|
|
17260
|
+
return `Basic ${encoded}`;
|
|
17261
|
+
}
|
|
17262
|
+
async function parseErrorBody(response) {
|
|
17263
|
+
try {
|
|
17264
|
+
const body = await response.text();
|
|
17265
|
+
return body ? body.slice(0, 800) : "";
|
|
17266
|
+
} catch {
|
|
17267
|
+
return "";
|
|
17268
|
+
}
|
|
17269
|
+
}
|
|
17270
|
+
async function publishConfluencePage(args, deps) {
|
|
17271
|
+
parseAdf(args.adf);
|
|
17272
|
+
if (!args.pageId && !args.spaceId) {
|
|
17273
|
+
throw new Error(
|
|
17274
|
+
"publishConfluencePage requires either pageId (update) or spaceId (create)"
|
|
17275
|
+
);
|
|
17276
|
+
}
|
|
17277
|
+
if (!args.pageId && !args.title) {
|
|
17278
|
+
throw new Error("Creating a new page requires a title");
|
|
17279
|
+
}
|
|
17280
|
+
const base = args.baseUrl.replace(/\/$/, "");
|
|
17281
|
+
const fetchFn = deps.fetch ?? globalThis.fetch;
|
|
17282
|
+
if (!fetchFn) {
|
|
17283
|
+
throw new Error("No fetch implementation available (Node >= 22 expected)");
|
|
17284
|
+
}
|
|
17285
|
+
const headers = {
|
|
17286
|
+
Authorization: basicAuthHeader(deps.auth),
|
|
17287
|
+
Accept: "application/json",
|
|
17288
|
+
"Content-Type": "application/json"
|
|
17289
|
+
};
|
|
17290
|
+
if (args.pageId) {
|
|
17291
|
+
return updatePage(args, base, headers, fetchFn);
|
|
17292
|
+
}
|
|
17293
|
+
return createPage(args, base, headers, fetchFn);
|
|
17294
|
+
}
|
|
17295
|
+
async function updatePage(args, base, headers, fetchFn) {
|
|
17296
|
+
const getUrl = `${base}/api/v2/pages/${encodeURIComponent(args.pageId)}`;
|
|
17297
|
+
const getResp = await fetchFn(getUrl, { method: "GET", headers });
|
|
17298
|
+
if (!getResp.ok) {
|
|
17299
|
+
const body = await parseErrorBody(getResp);
|
|
17300
|
+
throw new Error(
|
|
17301
|
+
`GET ${getUrl} failed with ${getResp.status} ${getResp.statusText}${body ? `: ${body}` : ""}`
|
|
17302
|
+
);
|
|
17303
|
+
}
|
|
17304
|
+
const current = await getResp.json();
|
|
17305
|
+
const nextVersion = current.version.number + 1;
|
|
17306
|
+
const title = args.title ?? current.title;
|
|
17307
|
+
const putUrl = `${base}/api/v2/pages/${encodeURIComponent(args.pageId)}`;
|
|
17308
|
+
const putResp = await fetchFn(putUrl, {
|
|
17309
|
+
method: "PUT",
|
|
17310
|
+
headers,
|
|
17311
|
+
body: JSON.stringify({
|
|
17312
|
+
id: args.pageId,
|
|
17313
|
+
status: "current",
|
|
17314
|
+
title,
|
|
17315
|
+
body: {
|
|
17316
|
+
representation: "atlas_doc_format",
|
|
17317
|
+
value: args.adf
|
|
17318
|
+
},
|
|
17319
|
+
version: { number: nextVersion }
|
|
17320
|
+
})
|
|
17321
|
+
});
|
|
17322
|
+
if (!putResp.ok) {
|
|
17323
|
+
const body = await parseErrorBody(putResp);
|
|
17324
|
+
throw new Error(
|
|
17325
|
+
`PUT ${putUrl} failed with ${putResp.status} ${putResp.statusText}${body ? `: ${body}` : ""}`
|
|
17326
|
+
);
|
|
17327
|
+
}
|
|
17328
|
+
const updated = await putResp.json();
|
|
17329
|
+
return {
|
|
17330
|
+
id: updated.id,
|
|
17331
|
+
title: updated.title,
|
|
17332
|
+
version: updated.version.number,
|
|
17333
|
+
url: buildPageUrl(base, updated._links?.webui, updated.id),
|
|
17334
|
+
action: "updated"
|
|
17335
|
+
};
|
|
17336
|
+
}
|
|
17337
|
+
async function createPage(args, base, headers, fetchFn) {
|
|
17338
|
+
const body = {
|
|
17339
|
+
spaceId: args.spaceId,
|
|
17340
|
+
status: "current",
|
|
17341
|
+
title: args.title,
|
|
17342
|
+
body: {
|
|
17343
|
+
representation: "atlas_doc_format",
|
|
17344
|
+
value: args.adf
|
|
17345
|
+
}
|
|
17346
|
+
};
|
|
17347
|
+
if (args.parentId) body.parentId = args.parentId;
|
|
17348
|
+
const postUrl = `${base}/api/v2/pages`;
|
|
17349
|
+
const resp = await fetchFn(postUrl, {
|
|
17350
|
+
method: "POST",
|
|
17351
|
+
headers,
|
|
17352
|
+
body: JSON.stringify(body)
|
|
17353
|
+
});
|
|
17354
|
+
if (!resp.ok) {
|
|
17355
|
+
const errBody = await parseErrorBody(resp);
|
|
17356
|
+
throw new Error(
|
|
17357
|
+
`POST ${postUrl} failed with ${resp.status} ${resp.statusText}${errBody ? `: ${errBody}` : ""}`
|
|
17358
|
+
);
|
|
17359
|
+
}
|
|
17360
|
+
const created = await resp.json();
|
|
17361
|
+
return {
|
|
17362
|
+
id: created.id,
|
|
17363
|
+
title: created.title,
|
|
17364
|
+
version: created.version.number,
|
|
17365
|
+
url: buildPageUrl(base, created._links?.webui, created.id),
|
|
17366
|
+
action: "created"
|
|
17367
|
+
};
|
|
17368
|
+
}
|
|
17369
|
+
function buildPageUrl(base, webui, id) {
|
|
17370
|
+
if (webui) {
|
|
17371
|
+
return webui.startsWith("http") ? webui : `${base}${webui}`;
|
|
17372
|
+
}
|
|
17373
|
+
return `${base}/pages/${id}`;
|
|
17374
|
+
}
|
|
17375
|
+
|
|
17376
|
+
// src/publishers/jira.ts
|
|
17377
|
+
function parseAdf2(adf) {
|
|
17378
|
+
let parsed;
|
|
17379
|
+
try {
|
|
17380
|
+
parsed = JSON.parse(adf);
|
|
17381
|
+
} catch (err) {
|
|
17382
|
+
throw new Error(
|
|
17383
|
+
`ADF payload is not valid JSON: ${err.message}`
|
|
17384
|
+
);
|
|
17385
|
+
}
|
|
17386
|
+
if (!parsed || typeof parsed !== "object" || parsed.type !== "doc" || !Array.isArray(parsed.content)) {
|
|
17387
|
+
throw new Error(
|
|
17388
|
+
`ADF payload must be an object with { version, type: "doc", content: [...] }`
|
|
17389
|
+
);
|
|
17390
|
+
}
|
|
17391
|
+
return parsed;
|
|
17392
|
+
}
|
|
17393
|
+
function basicAuthHeader2(auth) {
|
|
17394
|
+
const raw = `${auth.email}:${auth.token}`;
|
|
17395
|
+
const encoded = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(raw);
|
|
17396
|
+
return `Basic ${encoded}`;
|
|
17397
|
+
}
|
|
17398
|
+
async function parseErrorBody2(resp) {
|
|
17399
|
+
try {
|
|
17400
|
+
const body = await resp.text();
|
|
17401
|
+
return body ? body.slice(0, 800) : "";
|
|
17402
|
+
} catch {
|
|
17403
|
+
return "";
|
|
17404
|
+
}
|
|
17405
|
+
}
|
|
17406
|
+
async function publishJiraIssue(args, deps) {
|
|
17407
|
+
const adfObject = parseAdf2(args.adf);
|
|
17408
|
+
if (!args.issueKey) {
|
|
17409
|
+
throw new Error("publishJiraIssue requires an issueKey, e.g. PROJ-123");
|
|
17410
|
+
}
|
|
17411
|
+
const base = args.baseUrl.replace(/\/$/, "");
|
|
17412
|
+
const fetchFn = deps.fetch ?? globalThis.fetch;
|
|
17413
|
+
if (!fetchFn) {
|
|
17414
|
+
throw new Error("No fetch implementation available (Node >= 22 expected)");
|
|
17415
|
+
}
|
|
17416
|
+
const headers = {
|
|
17417
|
+
Authorization: basicAuthHeader2(deps.auth),
|
|
17418
|
+
Accept: "application/json",
|
|
17419
|
+
"Content-Type": "application/json"
|
|
17420
|
+
};
|
|
17421
|
+
const mode = args.mode ?? "comment";
|
|
17422
|
+
if (mode === "description") {
|
|
17423
|
+
return updateDescription(args.issueKey, base, adfObject, headers, fetchFn);
|
|
17424
|
+
}
|
|
17425
|
+
return addComment(args.issueKey, base, adfObject, headers, fetchFn);
|
|
17426
|
+
}
|
|
17427
|
+
async function addComment(issueKey, base, adf, headers, fetchFn) {
|
|
17428
|
+
const url = `${base}/rest/api/3/issue/${encodeURIComponent(issueKey)}/comment`;
|
|
17429
|
+
const resp = await fetchFn(url, {
|
|
17430
|
+
method: "POST",
|
|
17431
|
+
headers,
|
|
17432
|
+
body: JSON.stringify({ body: adf })
|
|
17433
|
+
});
|
|
17434
|
+
if (!resp.ok) {
|
|
17435
|
+
const body = await parseErrorBody2(resp);
|
|
17436
|
+
throw new Error(
|
|
17437
|
+
`POST ${url} failed with ${resp.status} ${resp.statusText}${body ? `: ${body}` : ""}`
|
|
17438
|
+
);
|
|
17439
|
+
}
|
|
17440
|
+
const comment = await resp.json();
|
|
17441
|
+
const issueUrl = `${base}/browse/${encodeURIComponent(issueKey)}`;
|
|
17442
|
+
return {
|
|
17443
|
+
issueKey,
|
|
17444
|
+
action: "comment-added",
|
|
17445
|
+
url: `${issueUrl}?focusedCommentId=${encodeURIComponent(comment.id)}`,
|
|
17446
|
+
commentId: comment.id
|
|
17447
|
+
};
|
|
17448
|
+
}
|
|
17449
|
+
async function updateDescription(issueKey, base, adf, headers, fetchFn) {
|
|
17450
|
+
const url = `${base}/rest/api/3/issue/${encodeURIComponent(issueKey)}`;
|
|
17451
|
+
const resp = await fetchFn(url, {
|
|
17452
|
+
method: "PUT",
|
|
17453
|
+
headers,
|
|
17454
|
+
body: JSON.stringify({ fields: { description: adf } })
|
|
17455
|
+
});
|
|
17456
|
+
if (!resp.ok) {
|
|
17457
|
+
const body = await parseErrorBody2(resp);
|
|
17458
|
+
throw new Error(
|
|
17459
|
+
`PUT ${url} failed with ${resp.status} ${resp.statusText}${body ? `: ${body}` : ""}`
|
|
17460
|
+
);
|
|
17461
|
+
}
|
|
17462
|
+
return {
|
|
17463
|
+
issueKey,
|
|
17464
|
+
action: "description-updated",
|
|
17465
|
+
url: `${base}/browse/${encodeURIComponent(issueKey)}`
|
|
17466
|
+
};
|
|
17467
|
+
}
|
|
17468
|
+
|
|
16829
17469
|
// src/converters/ndjson-parser.ts
|
|
16830
17470
|
function parseNdjson(ndjson) {
|
|
16831
17471
|
const lines = ndjson.trim().split("\n").filter(Boolean);
|
|
@@ -17126,10 +17766,10 @@ function pickleStepArgumentToDocs(ps) {
|
|
|
17126
17766
|
const docs = [];
|
|
17127
17767
|
const phase = "static";
|
|
17128
17768
|
if (ps.argument.dataTable) {
|
|
17129
|
-
const
|
|
17130
|
-
if (
|
|
17131
|
-
const columns =
|
|
17132
|
-
const rows =
|
|
17769
|
+
const table2 = ps.argument.dataTable;
|
|
17770
|
+
if (table2.rows.length > 0) {
|
|
17771
|
+
const columns = table2.rows[0].cells.map((c) => c.value);
|
|
17772
|
+
const rows = table2.rows.slice(1).map((r) => r.cells.map((c) => c.value));
|
|
17133
17773
|
docs.push({
|
|
17134
17774
|
kind: "table",
|
|
17135
17775
|
label: "",
|
|
@@ -17264,7 +17904,7 @@ function readBranchName(cwd = process.cwd()) {
|
|
|
17264
17904
|
}
|
|
17265
17905
|
|
|
17266
17906
|
// src/utils/duration.ts
|
|
17267
|
-
function
|
|
17907
|
+
function formatDuration3(ms) {
|
|
17268
17908
|
if (ms < 1e3) {
|
|
17269
17909
|
return `${Math.round(ms)} ms`;
|
|
17270
17910
|
}
|
|
@@ -17452,16 +18092,16 @@ function resolveTraceUrl(template, traceId) {
|
|
|
17452
18092
|
}
|
|
17453
18093
|
|
|
17454
18094
|
// src/notifiers/ansi-strip.ts
|
|
17455
|
-
function stripAnsi(
|
|
17456
|
-
return
|
|
18095
|
+
function stripAnsi(text2) {
|
|
18096
|
+
return text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
17457
18097
|
}
|
|
17458
18098
|
|
|
17459
18099
|
// src/notifiers/slack.ts
|
|
17460
|
-
function truncate(
|
|
17461
|
-
if (
|
|
17462
|
-
return
|
|
18100
|
+
function truncate(text2, maxLen) {
|
|
18101
|
+
if (text2.length <= maxLen) return text2;
|
|
18102
|
+
return text2.slice(0, maxLen - 3) + "...";
|
|
17463
18103
|
}
|
|
17464
|
-
function
|
|
18104
|
+
function formatDuration4(ms) {
|
|
17465
18105
|
const seconds = ms / 1e3;
|
|
17466
18106
|
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
17467
18107
|
const minutes = Math.floor(seconds / 60);
|
|
@@ -17488,7 +18128,7 @@ function buildSlackPayload(summary, maxFailedTests) {
|
|
|
17488
18128
|
{ type: "mrkdwn", text: `*Passed:* ${summary.passed}` },
|
|
17489
18129
|
{ type: "mrkdwn", text: `*Failed:* ${summary.failed}` },
|
|
17490
18130
|
{ type: "mrkdwn", text: `*Skipped:* ${summary.skipped}` },
|
|
17491
|
-
{ type: "mrkdwn", text: `*Duration:* ${
|
|
18131
|
+
{ type: "mrkdwn", text: `*Duration:* ${formatDuration4(summary.durationMs)}` },
|
|
17492
18132
|
{ type: "mrkdwn", text: `*Status:* ${statusText}` }
|
|
17493
18133
|
]
|
|
17494
18134
|
});
|
|
@@ -17503,9 +18143,9 @@ function buildSlackPayload(summary, maxFailedTests) {
|
|
|
17503
18143
|
}
|
|
17504
18144
|
return `*${name}*`;
|
|
17505
18145
|
});
|
|
17506
|
-
let
|
|
18146
|
+
let text2 = lines.join("\n\n");
|
|
17507
18147
|
if (summary.failedTests.length > maxFailedTests) {
|
|
17508
|
-
|
|
18148
|
+
text2 += `
|
|
17509
18149
|
|
|
17510
18150
|
_...and ${summary.failedTests.length - maxFailedTests} more_`;
|
|
17511
18151
|
}
|
|
@@ -17513,7 +18153,7 @@ _...and ${summary.failedTests.length - maxFailedTests} more_`;
|
|
|
17513
18153
|
type: "section",
|
|
17514
18154
|
text: {
|
|
17515
18155
|
type: "mrkdwn",
|
|
17516
|
-
text
|
|
18156
|
+
text: text2
|
|
17517
18157
|
}
|
|
17518
18158
|
});
|
|
17519
18159
|
}
|
|
@@ -17590,11 +18230,11 @@ async function sendSlackNotification(args, deps) {
|
|
|
17590
18230
|
}
|
|
17591
18231
|
|
|
17592
18232
|
// src/notifiers/teams.ts
|
|
17593
|
-
function truncate2(
|
|
17594
|
-
if (
|
|
17595
|
-
return
|
|
18233
|
+
function truncate2(text2, maxLen) {
|
|
18234
|
+
if (text2.length <= maxLen) return text2;
|
|
18235
|
+
return text2.slice(0, maxLen - 3) + "...";
|
|
17596
18236
|
}
|
|
17597
|
-
function
|
|
18237
|
+
function formatDuration5(ms) {
|
|
17598
18238
|
const seconds = ms / 1e3;
|
|
17599
18239
|
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
17600
18240
|
const minutes = Math.floor(seconds / 60);
|
|
@@ -17620,7 +18260,7 @@ function buildTeamsPayload(summary, maxFailedTests) {
|
|
|
17620
18260
|
{ title: "Passed", value: String(summary.passed) },
|
|
17621
18261
|
{ title: "Failed", value: String(summary.failed) },
|
|
17622
18262
|
{ title: "Skipped", value: String(summary.skipped) },
|
|
17623
|
-
{ title: "Duration", value:
|
|
18263
|
+
{ title: "Duration", value: formatDuration5(summary.durationMs) }
|
|
17624
18264
|
]
|
|
17625
18265
|
});
|
|
17626
18266
|
if (summary.failedTests.length > 0) {
|
|
@@ -18274,7 +18914,8 @@ var FORMAT_EXTENSIONS = {
|
|
|
18274
18914
|
"cucumber-html": ".cucumber.html",
|
|
18275
18915
|
junit: ".junit.xml",
|
|
18276
18916
|
"cucumber-json": ".cucumber.json",
|
|
18277
|
-
"cucumber-messages": ".ndjson"
|
|
18917
|
+
"cucumber-messages": ".ndjson",
|
|
18918
|
+
confluence: ".adf.json"
|
|
18278
18919
|
};
|
|
18279
18920
|
var TEST_EXTENSIONS = [
|
|
18280
18921
|
".test.ts",
|
|
@@ -18442,6 +19083,19 @@ var ReportGenerator = class {
|
|
|
18442
19083
|
includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
|
|
18443
19084
|
customRenderers: options.markdown?.customRenderers
|
|
18444
19085
|
},
|
|
19086
|
+
confluence: {
|
|
19087
|
+
title: options.confluence?.title ?? "User Stories",
|
|
19088
|
+
includeStatusIcons: options.confluence?.includeStatusIcons ?? true,
|
|
19089
|
+
includeMetadata: options.confluence?.includeMetadata ?? true,
|
|
19090
|
+
includeSummaryTable: options.confluence?.includeSummaryTable ?? true,
|
|
19091
|
+
includeErrors: options.confluence?.includeErrors ?? true,
|
|
19092
|
+
scenarioHeadingLevel: options.confluence?.scenarioHeadingLevel ?? 3,
|
|
19093
|
+
groupBy: options.confluence?.groupBy ?? "file",
|
|
19094
|
+
sortScenarios: options.confluence?.sortScenarios ?? "source",
|
|
19095
|
+
pretty: options.confluence?.pretty ?? true,
|
|
19096
|
+
permalinkBaseUrl: options.confluence?.permalinkBaseUrl,
|
|
19097
|
+
ticketUrlTemplate: options.confluence?.ticketUrlTemplate
|
|
19098
|
+
},
|
|
18445
19099
|
astro: {
|
|
18446
19100
|
assetsDir: options.astro?.assetsDir ?? "public/stories/assets",
|
|
18447
19101
|
assetsBaseUrl: options.astro?.assetsBaseUrl ?? "/stories/assets",
|
|
@@ -18617,6 +19271,22 @@ var ReportGenerator = class {
|
|
|
18617
19271
|
});
|
|
18618
19272
|
return formatter.format(run);
|
|
18619
19273
|
}
|
|
19274
|
+
case "confluence": {
|
|
19275
|
+
const formatter = new ConfluenceFormatter({
|
|
19276
|
+
title: this.options.confluence.title,
|
|
19277
|
+
includeStatusIcons: this.options.confluence.includeStatusIcons,
|
|
19278
|
+
includeMetadata: this.options.confluence.includeMetadata,
|
|
19279
|
+
includeSummaryTable: this.options.confluence.includeSummaryTable,
|
|
19280
|
+
includeErrors: this.options.confluence.includeErrors,
|
|
19281
|
+
scenarioHeadingLevel: this.options.confluence.scenarioHeadingLevel,
|
|
19282
|
+
groupBy: this.options.confluence.groupBy,
|
|
19283
|
+
sortScenarios: this.options.confluence.sortScenarios,
|
|
19284
|
+
pretty: this.options.confluence.pretty,
|
|
19285
|
+
permalinkBaseUrl: this.options.confluence.permalinkBaseUrl,
|
|
19286
|
+
ticketUrlTemplate: this.options.confluence.ticketUrlTemplate
|
|
19287
|
+
});
|
|
19288
|
+
return formatter.format(run);
|
|
19289
|
+
}
|
|
18620
19290
|
case "markdown": {
|
|
18621
19291
|
const formatter = new MarkdownFormatter({
|
|
18622
19292
|
title: this.options.markdown.title,
|
|
@@ -18676,6 +19346,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
18676
19346
|
// Annotate the CommonJS export names for ESM import in node:
|
|
18677
19347
|
0 && (module.exports = {
|
|
18678
19348
|
AstroFormatter,
|
|
19349
|
+
ConfluenceFormatter,
|
|
18679
19350
|
CucumberHtmlFormatter,
|
|
18680
19351
|
CucumberJsonFormatter,
|
|
18681
19352
|
CucumberMessagesFormatter,
|
|
@@ -18725,6 +19396,8 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
18725
19396
|
normalizeVitestResults,
|
|
18726
19397
|
parseEnvelopes,
|
|
18727
19398
|
parseNdjson,
|
|
19399
|
+
publishConfluencePage,
|
|
19400
|
+
publishJiraIssue,
|
|
18728
19401
|
readBranchName,
|
|
18729
19402
|
readGitSha,
|
|
18730
19403
|
readPackageVersion,
|