executable-stories-formatters 0.11.0 → 0.11.2
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/binding.gyp +9 -0
- package/dist/cli.js +722 -193
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +245 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -2
- package/dist/index.d.ts +88 -2
- package/dist/index.js +237 -41
- package/dist/index.js.map +1 -1
- package/index.js +1 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(src_exports, {
|
|
|
44
44
|
MIN_METRIC_SAMPLES: () => MIN_METRIC_SAMPLES,
|
|
45
45
|
MIN_PERF_SAMPLES: () => MIN_PERF_SAMPLES,
|
|
46
46
|
MarkdownFormatter: () => MarkdownFormatter,
|
|
47
|
+
ReleaseManifestFormatter: () => ReleaseManifestFormatter,
|
|
47
48
|
ReportGenerator: () => ReportGenerator,
|
|
48
49
|
ReviewHtmlFormatter: () => ReviewHtmlFormatter,
|
|
49
50
|
ReviewMarkdownFormatter: () => ReviewMarkdownFormatter,
|
|
@@ -83,6 +84,8 @@ __export(src_exports, {
|
|
|
83
84
|
generateTestCaseId: () => generateTestCaseId,
|
|
84
85
|
getAvailableThemes: () => getAvailableThemes,
|
|
85
86
|
getCssOnlyThemes: () => getCssOnlyThemes,
|
|
87
|
+
getDeploymentStatus: () => getDeploymentStatus,
|
|
88
|
+
getEnvironmentDrift: () => getEnvironmentDrift,
|
|
86
89
|
gradeEvidence: () => gradeEvidence,
|
|
87
90
|
hasSufficientHistory: () => hasSufficientHistory,
|
|
88
91
|
isReviewableSource: () => isReviewableSource,
|
|
@@ -104,6 +107,7 @@ __export(src_exports, {
|
|
|
104
107
|
readBranchName: () => readBranchName,
|
|
105
108
|
readGitSha: () => readGitSha,
|
|
106
109
|
readPackageVersion: () => readPackageVersion,
|
|
110
|
+
recordDeployment: () => recordDeployment,
|
|
107
111
|
regenerateArtifacts: () => regenerateArtifacts,
|
|
108
112
|
resolveAttachment: () => resolveAttachment,
|
|
109
113
|
resolveAttachments: () => resolveAttachments,
|
|
@@ -123,6 +127,7 @@ __export(src_exports, {
|
|
|
123
127
|
toBehaviorManifest: () => toBehaviorManifest,
|
|
124
128
|
toCIInfo: () => toCIInfo,
|
|
125
129
|
toRawCIInfo: () => toRawCIInfo,
|
|
130
|
+
toReleaseManifest: () => toReleaseManifest,
|
|
126
131
|
toScenarioIndex: () => toScenarioIndex,
|
|
127
132
|
toStoryReport: () => toStoryReport,
|
|
128
133
|
tryGetActiveOtelContext: () => tryGetActiveOtelContext,
|
|
@@ -130,8 +135,8 @@ __export(src_exports, {
|
|
|
130
135
|
validateCanonicalRun: () => validateCanonicalRun
|
|
131
136
|
});
|
|
132
137
|
module.exports = __toCommonJS(src_exports);
|
|
133
|
-
var
|
|
134
|
-
var
|
|
138
|
+
var fs10 = require("fs");
|
|
139
|
+
var path11 = __toESM(require("path"), 1);
|
|
135
140
|
var fsPromises = __toESM(require("fs/promises"), 1);
|
|
136
141
|
|
|
137
142
|
// src/converters/acl/status.ts
|
|
@@ -1020,23 +1025,23 @@ function buildFeature(relSourceFile, group) {
|
|
|
1020
1025
|
function ensureUniqueFeatureIds(features) {
|
|
1021
1026
|
const seen = /* @__PURE__ */ new Map();
|
|
1022
1027
|
for (const f of features) {
|
|
1023
|
-
const
|
|
1024
|
-
if (
|
|
1025
|
-
seen.set(f.id,
|
|
1028
|
+
const count2 = seen.get(f.id) ?? 0;
|
|
1029
|
+
if (count2 > 0) f.id = `${f.id}-${count2 + 1}`;
|
|
1030
|
+
seen.set(f.id, count2 + 1);
|
|
1026
1031
|
}
|
|
1027
1032
|
}
|
|
1028
1033
|
function ensureUniqueScenarioIds(feature) {
|
|
1029
1034
|
const seen = /* @__PURE__ */ new Map();
|
|
1030
1035
|
for (const s of feature.scenarios) {
|
|
1031
|
-
const
|
|
1032
|
-
if (
|
|
1033
|
-
const newId = `${s.id}-${
|
|
1036
|
+
const count2 = seen.get(s.id) ?? 0;
|
|
1037
|
+
if (count2 > 0) {
|
|
1038
|
+
const newId = `${s.id}-${count2 + 1}`;
|
|
1034
1039
|
for (const step of s.steps) {
|
|
1035
1040
|
step.id = step.id.replace(s.id, newId);
|
|
1036
1041
|
}
|
|
1037
1042
|
s.id = newId;
|
|
1038
1043
|
}
|
|
1039
|
-
seen.set(s.id,
|
|
1044
|
+
seen.set(s.id, count2 + 1);
|
|
1040
1045
|
}
|
|
1041
1046
|
}
|
|
1042
1047
|
function toStoryReport(run) {
|
|
@@ -15568,6 +15573,88 @@ function groupBy6(items, keyFn) {
|
|
|
15568
15573
|
return map;
|
|
15569
15574
|
}
|
|
15570
15575
|
|
|
15576
|
+
// src/formatters/release-manifest.ts
|
|
15577
|
+
var import_node_crypto2 = require("crypto");
|
|
15578
|
+
var ReleaseManifestFormatter = class {
|
|
15579
|
+
format(run) {
|
|
15580
|
+
const manifest = toReleaseManifest(run);
|
|
15581
|
+
const lines = [];
|
|
15582
|
+
lines.push("# Release Manifest");
|
|
15583
|
+
lines.push("");
|
|
15584
|
+
lines.push(`Generated: ${manifest.generatedAt}`);
|
|
15585
|
+
lines.push(`Run: ${manifest.run.startedAt} to ${manifest.run.finishedAt}`);
|
|
15586
|
+
if (manifest.run.branch) lines.push(`Branch: ${manifest.run.branch}`);
|
|
15587
|
+
if (manifest.run.gitSha) lines.push(`Commit: ${manifest.run.gitSha}`);
|
|
15588
|
+
lines.push(`Tested-together hash: \`${manifest.testedTogetherHash}\``);
|
|
15589
|
+
lines.push("");
|
|
15590
|
+
lines.push("| Scenarios | Passed | Failed | Skipped | Pending |");
|
|
15591
|
+
lines.push("| ---: | ---: | ---: | ---: | ---: |");
|
|
15592
|
+
lines.push(`| ${manifest.run.total} | ${manifest.run.passed} | ${manifest.run.failed} | ${manifest.run.skipped} | ${manifest.run.pending} |`);
|
|
15593
|
+
lines.push("");
|
|
15594
|
+
lines.push("## Scenarios");
|
|
15595
|
+
lines.push("");
|
|
15596
|
+
lines.push("| Status | Scenario | Source | Tags |");
|
|
15597
|
+
lines.push("| --- | --- | --- | --- |");
|
|
15598
|
+
for (const scenario of manifest.scenarios) {
|
|
15599
|
+
const source = `${scenario.sourceFile}:${scenario.sourceLine}`;
|
|
15600
|
+
const tags = scenario.tags.length > 0 ? scenario.tags.map((tag) => `\`${tag}\``).join(", ") : "";
|
|
15601
|
+
lines.push(`| ${renderStatus(scenario.status)} | ${escapePipe(scenario.title)} | \`${source}\` | ${tags} |`);
|
|
15602
|
+
}
|
|
15603
|
+
return lines.join("\n");
|
|
15604
|
+
}
|
|
15605
|
+
};
|
|
15606
|
+
function toReleaseManifest(run) {
|
|
15607
|
+
const scenarios = [...run.testCases].sort((a, b) => a.id.localeCompare(b.id)).map((tc) => ({
|
|
15608
|
+
id: tc.id,
|
|
15609
|
+
title: tc.story.scenario,
|
|
15610
|
+
status: tc.status,
|
|
15611
|
+
sourceFile: tc.sourceFile,
|
|
15612
|
+
sourceLine: tc.sourceLine,
|
|
15613
|
+
tags: tc.tags
|
|
15614
|
+
}));
|
|
15615
|
+
const fingerprint = scenarios.map((scenario) => `${scenario.id}:${scenario.status}`).join("\n");
|
|
15616
|
+
return {
|
|
15617
|
+
schemaVersion: "1.0",
|
|
15618
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15619
|
+
run: {
|
|
15620
|
+
startedAt: new Date(run.startedAtMs).toISOString(),
|
|
15621
|
+
finishedAt: new Date(run.finishedAtMs).toISOString(),
|
|
15622
|
+
gitSha: run.gitSha,
|
|
15623
|
+
branch: getBranch(run),
|
|
15624
|
+
total: run.testCases.length,
|
|
15625
|
+
passed: count(run.testCases, "passed"),
|
|
15626
|
+
failed: count(run.testCases, "failed"),
|
|
15627
|
+
skipped: count(run.testCases, "skipped"),
|
|
15628
|
+
pending: count(run.testCases, "pending")
|
|
15629
|
+
},
|
|
15630
|
+
testedTogetherHash: (0, import_node_crypto2.createHash)("sha256").update(fingerprint).digest("hex"),
|
|
15631
|
+
scenarios
|
|
15632
|
+
};
|
|
15633
|
+
}
|
|
15634
|
+
function getBranch(run) {
|
|
15635
|
+
return run.ci?.branch;
|
|
15636
|
+
}
|
|
15637
|
+
function count(testCases, status) {
|
|
15638
|
+
return testCases.filter((tc) => tc.status === status).length;
|
|
15639
|
+
}
|
|
15640
|
+
function renderStatus(status) {
|
|
15641
|
+
switch (status) {
|
|
15642
|
+
case "passed":
|
|
15643
|
+
return "passed";
|
|
15644
|
+
case "failed":
|
|
15645
|
+
return "failed";
|
|
15646
|
+
case "skipped":
|
|
15647
|
+
return "skipped";
|
|
15648
|
+
case "pending":
|
|
15649
|
+
return "pending";
|
|
15650
|
+
default:
|
|
15651
|
+
return status;
|
|
15652
|
+
}
|
|
15653
|
+
}
|
|
15654
|
+
function escapePipe(value) {
|
|
15655
|
+
return value.replace(/\|/g, "\\|");
|
|
15656
|
+
}
|
|
15657
|
+
|
|
15571
15658
|
// src/formatters/cucumber-messages/synthesize-feature.ts
|
|
15572
15659
|
function extractFeatureName(testCases, uri) {
|
|
15573
15660
|
for (const tc of testCases) {
|
|
@@ -15634,7 +15721,7 @@ function synthesizeFeature(uri, testCases) {
|
|
|
15634
15721
|
}
|
|
15635
15722
|
|
|
15636
15723
|
// src/utils/cucumber-messages.ts
|
|
15637
|
-
var
|
|
15724
|
+
var import_node_crypto3 = require("crypto");
|
|
15638
15725
|
function msToTimestamp(ms) {
|
|
15639
15726
|
const seconds = Math.floor(ms / 1e3);
|
|
15640
15727
|
const nanos = Math.round(ms % 1e3 * 1e6);
|
|
@@ -15700,7 +15787,7 @@ function statusToCucumberStatus(status) {
|
|
|
15700
15787
|
}
|
|
15701
15788
|
function deterministicId(kind, salt, ...parts) {
|
|
15702
15789
|
const input = [salt, kind, ...parts].join("::");
|
|
15703
|
-
return (0,
|
|
15790
|
+
return (0, import_node_crypto3.createHash)("sha1").update(input).digest("hex").slice(0, 36);
|
|
15704
15791
|
}
|
|
15705
15792
|
|
|
15706
15793
|
// src/formatters/cucumber-messages/build-gherkin-document.ts
|
|
@@ -16188,8 +16275,8 @@ function extractDocAttachments(step) {
|
|
|
16188
16275
|
}
|
|
16189
16276
|
return attachments;
|
|
16190
16277
|
}
|
|
16191
|
-
function guessMediaType(
|
|
16192
|
-
const lower =
|
|
16278
|
+
function guessMediaType(path12) {
|
|
16279
|
+
const lower = path12.toLowerCase();
|
|
16193
16280
|
if (lower.endsWith(".png")) return "image/png";
|
|
16194
16281
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
16195
16282
|
if (lower.endsWith(".gif")) return "image/gif";
|
|
@@ -16330,11 +16417,11 @@ var CucumberHtmlFormatter = class {
|
|
|
16330
16417
|
for (const envelope of envelopes) {
|
|
16331
16418
|
const accepted = htmlStream.write(envelope);
|
|
16332
16419
|
if (!accepted) {
|
|
16333
|
-
await new Promise((
|
|
16420
|
+
await new Promise((resolve9) => htmlStream.once("drain", resolve9));
|
|
16334
16421
|
}
|
|
16335
16422
|
}
|
|
16336
|
-
await new Promise((
|
|
16337
|
-
collector.on("finish",
|
|
16423
|
+
await new Promise((resolve9, reject) => {
|
|
16424
|
+
collector.on("finish", resolve9);
|
|
16338
16425
|
collector.on("error", reject);
|
|
16339
16426
|
htmlStream.end();
|
|
16340
16427
|
});
|
|
@@ -17398,8 +17485,8 @@ ${body}`;
|
|
|
17398
17485
|
}
|
|
17399
17486
|
buildFrontmatter(run) {
|
|
17400
17487
|
const badge = _AstroFormatter.computeBadge(run.testCases);
|
|
17401
|
-
const
|
|
17402
|
-
const description = `${
|
|
17488
|
+
const count2 = run.testCases.length;
|
|
17489
|
+
const description = `${count2} scenario${count2 !== 1 ? "s" : ""} \u2014 ${badge.text.toLowerCase()}`;
|
|
17403
17490
|
const lines = [
|
|
17404
17491
|
"---",
|
|
17405
17492
|
`title: ${yamlScalar(this.deriveTitle(run))}`,
|
|
@@ -18397,14 +18484,14 @@ ${result.errors.join("\n")}`);
|
|
|
18397
18484
|
}
|
|
18398
18485
|
|
|
18399
18486
|
// src/coverage-index.ts
|
|
18400
|
-
function normalizePath(
|
|
18401
|
-
return
|
|
18487
|
+
function normalizePath(path12) {
|
|
18488
|
+
return path12.replace(/^\.\//, "");
|
|
18402
18489
|
}
|
|
18403
18490
|
function scenariosCoveringPaths(index, paths) {
|
|
18404
18491
|
const queries = paths.map(normalizePath);
|
|
18405
18492
|
return index.scenarios.filter(
|
|
18406
18493
|
(scenario) => scenario.covers.some(
|
|
18407
|
-
(glob) => queries.some((
|
|
18494
|
+
(glob) => queries.some((path12) => matchesPattern(normalizePath(glob), path12))
|
|
18408
18495
|
)
|
|
18409
18496
|
);
|
|
18410
18497
|
}
|
|
@@ -19716,7 +19803,7 @@ async function sendTeamsNotification(args, deps) {
|
|
|
19716
19803
|
}
|
|
19717
19804
|
|
|
19718
19805
|
// src/notifiers/hmac.ts
|
|
19719
|
-
var
|
|
19806
|
+
var import_node_crypto4 = require("crypto");
|
|
19720
19807
|
function signBody(args) {
|
|
19721
19808
|
let input;
|
|
19722
19809
|
let timestamp;
|
|
@@ -19726,7 +19813,7 @@ function signBody(args) {
|
|
|
19726
19813
|
} else {
|
|
19727
19814
|
input = args.body;
|
|
19728
19815
|
}
|
|
19729
|
-
const hex = (0,
|
|
19816
|
+
const hex = (0, import_node_crypto4.createHmac)("sha256", args.secret).update(input, "utf8").digest("hex");
|
|
19730
19817
|
return {
|
|
19731
19818
|
signature: `sha256=${hex}`,
|
|
19732
19819
|
timestamp
|
|
@@ -20322,18 +20409,18 @@ function deriveChangeType(tags) {
|
|
|
20322
20409
|
}
|
|
20323
20410
|
return "unknown";
|
|
20324
20411
|
}
|
|
20325
|
-
function extensionOf(
|
|
20326
|
-
const base =
|
|
20412
|
+
function extensionOf(path12) {
|
|
20413
|
+
const base = path12.split("/").pop() ?? path12;
|
|
20327
20414
|
const dot = base.lastIndexOf(".");
|
|
20328
20415
|
return dot === -1 ? "" : base.slice(dot + 1).toLowerCase();
|
|
20329
20416
|
}
|
|
20330
|
-
function isTestFile(
|
|
20331
|
-
return TEST_INFIX.test(
|
|
20417
|
+
function isTestFile(path12) {
|
|
20418
|
+
return TEST_INFIX.test(path12);
|
|
20332
20419
|
}
|
|
20333
|
-
function isReviewableSource(
|
|
20334
|
-
if (isTestFile(
|
|
20335
|
-
if (
|
|
20336
|
-
return CODE_EXTENSIONS.has(extensionOf(
|
|
20420
|
+
function isReviewableSource(path12) {
|
|
20421
|
+
if (isTestFile(path12)) return false;
|
|
20422
|
+
if (path12.endsWith(".d.ts")) return false;
|
|
20423
|
+
return CODE_EXTENSIONS.has(extensionOf(path12));
|
|
20337
20424
|
}
|
|
20338
20425
|
function testBaseKey(testFile) {
|
|
20339
20426
|
return testFile.replace(TEST_INFIX, "");
|
|
@@ -20437,7 +20524,7 @@ function toClaim(testCase, changedSourcePaths) {
|
|
|
20437
20524
|
const { strength, reasons } = gradeEvidence(testCase, audience);
|
|
20438
20525
|
const key = testBaseKey(testCase.sourceFile);
|
|
20439
20526
|
const coversFiles = changedSourcePaths.filter(
|
|
20440
|
-
(
|
|
20527
|
+
(path12) => sourceBaseKey(path12) === key
|
|
20441
20528
|
);
|
|
20442
20529
|
return {
|
|
20443
20530
|
id: testCase.id,
|
|
@@ -20967,11 +21054,116 @@ applyTheme(getEffectiveTheme());` : "";
|
|
|
20967
21054
|
}
|
|
20968
21055
|
};
|
|
20969
21056
|
|
|
21057
|
+
// src/deploy/ledger.ts
|
|
21058
|
+
var fs9 = __toESM(require("fs"), 1);
|
|
21059
|
+
var path10 = __toESM(require("path"), 1);
|
|
21060
|
+
function createEmptyLedger() {
|
|
21061
|
+
return {
|
|
21062
|
+
deployments: [],
|
|
21063
|
+
schemaVersion: 1
|
|
21064
|
+
};
|
|
21065
|
+
}
|
|
21066
|
+
function loadLedger(ledgerPath) {
|
|
21067
|
+
const resolved = path10.resolve(ledgerPath);
|
|
21068
|
+
if (!fs9.existsSync(resolved)) {
|
|
21069
|
+
return createEmptyLedger();
|
|
21070
|
+
}
|
|
21071
|
+
try {
|
|
21072
|
+
const raw = JSON.parse(fs9.readFileSync(resolved, "utf8"));
|
|
21073
|
+
if (raw.schemaVersion !== 1) {
|
|
21074
|
+
throw new Error(`Unsupported ledger schemaVersion: ${raw.schemaVersion}`);
|
|
21075
|
+
}
|
|
21076
|
+
return raw;
|
|
21077
|
+
} catch (err) {
|
|
21078
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21079
|
+
throw new Error(`Failed to load deployment ledger at ${resolved}: ${msg}`);
|
|
21080
|
+
}
|
|
21081
|
+
}
|
|
21082
|
+
function saveLedger(ledger, ledgerPath) {
|
|
21083
|
+
const resolved = path10.resolve(ledgerPath);
|
|
21084
|
+
const dir = path10.dirname(resolved);
|
|
21085
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
21086
|
+
fs9.writeFileSync(resolved, JSON.stringify(ledger, null, 2), "utf8");
|
|
21087
|
+
}
|
|
21088
|
+
function getLatestDeployment(ledger, environment) {
|
|
21089
|
+
return [...ledger.deployments].reverse().find((d) => d.environment === environment);
|
|
21090
|
+
}
|
|
21091
|
+
|
|
21092
|
+
// src/deploy/index.ts
|
|
21093
|
+
function recordDeployment(args) {
|
|
21094
|
+
const ledger = loadLedger(args.ledgerPath);
|
|
21095
|
+
const previous = getLatestDeployment(ledger, args.environment);
|
|
21096
|
+
const entry = {
|
|
21097
|
+
environment: args.environment,
|
|
21098
|
+
tag: args.tag,
|
|
21099
|
+
sha: args.run.gitSha,
|
|
21100
|
+
runFile: args.runFilePath,
|
|
21101
|
+
scenarioIds: args.run.testCases.map((tc) => tc.id),
|
|
21102
|
+
scenarioStatuses: Object.fromEntries(args.run.testCases.map((tc) => [tc.id, tc.status])),
|
|
21103
|
+
timestamp: new Date(args.run.finishedAtMs).toISOString(),
|
|
21104
|
+
summary: countStatuses(args.run)
|
|
21105
|
+
};
|
|
21106
|
+
ledger.deployments.push(entry);
|
|
21107
|
+
if (previous) {
|
|
21108
|
+
const previousIds = new Set(previous.scenarioIds);
|
|
21109
|
+
const added = entry.scenarioIds.filter((id) => !previousIds.has(id)).length;
|
|
21110
|
+
const removed = previous.scenarioIds.filter((id) => !entry.scenarioIds.includes(id)).length;
|
|
21111
|
+
if (added > 0 || removed > 0) {
|
|
21112
|
+
}
|
|
21113
|
+
}
|
|
21114
|
+
saveLedger(ledger, args.ledgerPath);
|
|
21115
|
+
return { entry, ledgerPath: args.ledgerPath };
|
|
21116
|
+
}
|
|
21117
|
+
function getDeploymentStatus(ledgerPath) {
|
|
21118
|
+
const ledger = loadLedger(ledgerPath);
|
|
21119
|
+
const environments = {};
|
|
21120
|
+
for (const entry of ledger.deployments) {
|
|
21121
|
+
environments[entry.environment] = {
|
|
21122
|
+
latest: entry,
|
|
21123
|
+
previousDeployment: environments[entry.environment]?.latest
|
|
21124
|
+
};
|
|
21125
|
+
}
|
|
21126
|
+
return { environments, ledgerPath };
|
|
21127
|
+
}
|
|
21128
|
+
function getEnvironmentDrift(ledgerPath, envA, envB) {
|
|
21129
|
+
const ledger = loadLedger(ledgerPath);
|
|
21130
|
+
const aEntry = getLatestDeployment(ledger, envA);
|
|
21131
|
+
const bEntry = getLatestDeployment(ledger, envB);
|
|
21132
|
+
if (!aEntry) {
|
|
21133
|
+
throw new Error(`No deployment found for environment "${envA}"`);
|
|
21134
|
+
}
|
|
21135
|
+
if (!bEntry) {
|
|
21136
|
+
throw new Error(`No deployment found for environment "${envB}"`);
|
|
21137
|
+
}
|
|
21138
|
+
const aIds = new Set(aEntry.scenarioIds);
|
|
21139
|
+
const bIds = new Set(bEntry.scenarioIds);
|
|
21140
|
+
const onlyInA = aEntry.scenarioIds.filter((id) => !bIds.has(id));
|
|
21141
|
+
const onlyInB = bEntry.scenarioIds.filter((id) => !aIds.has(id));
|
|
21142
|
+
const inBoth = aEntry.scenarioIds.filter((id) => bIds.has(id));
|
|
21143
|
+
const statusChanged = inBoth.map((id) => ({
|
|
21144
|
+
id,
|
|
21145
|
+
statusA: aEntry.scenarioStatuses?.[id] ?? "unknown",
|
|
21146
|
+
statusB: bEntry.scenarioStatuses?.[id] ?? "unknown"
|
|
21147
|
+
})).filter((item) => item.statusA !== item.statusB);
|
|
21148
|
+
return { environmentA: envA, environmentB: envB, onlyInA, onlyInB, inBoth, statusChanged, aEntry, bEntry };
|
|
21149
|
+
}
|
|
21150
|
+
function countStatuses(run) {
|
|
21151
|
+
const summary = { total: 0, passed: 0, failed: 0, skipped: 0, pending: 0 };
|
|
21152
|
+
for (const tc of run.testCases) {
|
|
21153
|
+
summary.total++;
|
|
21154
|
+
if (tc.status in summary) {
|
|
21155
|
+
summary[tc.status]++;
|
|
21156
|
+
}
|
|
21157
|
+
}
|
|
21158
|
+
return summary;
|
|
21159
|
+
}
|
|
21160
|
+
|
|
20970
21161
|
// src/index.ts
|
|
20971
21162
|
var FORMAT_EXTENSIONS = {
|
|
20972
21163
|
astro: ".md",
|
|
20973
21164
|
"behavior-manifest-json": ".behavior-manifest.json",
|
|
20974
21165
|
markdown: ".md",
|
|
21166
|
+
"release-manifest": ".release-manifest.md",
|
|
20975
21167
|
html: ".html",
|
|
20976
21168
|
"cucumber-html": ".cucumber.html",
|
|
20977
21169
|
junit: ".junit.xml",
|
|
@@ -21010,11 +21202,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
21010
21202
|
const ext = FORMAT_EXTENSIONS[format];
|
|
21011
21203
|
const effectiveName = outputName + (outputNameSuffix ?? "");
|
|
21012
21204
|
if (mode === "aggregated") {
|
|
21013
|
-
return toPosix(
|
|
21205
|
+
return toPosix(path11.join(baseOutputDir, joinNameAndExt(effectiveName, ext)));
|
|
21014
21206
|
}
|
|
21015
21207
|
const normalizedSource = toPosix(sourceFile);
|
|
21016
|
-
const dirOfSource =
|
|
21017
|
-
let baseName =
|
|
21208
|
+
const dirOfSource = path11.posix.dirname(normalizedSource);
|
|
21209
|
+
let baseName = path11.posix.basename(normalizedSource);
|
|
21018
21210
|
for (const testExt of TEST_EXTENSIONS) {
|
|
21019
21211
|
if (baseName.endsWith(testExt)) {
|
|
21020
21212
|
baseName = baseName.slice(0, -testExt.length);
|
|
@@ -21023,12 +21215,12 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
21023
21215
|
}
|
|
21024
21216
|
const fileName = `${baseName}.${effectiveName}${ext}`;
|
|
21025
21217
|
if (colocatedStyle === "adjacent") {
|
|
21026
|
-
return toPosix(
|
|
21218
|
+
return toPosix(path11.posix.join(dirOfSource, fileName));
|
|
21027
21219
|
}
|
|
21028
21220
|
if (colocatedStyle === "flat") {
|
|
21029
|
-
return toPosix(
|
|
21221
|
+
return toPosix(path11.posix.join(baseOutputDir, `${cleanTestStem(normalizedSource)}${ext}`));
|
|
21030
21222
|
}
|
|
21031
|
-
return toPosix(
|
|
21223
|
+
return toPosix(path11.posix.join(baseOutputDir, dirOfSource, fileName));
|
|
21032
21224
|
}
|
|
21033
21225
|
function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
|
|
21034
21226
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -21235,8 +21427,8 @@ var ReportGenerator = class {
|
|
|
21235
21427
|
if (astroPaths) {
|
|
21236
21428
|
for (const mdPath of astroPaths) {
|
|
21237
21429
|
const content = await fsPromises.readFile(mdPath, "utf8");
|
|
21238
|
-
const mdDir =
|
|
21239
|
-
const assetsDir =
|
|
21430
|
+
const mdDir = path11.dirname(mdPath);
|
|
21431
|
+
const assetsDir = path11.resolve(this.options.astro.assetsDir);
|
|
21240
21432
|
const result = copyMarkdownAssets({
|
|
21241
21433
|
markdown: content,
|
|
21242
21434
|
markdownDir: mdDir,
|
|
@@ -21267,9 +21459,9 @@ var ReportGenerator = class {
|
|
|
21267
21459
|
if (groups.size === 0 && this.options.output.mode === "aggregated") {
|
|
21268
21460
|
const ext = FORMAT_EXTENSIONS[format];
|
|
21269
21461
|
const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
|
|
21270
|
-
const outputPath = toPosix(
|
|
21462
|
+
const outputPath = toPosix(path11.join(this.options.outputDir, joinNameAndExt(effectiveName, ext)));
|
|
21271
21463
|
const content = await this.formatContent(run, format);
|
|
21272
|
-
const dir =
|
|
21464
|
+
const dir = path11.dirname(outputPath);
|
|
21273
21465
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
21274
21466
|
await this.deps.writeFile(outputPath, content);
|
|
21275
21467
|
return [outputPath];
|
|
@@ -21281,7 +21473,7 @@ var ReportGenerator = class {
|
|
|
21281
21473
|
testCases
|
|
21282
21474
|
};
|
|
21283
21475
|
const content = await this.formatContent(groupRun, format);
|
|
21284
|
-
const dir =
|
|
21476
|
+
const dir = path11.dirname(outputPath);
|
|
21285
21477
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
21286
21478
|
await this.deps.writeFile(outputPath, content);
|
|
21287
21479
|
writtenPaths.push(outputPath);
|
|
@@ -21390,6 +21582,10 @@ var ReportGenerator = class {
|
|
|
21390
21582
|
});
|
|
21391
21583
|
return formatter.format(run);
|
|
21392
21584
|
}
|
|
21585
|
+
case "release-manifest": {
|
|
21586
|
+
const formatter = new ReleaseManifestFormatter();
|
|
21587
|
+
return formatter.format(run);
|
|
21588
|
+
}
|
|
21393
21589
|
case "story-report-json": {
|
|
21394
21590
|
const formatter = new StoryReportJsonFormatter({
|
|
21395
21591
|
pretty: this.options.storyReportJson.pretty
|
|
@@ -21424,7 +21620,7 @@ async function generateRunComparison(args) {
|
|
|
21424
21620
|
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
21425
21621
|
for (const format of args.formats) {
|
|
21426
21622
|
const ext = format === "html" ? ".html" : ".md";
|
|
21427
|
-
const outputPath = toPosix(
|
|
21623
|
+
const outputPath = toPosix(path11.join(outputDir, `${outputName}${ext}`));
|
|
21428
21624
|
const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
|
|
21429
21625
|
await fsPromises.writeFile(outputPath, content, "utf8");
|
|
21430
21626
|
files.push(outputPath);
|
|
@@ -21459,6 +21655,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
21459
21655
|
MIN_METRIC_SAMPLES,
|
|
21460
21656
|
MIN_PERF_SAMPLES,
|
|
21461
21657
|
MarkdownFormatter,
|
|
21658
|
+
ReleaseManifestFormatter,
|
|
21462
21659
|
ReportGenerator,
|
|
21463
21660
|
ReviewHtmlFormatter,
|
|
21464
21661
|
ReviewMarkdownFormatter,
|
|
@@ -21498,6 +21695,8 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
21498
21695
|
generateTestCaseId,
|
|
21499
21696
|
getAvailableThemes,
|
|
21500
21697
|
getCssOnlyThemes,
|
|
21698
|
+
getDeploymentStatus,
|
|
21699
|
+
getEnvironmentDrift,
|
|
21501
21700
|
gradeEvidence,
|
|
21502
21701
|
hasSufficientHistory,
|
|
21503
21702
|
isReviewableSource,
|
|
@@ -21519,6 +21718,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
21519
21718
|
readBranchName,
|
|
21520
21719
|
readGitSha,
|
|
21521
21720
|
readPackageVersion,
|
|
21721
|
+
recordDeployment,
|
|
21522
21722
|
regenerateArtifacts,
|
|
21523
21723
|
resolveAttachment,
|
|
21524
21724
|
resolveAttachments,
|
|
@@ -21538,6 +21738,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
21538
21738
|
toBehaviorManifest,
|
|
21539
21739
|
toCIInfo,
|
|
21540
21740
|
toRawCIInfo,
|
|
21741
|
+
toReleaseManifest,
|
|
21541
21742
|
toScenarioIndex,
|
|
21542
21743
|
toStoryReport,
|
|
21543
21744
|
tryGetActiveOtelContext,
|