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.js
CHANGED
|
@@ -32,8 +32,8 @@ function generateRunId(startedAtMs, projectRoot) {
|
|
|
32
32
|
const input = `${startedAtMs}::${projectRoot}`;
|
|
33
33
|
return createHash("sha1").update(input).digest("hex").slice(0, 16);
|
|
34
34
|
}
|
|
35
|
-
function slugify(
|
|
36
|
-
return
|
|
35
|
+
function slugify(text2) {
|
|
36
|
+
return text2.toLowerCase().replace(/[/\\]+/g, "-").replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
37
37
|
}
|
|
38
38
|
function generateFeatureId(uri) {
|
|
39
39
|
const pathWithoutExt = uri.replace(/\.[^.]+$/, "");
|
|
@@ -13022,7 +13022,7 @@ function renderDocEntry(entry, deps) {
|
|
|
13022
13022
|
// src/formatters/html/renderers/steps.ts
|
|
13023
13023
|
var CONTINUATION_KEYWORDS = ["And", "But", "*"];
|
|
13024
13024
|
function renderStep(step, stepResult, index, deps) {
|
|
13025
|
-
const
|
|
13025
|
+
const statusIcon2 = stepResult ? deps.getStatusIcon(stepResult.status) : "\u25CB";
|
|
13026
13026
|
const statusClass = stepResult ? `status-${stepResult.status}` : "";
|
|
13027
13027
|
const duration = stepResult && stepResult.durationMs > 0 ? `${stepResult.durationMs}ms` : "";
|
|
13028
13028
|
const keywordTrimmed = step.keyword.trim();
|
|
@@ -13031,7 +13031,7 @@ function renderStep(step, stepResult, index, deps) {
|
|
|
13031
13031
|
const stepDocs = deps.renderDocs(step.docs, "step-docs");
|
|
13032
13032
|
const textHtml = deps.highlightStepParams ? deps.highlightStepParams(step.text) : deps.escapeHtml(step.text);
|
|
13033
13033
|
return `<div class="${stepClass}" data-keyword="${deps.escapeHtml(keywordTrimmed)}" data-text="${deps.escapeHtml(step.text)}">
|
|
13034
|
-
<span class="step-status ${statusClass}">${
|
|
13034
|
+
<span class="step-status ${statusClass}">${statusIcon2}</span>
|
|
13035
13035
|
<span class="step-keyword">${deps.escapeHtml(step.keyword)}</span>
|
|
13036
13036
|
<span class="step-text">${textHtml}</span>
|
|
13037
13037
|
<span class="step-duration">${duration}</span>
|
|
@@ -13047,10 +13047,10 @@ function renderSteps(args, deps) {
|
|
|
13047
13047
|
|
|
13048
13048
|
// src/formatters/html/renderers/step-params.ts
|
|
13049
13049
|
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.\-])\d+(?:\.\d+)?(?![\w.\-])/g;
|
|
13050
|
-
function highlightStepParams(
|
|
13051
|
-
const matches = Array.from(
|
|
13050
|
+
function highlightStepParams(text2, deps) {
|
|
13051
|
+
const matches = Array.from(text2.matchAll(STEP_PARAM_PATTERN));
|
|
13052
13052
|
if (matches.length === 0) {
|
|
13053
|
-
return deps.escapeHtml(
|
|
13053
|
+
return deps.escapeHtml(text2);
|
|
13054
13054
|
}
|
|
13055
13055
|
let result = "";
|
|
13056
13056
|
let lastIndex = 0;
|
|
@@ -13058,13 +13058,13 @@ function highlightStepParams(text, deps) {
|
|
|
13058
13058
|
const matchStart = match.index;
|
|
13059
13059
|
const matchEnd = matchStart + match[0].length;
|
|
13060
13060
|
if (matchStart > lastIndex) {
|
|
13061
|
-
result += deps.escapeHtml(
|
|
13061
|
+
result += deps.escapeHtml(text2.slice(lastIndex, matchStart));
|
|
13062
13062
|
}
|
|
13063
13063
|
result += `<span class="step-param">${deps.escapeHtml(match[0])}</span>`;
|
|
13064
13064
|
lastIndex = matchEnd;
|
|
13065
13065
|
}
|
|
13066
|
-
if (lastIndex <
|
|
13067
|
-
result += deps.escapeHtml(
|
|
13066
|
+
if (lastIndex < text2.length) {
|
|
13067
|
+
result += deps.escapeHtml(text2.slice(lastIndex));
|
|
13068
13068
|
}
|
|
13069
13069
|
return result;
|
|
13070
13070
|
}
|
|
@@ -13087,7 +13087,7 @@ function renderTicket(ticket, template, escapeHtml3) {
|
|
|
13087
13087
|
}
|
|
13088
13088
|
function renderScenario(args, deps) {
|
|
13089
13089
|
const { tc } = args;
|
|
13090
|
-
const
|
|
13090
|
+
const statusIcon2 = deps.getStatusIcon(tc.status);
|
|
13091
13091
|
const statusClass = `status-${tc.status}`;
|
|
13092
13092
|
const duration = tc.durationMs > 0 ? `${(tc.durationMs / 1e3).toFixed(2)}s` : "";
|
|
13093
13093
|
const tags = tc.tags.map((t) => `<span class="tag">${deps.escapeHtml(t)}</span>`).join("");
|
|
@@ -13156,7 +13156,7 @@ function renderScenario(args, deps) {
|
|
|
13156
13156
|
<div class="scenario-header" role="button" tabindex="0" aria-expanded="${ariaExpanded}">
|
|
13157
13157
|
<div class="scenario-info">
|
|
13158
13158
|
<div class="scenario-title">
|
|
13159
|
-
<span class="status-icon ${statusClass}">${
|
|
13159
|
+
<span class="status-icon ${statusClass}">${statusIcon2}</span>
|
|
13160
13160
|
<span class="scenario-name">${deps.escapeHtml(tc.story.scenario)}</span>
|
|
13161
13161
|
</div>
|
|
13162
13162
|
<div class="scenario-meta">${tags}${tickets}${sourceLink}${traceBadge}${metricBadges}</div>
|
|
@@ -13295,11 +13295,11 @@ function buildTooltip(span, escapeHtml3) {
|
|
|
13295
13295
|
parts.push(`${key}=${formatted}`);
|
|
13296
13296
|
}
|
|
13297
13297
|
}
|
|
13298
|
-
let
|
|
13299
|
-
if (
|
|
13300
|
-
|
|
13298
|
+
let text2 = parts.join("\n");
|
|
13299
|
+
if (text2.length > TOOLTIP_MAX_LENGTH) {
|
|
13300
|
+
text2 = text2.slice(0, TOOLTIP_MAX_LENGTH - 3) + "...";
|
|
13301
13301
|
}
|
|
13302
|
-
return escapeHtml3(
|
|
13302
|
+
return escapeHtml3(text2);
|
|
13303
13303
|
}
|
|
13304
13304
|
function renderTraceView(args, deps) {
|
|
13305
13305
|
if (!args.spans || args.spans.length === 0) return "";
|
|
@@ -13522,11 +13522,11 @@ function renderToc(args, deps) {
|
|
|
13522
13522
|
const featureName = suitePaths.length > 0 && suitePaths[0].length > 0 ? suitePaths[0][0] : file.split("/").pop()?.replace(/\.[^.]+$/, "") ?? file;
|
|
13523
13523
|
const featureSlug = `feature-${slugify(file)}`;
|
|
13524
13524
|
const scenarios = testCases.map((tc) => {
|
|
13525
|
-
const
|
|
13525
|
+
const statusIcon2 = deps.getStatusIcon(tc.status);
|
|
13526
13526
|
const statusClass = `status-${tc.status}`;
|
|
13527
13527
|
const failedClass = tc.status === "failed" ? " toc-failed" : "";
|
|
13528
13528
|
return `<a class="toc-scenario${failedClass}" href="#scenario-${tc.id}">
|
|
13529
|
-
<span class="toc-status ${statusClass}">${
|
|
13529
|
+
<span class="toc-status ${statusClass}">${statusIcon2}</span>
|
|
13530
13530
|
${deps.escapeHtml(tc.story.scenario)}
|
|
13531
13531
|
</a>`;
|
|
13532
13532
|
}).join("\n");
|
|
@@ -13584,7 +13584,7 @@ function createHtmlFormatter(options = {}) {
|
|
|
13584
13584
|
escapeHtml,
|
|
13585
13585
|
getStatusIcon,
|
|
13586
13586
|
renderDocs,
|
|
13587
|
-
highlightStepParams: (
|
|
13587
|
+
highlightStepParams: (text2) => highlightStepParams(text2, { escapeHtml })
|
|
13588
13588
|
};
|
|
13589
13589
|
const scenarioDeps = {
|
|
13590
13590
|
escapeHtml,
|
|
@@ -14519,7 +14519,7 @@ function deterministicId(kind, salt, ...parts) {
|
|
|
14519
14519
|
|
|
14520
14520
|
// src/formatters/cucumber-messages/build-gherkin-document.ts
|
|
14521
14521
|
function buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt) {
|
|
14522
|
-
const { lineMap, featureName, featureTags, text } = synthesized;
|
|
14522
|
+
const { lineMap, featureName, featureTags, text: text2 } = synthesized;
|
|
14523
14523
|
const featureTagNodes = featureTags.map((tag, i) => ({
|
|
14524
14524
|
location: {
|
|
14525
14525
|
line: lineMap.featureTagLine ?? 1,
|
|
@@ -14588,7 +14588,7 @@ function buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt) {
|
|
|
14588
14588
|
sourceEnvelope: {
|
|
14589
14589
|
source: {
|
|
14590
14590
|
uri,
|
|
14591
|
-
data:
|
|
14591
|
+
data: text2,
|
|
14592
14592
|
mediaType: "text/x.cucumber.gherkin+plain"
|
|
14593
14593
|
}
|
|
14594
14594
|
},
|
|
@@ -14599,8 +14599,8 @@ function buildStepArguments(step, stepLine) {
|
|
|
14599
14599
|
if (!step.docs || step.docs.length === 0) return {};
|
|
14600
14600
|
const tableDocs = step.docs.filter((d) => d.kind === "table");
|
|
14601
14601
|
if (tableDocs.length > 0) {
|
|
14602
|
-
const
|
|
14603
|
-
return { dataTable: buildDataTable(
|
|
14602
|
+
const table2 = tableDocs[0];
|
|
14603
|
+
return { dataTable: buildDataTable(table2, stepLine + 1) };
|
|
14604
14604
|
}
|
|
14605
14605
|
for (const doc of step.docs) {
|
|
14606
14606
|
const ds = docEntryToDocString(doc, stepLine + 1);
|
|
@@ -14671,21 +14671,21 @@ function docEntryToDocString(doc, line) {
|
|
|
14671
14671
|
return void 0;
|
|
14672
14672
|
}
|
|
14673
14673
|
}
|
|
14674
|
-
function buildDataTable(
|
|
14674
|
+
function buildDataTable(table2, line) {
|
|
14675
14675
|
const rows = [];
|
|
14676
14676
|
rows.push({
|
|
14677
14677
|
location: { line },
|
|
14678
|
-
cells:
|
|
14678
|
+
cells: table2.columns.map((col) => ({
|
|
14679
14679
|
location: { line },
|
|
14680
14680
|
value: col
|
|
14681
14681
|
})),
|
|
14682
14682
|
id: ""
|
|
14683
14683
|
});
|
|
14684
|
-
for (let r = 0; r <
|
|
14684
|
+
for (let r = 0; r < table2.rows.length; r++) {
|
|
14685
14685
|
const rowLine = line + 1 + r;
|
|
14686
14686
|
rows.push({
|
|
14687
14687
|
location: { line: rowLine },
|
|
14688
|
-
cells:
|
|
14688
|
+
cells: table2.rows[r].map((cell) => ({
|
|
14689
14689
|
location: { line: rowLine },
|
|
14690
14690
|
value: cell
|
|
14691
14691
|
})),
|
|
@@ -14776,12 +14776,12 @@ function docEntryToPickleDocString(doc) {
|
|
|
14776
14776
|
return void 0;
|
|
14777
14777
|
}
|
|
14778
14778
|
}
|
|
14779
|
-
function buildPickleTable(
|
|
14779
|
+
function buildPickleTable(table2) {
|
|
14780
14780
|
const rows = [];
|
|
14781
14781
|
rows.push({
|
|
14782
|
-
cells:
|
|
14782
|
+
cells: table2.columns.map((col) => ({ value: col }))
|
|
14783
14783
|
});
|
|
14784
|
-
for (const row of
|
|
14784
|
+
for (const row of table2.rows) {
|
|
14785
14785
|
rows.push({
|
|
14786
14786
|
cells: row.map((cell) => ({ value: cell }))
|
|
14787
14787
|
});
|
|
@@ -16197,6 +16197,414 @@ ${body}`;
|
|
|
16197
16197
|
}
|
|
16198
16198
|
};
|
|
16199
16199
|
|
|
16200
|
+
// src/formatters/confluence.ts
|
|
16201
|
+
var ConfluenceFormatter = class {
|
|
16202
|
+
options;
|
|
16203
|
+
constructor(options = {}) {
|
|
16204
|
+
this.options = {
|
|
16205
|
+
title: options.title ?? "User Stories",
|
|
16206
|
+
includeStatusIcons: options.includeStatusIcons ?? true,
|
|
16207
|
+
includeMetadata: options.includeMetadata ?? true,
|
|
16208
|
+
includeSummaryTable: options.includeSummaryTable ?? true,
|
|
16209
|
+
includeErrors: options.includeErrors ?? true,
|
|
16210
|
+
scenarioHeadingLevel: options.scenarioHeadingLevel ?? 3,
|
|
16211
|
+
groupBy: options.groupBy ?? "file",
|
|
16212
|
+
sortScenarios: options.sortScenarios ?? "source",
|
|
16213
|
+
pretty: options.pretty ?? true,
|
|
16214
|
+
permalinkBaseUrl: options.permalinkBaseUrl,
|
|
16215
|
+
ticketUrlTemplate: options.ticketUrlTemplate
|
|
16216
|
+
};
|
|
16217
|
+
}
|
|
16218
|
+
/** Build the ADF document tree. Returns the JS object (not stringified). */
|
|
16219
|
+
formatToAdf(run) {
|
|
16220
|
+
const content = [];
|
|
16221
|
+
content.push(heading(1, [text(this.options.title)]));
|
|
16222
|
+
if (this.options.includeMetadata) {
|
|
16223
|
+
const metaTable = this.renderMetadataTable(run);
|
|
16224
|
+
if (metaTable) content.push(metaTable);
|
|
16225
|
+
}
|
|
16226
|
+
if (this.options.includeSummaryTable) {
|
|
16227
|
+
content.push(this.renderSummaryTable(run));
|
|
16228
|
+
}
|
|
16229
|
+
switch (this.options.groupBy) {
|
|
16230
|
+
case "none":
|
|
16231
|
+
this.renderFlat(content, run.testCases);
|
|
16232
|
+
break;
|
|
16233
|
+
case "suite":
|
|
16234
|
+
this.renderBySuite(content, run.testCases);
|
|
16235
|
+
break;
|
|
16236
|
+
case "file":
|
|
16237
|
+
default:
|
|
16238
|
+
this.renderByFile(content, run.testCases);
|
|
16239
|
+
break;
|
|
16240
|
+
}
|
|
16241
|
+
return { version: 1, type: "doc", content };
|
|
16242
|
+
}
|
|
16243
|
+
/** Format a test run as an ADF JSON string. */
|
|
16244
|
+
format(run) {
|
|
16245
|
+
const adf = this.formatToAdf(run);
|
|
16246
|
+
return this.options.pretty ? JSON.stringify(adf, null, 2) : JSON.stringify(adf);
|
|
16247
|
+
}
|
|
16248
|
+
// --------------------------------------------------------------------------
|
|
16249
|
+
// Metadata / summary tables
|
|
16250
|
+
// --------------------------------------------------------------------------
|
|
16251
|
+
renderMetadataTable(run) {
|
|
16252
|
+
const rows = [];
|
|
16253
|
+
rows.push(["Date", new Date(run.startedAtMs).toISOString()]);
|
|
16254
|
+
if (run.packageVersion) rows.push(["Version", run.packageVersion]);
|
|
16255
|
+
if (run.gitSha) {
|
|
16256
|
+
const shortSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;
|
|
16257
|
+
rows.push(["Git SHA", shortSha]);
|
|
16258
|
+
}
|
|
16259
|
+
if (rows.length === 0) return null;
|
|
16260
|
+
return table([
|
|
16261
|
+
tableRow([tableHeader("Key"), tableHeader("Value")]),
|
|
16262
|
+
...rows.map(([k, v]) => tableRow([tableCell(k), tableCell(v)]))
|
|
16263
|
+
]);
|
|
16264
|
+
}
|
|
16265
|
+
renderSummaryTable(run) {
|
|
16266
|
+
const total = run.testCases.length;
|
|
16267
|
+
const steps = run.testCases.reduce(
|
|
16268
|
+
(acc, tc) => acc + tc.story.steps.length,
|
|
16269
|
+
0
|
|
16270
|
+
);
|
|
16271
|
+
const passed = run.testCases.filter((tc) => tc.status === "passed").length;
|
|
16272
|
+
const failed = run.testCases.filter((tc) => tc.status === "failed").length;
|
|
16273
|
+
const skipped = run.testCases.filter((tc) => tc.status === "skipped").length;
|
|
16274
|
+
const pending = run.testCases.filter((tc) => tc.status === "pending").length;
|
|
16275
|
+
return table([
|
|
16276
|
+
tableRow([
|
|
16277
|
+
tableHeader("Scenarios"),
|
|
16278
|
+
tableHeader("Steps"),
|
|
16279
|
+
tableHeader("Passed"),
|
|
16280
|
+
tableHeader("Failed"),
|
|
16281
|
+
tableHeader("Skipped"),
|
|
16282
|
+
tableHeader("Pending"),
|
|
16283
|
+
tableHeader("Duration")
|
|
16284
|
+
]),
|
|
16285
|
+
tableRow([
|
|
16286
|
+
tableCell(String(total)),
|
|
16287
|
+
tableCell(String(steps)),
|
|
16288
|
+
tableCell(String(passed)),
|
|
16289
|
+
tableCell(String(failed)),
|
|
16290
|
+
tableCell(String(skipped)),
|
|
16291
|
+
tableCell(String(pending)),
|
|
16292
|
+
tableCell(formatDuration2(run.durationMs))
|
|
16293
|
+
])
|
|
16294
|
+
]);
|
|
16295
|
+
}
|
|
16296
|
+
// --------------------------------------------------------------------------
|
|
16297
|
+
// Grouping
|
|
16298
|
+
// --------------------------------------------------------------------------
|
|
16299
|
+
renderByFile(content, testCases) {
|
|
16300
|
+
const byFile = groupBy7(testCases, (tc) => tc.sourceFile);
|
|
16301
|
+
for (const [file, fileCases] of byFile) {
|
|
16302
|
+
content.push(heading(2, [codeInline(file)]));
|
|
16303
|
+
this.renderSuiteGroups(content, fileCases, 3);
|
|
16304
|
+
}
|
|
16305
|
+
}
|
|
16306
|
+
renderBySuite(content, testCases) {
|
|
16307
|
+
this.renderSuiteGroups(content, testCases, 2);
|
|
16308
|
+
}
|
|
16309
|
+
renderFlat(content, testCases) {
|
|
16310
|
+
const sorted = this.sortCases(testCases);
|
|
16311
|
+
for (const tc of sorted) this.renderScenario(content, tc);
|
|
16312
|
+
}
|
|
16313
|
+
renderSuiteGroups(content, testCases, baseLevel) {
|
|
16314
|
+
const bySuite = groupBy7(testCases, (tc) => tc.titlePath.join(" - "));
|
|
16315
|
+
const entries = this.sortSuiteGroups([...bySuite.entries()]);
|
|
16316
|
+
for (const [suitePath, cases] of entries) {
|
|
16317
|
+
if (suitePath) {
|
|
16318
|
+
content.push(
|
|
16319
|
+
heading(clampHeadingLevel(baseLevel), [text(suitePath)])
|
|
16320
|
+
);
|
|
16321
|
+
}
|
|
16322
|
+
for (const tc of this.sortCases(cases)) {
|
|
16323
|
+
this.renderScenario(content, tc);
|
|
16324
|
+
}
|
|
16325
|
+
}
|
|
16326
|
+
}
|
|
16327
|
+
// --------------------------------------------------------------------------
|
|
16328
|
+
// Scenario
|
|
16329
|
+
// --------------------------------------------------------------------------
|
|
16330
|
+
renderScenario(content, tc) {
|
|
16331
|
+
const level = clampHeadingLevel(this.options.scenarioHeadingLevel);
|
|
16332
|
+
const headingNodes = [];
|
|
16333
|
+
if (this.options.includeStatusIcons) {
|
|
16334
|
+
headingNodes.push(text(`${statusIcon(tc.status)} `));
|
|
16335
|
+
}
|
|
16336
|
+
headingNodes.push(text(tc.story.scenario));
|
|
16337
|
+
content.push(heading(level, headingNodes));
|
|
16338
|
+
const metaChildren = [];
|
|
16339
|
+
if (tc.tags.length > 0) {
|
|
16340
|
+
metaChildren.push(text("Tags: ", strong()));
|
|
16341
|
+
tc.tags.forEach((t, i) => {
|
|
16342
|
+
if (i > 0) metaChildren.push(text(", "));
|
|
16343
|
+
metaChildren.push(codeInline(t));
|
|
16344
|
+
});
|
|
16345
|
+
}
|
|
16346
|
+
if (tc.story.tickets && tc.story.tickets.length > 0) {
|
|
16347
|
+
if (metaChildren.length > 0) metaChildren.push(text(" | "));
|
|
16348
|
+
metaChildren.push(text("Tickets: ", strong()));
|
|
16349
|
+
tc.story.tickets.forEach((ticket, i) => {
|
|
16350
|
+
if (i > 0) metaChildren.push(text(", "));
|
|
16351
|
+
const url = ticket.url ?? (this.options.ticketUrlTemplate ? this.options.ticketUrlTemplate.replace("{ticket}", ticket.id) : void 0);
|
|
16352
|
+
metaChildren.push(
|
|
16353
|
+
url ? link(ticket.id, url) : codeInline(ticket.id)
|
|
16354
|
+
);
|
|
16355
|
+
});
|
|
16356
|
+
}
|
|
16357
|
+
if (this.options.permalinkBaseUrl && tc.sourceFile !== "unknown" && tc.sourceFile) {
|
|
16358
|
+
if (metaChildren.length > 0) metaChildren.push(text(" | "));
|
|
16359
|
+
metaChildren.push(text("Source: ", strong()));
|
|
16360
|
+
const base = this.options.permalinkBaseUrl.replace(/\/$/, "");
|
|
16361
|
+
const url = `${base}/${tc.sourceFile}${tc.sourceLine > 0 ? `#L${tc.sourceLine}` : ""}`;
|
|
16362
|
+
metaChildren.push(link(tc.sourceFile, url));
|
|
16363
|
+
}
|
|
16364
|
+
if (metaChildren.length > 0) {
|
|
16365
|
+
content.push(paragraph(metaChildren));
|
|
16366
|
+
}
|
|
16367
|
+
if (tc.story.docs && tc.story.docs.length > 0) {
|
|
16368
|
+
for (const doc of tc.story.docs) this.renderDocEntry(content, doc);
|
|
16369
|
+
}
|
|
16370
|
+
if (tc.story.steps.length > 0) {
|
|
16371
|
+
content.push(this.renderStepsList(tc.story.steps));
|
|
16372
|
+
for (const step of tc.story.steps) {
|
|
16373
|
+
if (step.docs && step.docs.length > 0) {
|
|
16374
|
+
for (const doc of step.docs) this.renderDocEntry(content, doc);
|
|
16375
|
+
}
|
|
16376
|
+
}
|
|
16377
|
+
}
|
|
16378
|
+
if (tc.status === "failed" && tc.errorMessage && this.options.includeErrors) {
|
|
16379
|
+
const errorContent = (tc.errorMessage ?? "") + (tc.errorStack ? `
|
|
16380
|
+
|
|
16381
|
+
${tc.errorStack}` : "");
|
|
16382
|
+
content.push(
|
|
16383
|
+
panel("warning", [paragraph([text("Failure", strong())])])
|
|
16384
|
+
);
|
|
16385
|
+
content.push(codeBlock(errorContent, "text"));
|
|
16386
|
+
}
|
|
16387
|
+
}
|
|
16388
|
+
renderStepsList(steps) {
|
|
16389
|
+
return {
|
|
16390
|
+
type: "bulletList",
|
|
16391
|
+
content: steps.map((step) => {
|
|
16392
|
+
const children = [text(`${step.keyword} `, strong()), text(step.text)];
|
|
16393
|
+
if (step.mode && step.mode !== "normal") {
|
|
16394
|
+
children.push(text(` (${step.mode})`, em()));
|
|
16395
|
+
}
|
|
16396
|
+
return {
|
|
16397
|
+
type: "listItem",
|
|
16398
|
+
content: [paragraph(children)]
|
|
16399
|
+
};
|
|
16400
|
+
})
|
|
16401
|
+
};
|
|
16402
|
+
}
|
|
16403
|
+
// --------------------------------------------------------------------------
|
|
16404
|
+
// Doc entries
|
|
16405
|
+
// --------------------------------------------------------------------------
|
|
16406
|
+
renderDocEntry(content, entry) {
|
|
16407
|
+
switch (entry.kind) {
|
|
16408
|
+
case "note":
|
|
16409
|
+
content.push(panel("info", [paragraph([text(entry.text)])]));
|
|
16410
|
+
break;
|
|
16411
|
+
case "tag": {
|
|
16412
|
+
const kids = [];
|
|
16413
|
+
entry.names.forEach((name, i) => {
|
|
16414
|
+
if (i > 0) kids.push(text(" "));
|
|
16415
|
+
kids.push(codeInline(name));
|
|
16416
|
+
});
|
|
16417
|
+
if (kids.length > 0) content.push(paragraph(kids));
|
|
16418
|
+
break;
|
|
16419
|
+
}
|
|
16420
|
+
case "kv": {
|
|
16421
|
+
const val = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
16422
|
+
content.push(
|
|
16423
|
+
paragraph([text(`${entry.label}: `, strong()), codeInline(val)])
|
|
16424
|
+
);
|
|
16425
|
+
break;
|
|
16426
|
+
}
|
|
16427
|
+
case "code":
|
|
16428
|
+
if (entry.label) {
|
|
16429
|
+
content.push(paragraph([text(entry.label, strong())]));
|
|
16430
|
+
}
|
|
16431
|
+
content.push(codeBlock(entry.content ?? "", entry.lang));
|
|
16432
|
+
break;
|
|
16433
|
+
case "table":
|
|
16434
|
+
if (entry.label) {
|
|
16435
|
+
content.push(paragraph([text(entry.label, strong())]));
|
|
16436
|
+
}
|
|
16437
|
+
content.push(
|
|
16438
|
+
table([
|
|
16439
|
+
tableRow(entry.columns.map((c) => tableHeader(c))),
|
|
16440
|
+
...entry.rows.map(
|
|
16441
|
+
(row) => tableRow(row.map((cell) => tableCell(cell)))
|
|
16442
|
+
)
|
|
16443
|
+
])
|
|
16444
|
+
);
|
|
16445
|
+
break;
|
|
16446
|
+
case "link":
|
|
16447
|
+
content.push(paragraph([link(entry.label, entry.url)]));
|
|
16448
|
+
break;
|
|
16449
|
+
case "section":
|
|
16450
|
+
if (entry.title) {
|
|
16451
|
+
content.push(paragraph([text(entry.title, strong())]));
|
|
16452
|
+
}
|
|
16453
|
+
if (entry.markdown) {
|
|
16454
|
+
for (const para of entry.markdown.split(/\n{2,}/)) {
|
|
16455
|
+
const trimmed = para.trim();
|
|
16456
|
+
if (trimmed) content.push(paragraph([text(trimmed)]));
|
|
16457
|
+
}
|
|
16458
|
+
}
|
|
16459
|
+
break;
|
|
16460
|
+
case "mermaid":
|
|
16461
|
+
if (entry.title) {
|
|
16462
|
+
content.push(paragraph([text(entry.title, strong())]));
|
|
16463
|
+
}
|
|
16464
|
+
content.push(codeBlock(entry.code ?? "", "mermaid"));
|
|
16465
|
+
break;
|
|
16466
|
+
case "screenshot":
|
|
16467
|
+
content.push(
|
|
16468
|
+
paragraph([
|
|
16469
|
+
text(entry.alt ?? "Screenshot", strong()),
|
|
16470
|
+
text(": "),
|
|
16471
|
+
link(entry.path, entry.path)
|
|
16472
|
+
])
|
|
16473
|
+
);
|
|
16474
|
+
break;
|
|
16475
|
+
case "custom":
|
|
16476
|
+
content.push(paragraph([text(`[${entry.type}]`, strong())]));
|
|
16477
|
+
content.push(codeBlock(JSON.stringify(entry.data ?? null, null, 2), "json"));
|
|
16478
|
+
break;
|
|
16479
|
+
}
|
|
16480
|
+
if (entry.children && entry.children.length > 0) {
|
|
16481
|
+
for (const child of entry.children) {
|
|
16482
|
+
this.renderDocEntry(content, child);
|
|
16483
|
+
}
|
|
16484
|
+
}
|
|
16485
|
+
}
|
|
16486
|
+
// --------------------------------------------------------------------------
|
|
16487
|
+
// Sorting
|
|
16488
|
+
// --------------------------------------------------------------------------
|
|
16489
|
+
sortCases(cases) {
|
|
16490
|
+
if (this.options.sortScenarios === "alpha") {
|
|
16491
|
+
return [...cases].sort(
|
|
16492
|
+
(a, b) => a.story.scenario.localeCompare(b.story.scenario)
|
|
16493
|
+
);
|
|
16494
|
+
}
|
|
16495
|
+
if (this.options.sortScenarios === "source") {
|
|
16496
|
+
return [...cases].sort(
|
|
16497
|
+
(a, b) => (a.story.sourceOrder ?? 0) - (b.story.sourceOrder ?? 0)
|
|
16498
|
+
);
|
|
16499
|
+
}
|
|
16500
|
+
return cases;
|
|
16501
|
+
}
|
|
16502
|
+
sortSuiteGroups(entries) {
|
|
16503
|
+
if (this.options.sortScenarios === "alpha") {
|
|
16504
|
+
return entries.sort(([a], [b]) => a.localeCompare(b));
|
|
16505
|
+
}
|
|
16506
|
+
if (this.options.sortScenarios === "source") {
|
|
16507
|
+
return entries.sort(([, a], [, b]) => {
|
|
16508
|
+
const minA = Math.min(...a.map((s) => s.story.sourceOrder ?? Infinity));
|
|
16509
|
+
const minB = Math.min(...b.map((s) => s.story.sourceOrder ?? Infinity));
|
|
16510
|
+
return minA - minB;
|
|
16511
|
+
});
|
|
16512
|
+
}
|
|
16513
|
+
return entries;
|
|
16514
|
+
}
|
|
16515
|
+
};
|
|
16516
|
+
function text(value, mark) {
|
|
16517
|
+
const node = { type: "text", text: value };
|
|
16518
|
+
if (mark) {
|
|
16519
|
+
node.marks = Array.isArray(mark) ? mark : [mark];
|
|
16520
|
+
}
|
|
16521
|
+
return node;
|
|
16522
|
+
}
|
|
16523
|
+
function strong() {
|
|
16524
|
+
return { type: "strong" };
|
|
16525
|
+
}
|
|
16526
|
+
function em() {
|
|
16527
|
+
return { type: "em" };
|
|
16528
|
+
}
|
|
16529
|
+
function codeMark() {
|
|
16530
|
+
return { type: "code" };
|
|
16531
|
+
}
|
|
16532
|
+
function codeInline(value) {
|
|
16533
|
+
return text(value, codeMark());
|
|
16534
|
+
}
|
|
16535
|
+
function link(label, href) {
|
|
16536
|
+
return text(label, { type: "link", attrs: { href } });
|
|
16537
|
+
}
|
|
16538
|
+
function paragraph(content) {
|
|
16539
|
+
return { type: "paragraph", content };
|
|
16540
|
+
}
|
|
16541
|
+
function heading(level, content) {
|
|
16542
|
+
return {
|
|
16543
|
+
type: "heading",
|
|
16544
|
+
attrs: { level: clampHeadingLevel(level) },
|
|
16545
|
+
content
|
|
16546
|
+
};
|
|
16547
|
+
}
|
|
16548
|
+
function codeBlock(content, lang) {
|
|
16549
|
+
return {
|
|
16550
|
+
type: "codeBlock",
|
|
16551
|
+
attrs: lang ? { language: lang } : {},
|
|
16552
|
+
content: content ? [{ type: "text", text: content }] : []
|
|
16553
|
+
};
|
|
16554
|
+
}
|
|
16555
|
+
function panel(panelType, content) {
|
|
16556
|
+
return { type: "panel", attrs: { panelType }, content };
|
|
16557
|
+
}
|
|
16558
|
+
function table(rows) {
|
|
16559
|
+
return {
|
|
16560
|
+
type: "table",
|
|
16561
|
+
attrs: { isNumberColumnEnabled: false, layout: "default" },
|
|
16562
|
+
content: rows
|
|
16563
|
+
};
|
|
16564
|
+
}
|
|
16565
|
+
function tableRow(cells) {
|
|
16566
|
+
return { type: "tableRow", content: cells };
|
|
16567
|
+
}
|
|
16568
|
+
function tableHeader(value) {
|
|
16569
|
+
return { type: "tableHeader", content: [paragraph([text(value)])] };
|
|
16570
|
+
}
|
|
16571
|
+
function tableCell(value) {
|
|
16572
|
+
return { type: "tableCell", content: [paragraph([text(value)])] };
|
|
16573
|
+
}
|
|
16574
|
+
function clampHeadingLevel(level) {
|
|
16575
|
+
if (level < 1) return 1;
|
|
16576
|
+
if (level > 6) return 6;
|
|
16577
|
+
return level;
|
|
16578
|
+
}
|
|
16579
|
+
function statusIcon(status) {
|
|
16580
|
+
switch (status) {
|
|
16581
|
+
case "passed":
|
|
16582
|
+
return "\u2705";
|
|
16583
|
+
case "failed":
|
|
16584
|
+
return "\u274C";
|
|
16585
|
+
case "skipped":
|
|
16586
|
+
return "\u23E9";
|
|
16587
|
+
case "pending":
|
|
16588
|
+
return "\u{1F4DD}";
|
|
16589
|
+
default:
|
|
16590
|
+
return "\u26A0\uFE0F";
|
|
16591
|
+
}
|
|
16592
|
+
}
|
|
16593
|
+
function formatDuration2(ms) {
|
|
16594
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
16595
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
16596
|
+
}
|
|
16597
|
+
function groupBy7(items, keyFn) {
|
|
16598
|
+
const map = /* @__PURE__ */ new Map();
|
|
16599
|
+
for (const item of items) {
|
|
16600
|
+
const key = keyFn(item);
|
|
16601
|
+
const existing = map.get(key);
|
|
16602
|
+
if (existing) existing.push(item);
|
|
16603
|
+
else map.set(key, [item]);
|
|
16604
|
+
}
|
|
16605
|
+
return map;
|
|
16606
|
+
}
|
|
16607
|
+
|
|
16200
16608
|
// src/formatters/astro-assets.ts
|
|
16201
16609
|
import * as fs4 from "fs";
|
|
16202
16610
|
import * as path4 from "path";
|
|
@@ -16722,6 +17130,235 @@ ${result.errors.join("\n")}`);
|
|
|
16722
17130
|
}
|
|
16723
17131
|
}
|
|
16724
17132
|
|
|
17133
|
+
// src/publishers/confluence.ts
|
|
17134
|
+
function parseAdf(adf) {
|
|
17135
|
+
let parsed;
|
|
17136
|
+
try {
|
|
17137
|
+
parsed = JSON.parse(adf);
|
|
17138
|
+
} catch (err) {
|
|
17139
|
+
throw new Error(
|
|
17140
|
+
`ADF payload is not valid JSON: ${err.message}`
|
|
17141
|
+
);
|
|
17142
|
+
}
|
|
17143
|
+
if (!parsed || typeof parsed !== "object" || parsed.type !== "doc" || !Array.isArray(parsed.content)) {
|
|
17144
|
+
throw new Error(
|
|
17145
|
+
`ADF payload must be an object with { version, type: "doc", content: [...] }`
|
|
17146
|
+
);
|
|
17147
|
+
}
|
|
17148
|
+
return parsed;
|
|
17149
|
+
}
|
|
17150
|
+
function basicAuthHeader(auth) {
|
|
17151
|
+
const raw = `${auth.email}:${auth.token}`;
|
|
17152
|
+
const encoded = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(raw);
|
|
17153
|
+
return `Basic ${encoded}`;
|
|
17154
|
+
}
|
|
17155
|
+
async function parseErrorBody(response) {
|
|
17156
|
+
try {
|
|
17157
|
+
const body = await response.text();
|
|
17158
|
+
return body ? body.slice(0, 800) : "";
|
|
17159
|
+
} catch {
|
|
17160
|
+
return "";
|
|
17161
|
+
}
|
|
17162
|
+
}
|
|
17163
|
+
async function publishConfluencePage(args, deps) {
|
|
17164
|
+
parseAdf(args.adf);
|
|
17165
|
+
if (!args.pageId && !args.spaceId) {
|
|
17166
|
+
throw new Error(
|
|
17167
|
+
"publishConfluencePage requires either pageId (update) or spaceId (create)"
|
|
17168
|
+
);
|
|
17169
|
+
}
|
|
17170
|
+
if (!args.pageId && !args.title) {
|
|
17171
|
+
throw new Error("Creating a new page requires a title");
|
|
17172
|
+
}
|
|
17173
|
+
const base = args.baseUrl.replace(/\/$/, "");
|
|
17174
|
+
const fetchFn = deps.fetch ?? globalThis.fetch;
|
|
17175
|
+
if (!fetchFn) {
|
|
17176
|
+
throw new Error("No fetch implementation available (Node >= 22 expected)");
|
|
17177
|
+
}
|
|
17178
|
+
const headers = {
|
|
17179
|
+
Authorization: basicAuthHeader(deps.auth),
|
|
17180
|
+
Accept: "application/json",
|
|
17181
|
+
"Content-Type": "application/json"
|
|
17182
|
+
};
|
|
17183
|
+
if (args.pageId) {
|
|
17184
|
+
return updatePage(args, base, headers, fetchFn);
|
|
17185
|
+
}
|
|
17186
|
+
return createPage(args, base, headers, fetchFn);
|
|
17187
|
+
}
|
|
17188
|
+
async function updatePage(args, base, headers, fetchFn) {
|
|
17189
|
+
const getUrl = `${base}/api/v2/pages/${encodeURIComponent(args.pageId)}`;
|
|
17190
|
+
const getResp = await fetchFn(getUrl, { method: "GET", headers });
|
|
17191
|
+
if (!getResp.ok) {
|
|
17192
|
+
const body = await parseErrorBody(getResp);
|
|
17193
|
+
throw new Error(
|
|
17194
|
+
`GET ${getUrl} failed with ${getResp.status} ${getResp.statusText}${body ? `: ${body}` : ""}`
|
|
17195
|
+
);
|
|
17196
|
+
}
|
|
17197
|
+
const current = await getResp.json();
|
|
17198
|
+
const nextVersion = current.version.number + 1;
|
|
17199
|
+
const title = args.title ?? current.title;
|
|
17200
|
+
const putUrl = `${base}/api/v2/pages/${encodeURIComponent(args.pageId)}`;
|
|
17201
|
+
const putResp = await fetchFn(putUrl, {
|
|
17202
|
+
method: "PUT",
|
|
17203
|
+
headers,
|
|
17204
|
+
body: JSON.stringify({
|
|
17205
|
+
id: args.pageId,
|
|
17206
|
+
status: "current",
|
|
17207
|
+
title,
|
|
17208
|
+
body: {
|
|
17209
|
+
representation: "atlas_doc_format",
|
|
17210
|
+
value: args.adf
|
|
17211
|
+
},
|
|
17212
|
+
version: { number: nextVersion }
|
|
17213
|
+
})
|
|
17214
|
+
});
|
|
17215
|
+
if (!putResp.ok) {
|
|
17216
|
+
const body = await parseErrorBody(putResp);
|
|
17217
|
+
throw new Error(
|
|
17218
|
+
`PUT ${putUrl} failed with ${putResp.status} ${putResp.statusText}${body ? `: ${body}` : ""}`
|
|
17219
|
+
);
|
|
17220
|
+
}
|
|
17221
|
+
const updated = await putResp.json();
|
|
17222
|
+
return {
|
|
17223
|
+
id: updated.id,
|
|
17224
|
+
title: updated.title,
|
|
17225
|
+
version: updated.version.number,
|
|
17226
|
+
url: buildPageUrl(base, updated._links?.webui, updated.id),
|
|
17227
|
+
action: "updated"
|
|
17228
|
+
};
|
|
17229
|
+
}
|
|
17230
|
+
async function createPage(args, base, headers, fetchFn) {
|
|
17231
|
+
const body = {
|
|
17232
|
+
spaceId: args.spaceId,
|
|
17233
|
+
status: "current",
|
|
17234
|
+
title: args.title,
|
|
17235
|
+
body: {
|
|
17236
|
+
representation: "atlas_doc_format",
|
|
17237
|
+
value: args.adf
|
|
17238
|
+
}
|
|
17239
|
+
};
|
|
17240
|
+
if (args.parentId) body.parentId = args.parentId;
|
|
17241
|
+
const postUrl = `${base}/api/v2/pages`;
|
|
17242
|
+
const resp = await fetchFn(postUrl, {
|
|
17243
|
+
method: "POST",
|
|
17244
|
+
headers,
|
|
17245
|
+
body: JSON.stringify(body)
|
|
17246
|
+
});
|
|
17247
|
+
if (!resp.ok) {
|
|
17248
|
+
const errBody = await parseErrorBody(resp);
|
|
17249
|
+
throw new Error(
|
|
17250
|
+
`POST ${postUrl} failed with ${resp.status} ${resp.statusText}${errBody ? `: ${errBody}` : ""}`
|
|
17251
|
+
);
|
|
17252
|
+
}
|
|
17253
|
+
const created = await resp.json();
|
|
17254
|
+
return {
|
|
17255
|
+
id: created.id,
|
|
17256
|
+
title: created.title,
|
|
17257
|
+
version: created.version.number,
|
|
17258
|
+
url: buildPageUrl(base, created._links?.webui, created.id),
|
|
17259
|
+
action: "created"
|
|
17260
|
+
};
|
|
17261
|
+
}
|
|
17262
|
+
function buildPageUrl(base, webui, id) {
|
|
17263
|
+
if (webui) {
|
|
17264
|
+
return webui.startsWith("http") ? webui : `${base}${webui}`;
|
|
17265
|
+
}
|
|
17266
|
+
return `${base}/pages/${id}`;
|
|
17267
|
+
}
|
|
17268
|
+
|
|
17269
|
+
// src/publishers/jira.ts
|
|
17270
|
+
function parseAdf2(adf) {
|
|
17271
|
+
let parsed;
|
|
17272
|
+
try {
|
|
17273
|
+
parsed = JSON.parse(adf);
|
|
17274
|
+
} catch (err) {
|
|
17275
|
+
throw new Error(
|
|
17276
|
+
`ADF payload is not valid JSON: ${err.message}`
|
|
17277
|
+
);
|
|
17278
|
+
}
|
|
17279
|
+
if (!parsed || typeof parsed !== "object" || parsed.type !== "doc" || !Array.isArray(parsed.content)) {
|
|
17280
|
+
throw new Error(
|
|
17281
|
+
`ADF payload must be an object with { version, type: "doc", content: [...] }`
|
|
17282
|
+
);
|
|
17283
|
+
}
|
|
17284
|
+
return parsed;
|
|
17285
|
+
}
|
|
17286
|
+
function basicAuthHeader2(auth) {
|
|
17287
|
+
const raw = `${auth.email}:${auth.token}`;
|
|
17288
|
+
const encoded = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(raw);
|
|
17289
|
+
return `Basic ${encoded}`;
|
|
17290
|
+
}
|
|
17291
|
+
async function parseErrorBody2(resp) {
|
|
17292
|
+
try {
|
|
17293
|
+
const body = await resp.text();
|
|
17294
|
+
return body ? body.slice(0, 800) : "";
|
|
17295
|
+
} catch {
|
|
17296
|
+
return "";
|
|
17297
|
+
}
|
|
17298
|
+
}
|
|
17299
|
+
async function publishJiraIssue(args, deps) {
|
|
17300
|
+
const adfObject = parseAdf2(args.adf);
|
|
17301
|
+
if (!args.issueKey) {
|
|
17302
|
+
throw new Error("publishJiraIssue requires an issueKey, e.g. PROJ-123");
|
|
17303
|
+
}
|
|
17304
|
+
const base = args.baseUrl.replace(/\/$/, "");
|
|
17305
|
+
const fetchFn = deps.fetch ?? globalThis.fetch;
|
|
17306
|
+
if (!fetchFn) {
|
|
17307
|
+
throw new Error("No fetch implementation available (Node >= 22 expected)");
|
|
17308
|
+
}
|
|
17309
|
+
const headers = {
|
|
17310
|
+
Authorization: basicAuthHeader2(deps.auth),
|
|
17311
|
+
Accept: "application/json",
|
|
17312
|
+
"Content-Type": "application/json"
|
|
17313
|
+
};
|
|
17314
|
+
const mode = args.mode ?? "comment";
|
|
17315
|
+
if (mode === "description") {
|
|
17316
|
+
return updateDescription(args.issueKey, base, adfObject, headers, fetchFn);
|
|
17317
|
+
}
|
|
17318
|
+
return addComment(args.issueKey, base, adfObject, headers, fetchFn);
|
|
17319
|
+
}
|
|
17320
|
+
async function addComment(issueKey, base, adf, headers, fetchFn) {
|
|
17321
|
+
const url = `${base}/rest/api/3/issue/${encodeURIComponent(issueKey)}/comment`;
|
|
17322
|
+
const resp = await fetchFn(url, {
|
|
17323
|
+
method: "POST",
|
|
17324
|
+
headers,
|
|
17325
|
+
body: JSON.stringify({ body: adf })
|
|
17326
|
+
});
|
|
17327
|
+
if (!resp.ok) {
|
|
17328
|
+
const body = await parseErrorBody2(resp);
|
|
17329
|
+
throw new Error(
|
|
17330
|
+
`POST ${url} failed with ${resp.status} ${resp.statusText}${body ? `: ${body}` : ""}`
|
|
17331
|
+
);
|
|
17332
|
+
}
|
|
17333
|
+
const comment = await resp.json();
|
|
17334
|
+
const issueUrl = `${base}/browse/${encodeURIComponent(issueKey)}`;
|
|
17335
|
+
return {
|
|
17336
|
+
issueKey,
|
|
17337
|
+
action: "comment-added",
|
|
17338
|
+
url: `${issueUrl}?focusedCommentId=${encodeURIComponent(comment.id)}`,
|
|
17339
|
+
commentId: comment.id
|
|
17340
|
+
};
|
|
17341
|
+
}
|
|
17342
|
+
async function updateDescription(issueKey, base, adf, headers, fetchFn) {
|
|
17343
|
+
const url = `${base}/rest/api/3/issue/${encodeURIComponent(issueKey)}`;
|
|
17344
|
+
const resp = await fetchFn(url, {
|
|
17345
|
+
method: "PUT",
|
|
17346
|
+
headers,
|
|
17347
|
+
body: JSON.stringify({ fields: { description: adf } })
|
|
17348
|
+
});
|
|
17349
|
+
if (!resp.ok) {
|
|
17350
|
+
const body = await parseErrorBody2(resp);
|
|
17351
|
+
throw new Error(
|
|
17352
|
+
`PUT ${url} failed with ${resp.status} ${resp.statusText}${body ? `: ${body}` : ""}`
|
|
17353
|
+
);
|
|
17354
|
+
}
|
|
17355
|
+
return {
|
|
17356
|
+
issueKey,
|
|
17357
|
+
action: "description-updated",
|
|
17358
|
+
url: `${base}/browse/${encodeURIComponent(issueKey)}`
|
|
17359
|
+
};
|
|
17360
|
+
}
|
|
17361
|
+
|
|
16725
17362
|
// src/converters/ndjson-parser.ts
|
|
16726
17363
|
function parseNdjson(ndjson) {
|
|
16727
17364
|
const lines = ndjson.trim().split("\n").filter(Boolean);
|
|
@@ -17022,10 +17659,10 @@ function pickleStepArgumentToDocs(ps) {
|
|
|
17022
17659
|
const docs = [];
|
|
17023
17660
|
const phase = "static";
|
|
17024
17661
|
if (ps.argument.dataTable) {
|
|
17025
|
-
const
|
|
17026
|
-
if (
|
|
17027
|
-
const columns =
|
|
17028
|
-
const rows =
|
|
17662
|
+
const table2 = ps.argument.dataTable;
|
|
17663
|
+
if (table2.rows.length > 0) {
|
|
17664
|
+
const columns = table2.rows[0].cells.map((c) => c.value);
|
|
17665
|
+
const rows = table2.rows.slice(1).map((r) => r.cells.map((c) => c.value));
|
|
17029
17666
|
docs.push({
|
|
17030
17667
|
kind: "table",
|
|
17031
17668
|
label: "",
|
|
@@ -17160,7 +17797,7 @@ function readBranchName(cwd = process.cwd()) {
|
|
|
17160
17797
|
}
|
|
17161
17798
|
|
|
17162
17799
|
// src/utils/duration.ts
|
|
17163
|
-
function
|
|
17800
|
+
function formatDuration3(ms) {
|
|
17164
17801
|
if (ms < 1e3) {
|
|
17165
17802
|
return `${Math.round(ms)} ms`;
|
|
17166
17803
|
}
|
|
@@ -17347,16 +17984,16 @@ function resolveTraceUrl(template, traceId) {
|
|
|
17347
17984
|
}
|
|
17348
17985
|
|
|
17349
17986
|
// src/notifiers/ansi-strip.ts
|
|
17350
|
-
function stripAnsi(
|
|
17351
|
-
return
|
|
17987
|
+
function stripAnsi(text2) {
|
|
17988
|
+
return text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
17352
17989
|
}
|
|
17353
17990
|
|
|
17354
17991
|
// src/notifiers/slack.ts
|
|
17355
|
-
function truncate(
|
|
17356
|
-
if (
|
|
17357
|
-
return
|
|
17992
|
+
function truncate(text2, maxLen) {
|
|
17993
|
+
if (text2.length <= maxLen) return text2;
|
|
17994
|
+
return text2.slice(0, maxLen - 3) + "...";
|
|
17358
17995
|
}
|
|
17359
|
-
function
|
|
17996
|
+
function formatDuration4(ms) {
|
|
17360
17997
|
const seconds = ms / 1e3;
|
|
17361
17998
|
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
17362
17999
|
const minutes = Math.floor(seconds / 60);
|
|
@@ -17383,7 +18020,7 @@ function buildSlackPayload(summary, maxFailedTests) {
|
|
|
17383
18020
|
{ type: "mrkdwn", text: `*Passed:* ${summary.passed}` },
|
|
17384
18021
|
{ type: "mrkdwn", text: `*Failed:* ${summary.failed}` },
|
|
17385
18022
|
{ type: "mrkdwn", text: `*Skipped:* ${summary.skipped}` },
|
|
17386
|
-
{ type: "mrkdwn", text: `*Duration:* ${
|
|
18023
|
+
{ type: "mrkdwn", text: `*Duration:* ${formatDuration4(summary.durationMs)}` },
|
|
17387
18024
|
{ type: "mrkdwn", text: `*Status:* ${statusText}` }
|
|
17388
18025
|
]
|
|
17389
18026
|
});
|
|
@@ -17398,9 +18035,9 @@ function buildSlackPayload(summary, maxFailedTests) {
|
|
|
17398
18035
|
}
|
|
17399
18036
|
return `*${name}*`;
|
|
17400
18037
|
});
|
|
17401
|
-
let
|
|
18038
|
+
let text2 = lines.join("\n\n");
|
|
17402
18039
|
if (summary.failedTests.length > maxFailedTests) {
|
|
17403
|
-
|
|
18040
|
+
text2 += `
|
|
17404
18041
|
|
|
17405
18042
|
_...and ${summary.failedTests.length - maxFailedTests} more_`;
|
|
17406
18043
|
}
|
|
@@ -17408,7 +18045,7 @@ _...and ${summary.failedTests.length - maxFailedTests} more_`;
|
|
|
17408
18045
|
type: "section",
|
|
17409
18046
|
text: {
|
|
17410
18047
|
type: "mrkdwn",
|
|
17411
|
-
text
|
|
18048
|
+
text: text2
|
|
17412
18049
|
}
|
|
17413
18050
|
});
|
|
17414
18051
|
}
|
|
@@ -17485,11 +18122,11 @@ async function sendSlackNotification(args, deps) {
|
|
|
17485
18122
|
}
|
|
17486
18123
|
|
|
17487
18124
|
// src/notifiers/teams.ts
|
|
17488
|
-
function truncate2(
|
|
17489
|
-
if (
|
|
17490
|
-
return
|
|
18125
|
+
function truncate2(text2, maxLen) {
|
|
18126
|
+
if (text2.length <= maxLen) return text2;
|
|
18127
|
+
return text2.slice(0, maxLen - 3) + "...";
|
|
17491
18128
|
}
|
|
17492
|
-
function
|
|
18129
|
+
function formatDuration5(ms) {
|
|
17493
18130
|
const seconds = ms / 1e3;
|
|
17494
18131
|
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
17495
18132
|
const minutes = Math.floor(seconds / 60);
|
|
@@ -17515,7 +18152,7 @@ function buildTeamsPayload(summary, maxFailedTests) {
|
|
|
17515
18152
|
{ title: "Passed", value: String(summary.passed) },
|
|
17516
18153
|
{ title: "Failed", value: String(summary.failed) },
|
|
17517
18154
|
{ title: "Skipped", value: String(summary.skipped) },
|
|
17518
|
-
{ title: "Duration", value:
|
|
18155
|
+
{ title: "Duration", value: formatDuration5(summary.durationMs) }
|
|
17519
18156
|
]
|
|
17520
18157
|
});
|
|
17521
18158
|
if (summary.failedTests.length > 0) {
|
|
@@ -18169,7 +18806,8 @@ var FORMAT_EXTENSIONS = {
|
|
|
18169
18806
|
"cucumber-html": ".cucumber.html",
|
|
18170
18807
|
junit: ".junit.xml",
|
|
18171
18808
|
"cucumber-json": ".cucumber.json",
|
|
18172
|
-
"cucumber-messages": ".ndjson"
|
|
18809
|
+
"cucumber-messages": ".ndjson",
|
|
18810
|
+
confluence: ".adf.json"
|
|
18173
18811
|
};
|
|
18174
18812
|
var TEST_EXTENSIONS = [
|
|
18175
18813
|
".test.ts",
|
|
@@ -18337,6 +18975,19 @@ var ReportGenerator = class {
|
|
|
18337
18975
|
includeSourceLinks: options.markdown?.includeSourceLinks ?? true,
|
|
18338
18976
|
customRenderers: options.markdown?.customRenderers
|
|
18339
18977
|
},
|
|
18978
|
+
confluence: {
|
|
18979
|
+
title: options.confluence?.title ?? "User Stories",
|
|
18980
|
+
includeStatusIcons: options.confluence?.includeStatusIcons ?? true,
|
|
18981
|
+
includeMetadata: options.confluence?.includeMetadata ?? true,
|
|
18982
|
+
includeSummaryTable: options.confluence?.includeSummaryTable ?? true,
|
|
18983
|
+
includeErrors: options.confluence?.includeErrors ?? true,
|
|
18984
|
+
scenarioHeadingLevel: options.confluence?.scenarioHeadingLevel ?? 3,
|
|
18985
|
+
groupBy: options.confluence?.groupBy ?? "file",
|
|
18986
|
+
sortScenarios: options.confluence?.sortScenarios ?? "source",
|
|
18987
|
+
pretty: options.confluence?.pretty ?? true,
|
|
18988
|
+
permalinkBaseUrl: options.confluence?.permalinkBaseUrl,
|
|
18989
|
+
ticketUrlTemplate: options.confluence?.ticketUrlTemplate
|
|
18990
|
+
},
|
|
18340
18991
|
astro: {
|
|
18341
18992
|
assetsDir: options.astro?.assetsDir ?? "public/stories/assets",
|
|
18342
18993
|
assetsBaseUrl: options.astro?.assetsBaseUrl ?? "/stories/assets",
|
|
@@ -18512,6 +19163,22 @@ var ReportGenerator = class {
|
|
|
18512
19163
|
});
|
|
18513
19164
|
return formatter.format(run);
|
|
18514
19165
|
}
|
|
19166
|
+
case "confluence": {
|
|
19167
|
+
const formatter = new ConfluenceFormatter({
|
|
19168
|
+
title: this.options.confluence.title,
|
|
19169
|
+
includeStatusIcons: this.options.confluence.includeStatusIcons,
|
|
19170
|
+
includeMetadata: this.options.confluence.includeMetadata,
|
|
19171
|
+
includeSummaryTable: this.options.confluence.includeSummaryTable,
|
|
19172
|
+
includeErrors: this.options.confluence.includeErrors,
|
|
19173
|
+
scenarioHeadingLevel: this.options.confluence.scenarioHeadingLevel,
|
|
19174
|
+
groupBy: this.options.confluence.groupBy,
|
|
19175
|
+
sortScenarios: this.options.confluence.sortScenarios,
|
|
19176
|
+
pretty: this.options.confluence.pretty,
|
|
19177
|
+
permalinkBaseUrl: this.options.confluence.permalinkBaseUrl,
|
|
19178
|
+
ticketUrlTemplate: this.options.confluence.ticketUrlTemplate
|
|
19179
|
+
});
|
|
19180
|
+
return formatter.format(run);
|
|
19181
|
+
}
|
|
18515
19182
|
case "markdown": {
|
|
18516
19183
|
const formatter = new MarkdownFormatter({
|
|
18517
19184
|
title: this.options.markdown.title,
|
|
@@ -18570,6 +19237,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
18570
19237
|
}
|
|
18571
19238
|
export {
|
|
18572
19239
|
AstroFormatter,
|
|
19240
|
+
ConfluenceFormatter,
|
|
18573
19241
|
CucumberHtmlFormatter,
|
|
18574
19242
|
CucumberJsonFormatter,
|
|
18575
19243
|
CucumberMessagesFormatter,
|
|
@@ -18601,7 +19269,7 @@ export {
|
|
|
18601
19269
|
detectPerformanceTrend,
|
|
18602
19270
|
diffRuns,
|
|
18603
19271
|
findGitDir,
|
|
18604
|
-
|
|
19272
|
+
formatDuration3 as formatDuration,
|
|
18605
19273
|
generateRunComparison,
|
|
18606
19274
|
generateRunId,
|
|
18607
19275
|
generateTestCaseId,
|
|
@@ -18619,6 +19287,8 @@ export {
|
|
|
18619
19287
|
normalizeVitestResults,
|
|
18620
19288
|
parseEnvelopes,
|
|
18621
19289
|
parseNdjson,
|
|
19290
|
+
publishConfluencePage,
|
|
19291
|
+
publishJiraIssue,
|
|
18622
19292
|
readBranchName,
|
|
18623
19293
|
readGitSha,
|
|
18624
19294
|
readPackageVersion,
|