@allurereport/ci 3.19.0 → 3.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/reportContext.js +39 -5
- package/dist/reportMarkdown.d.ts +11 -1
- package/dist/reportMarkdown.js +51 -21
- package/package.json +4 -4
package/dist/reportContext.js
CHANGED
|
@@ -27,6 +27,7 @@ const emptyResolutionStats = () => ({
|
|
|
27
27
|
});
|
|
28
28
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29
29
|
const isTestStatus = (value) => typeof value === "string" && TEST_STATUSES.includes(value);
|
|
30
|
+
const getFiniteNumber = (value, fallback = 0) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
30
31
|
const normalizePath = (filePath) => filePath.split(sep).join("/");
|
|
31
32
|
const readOptionalJson = async (filePath, onError) => {
|
|
32
33
|
if (!existsSync(filePath)) {
|
|
@@ -40,12 +41,43 @@ const readOptionalJson = async (filePath, onError) => {
|
|
|
40
41
|
return undefined;
|
|
41
42
|
}
|
|
42
43
|
};
|
|
44
|
+
const getReportName = (summary) => {
|
|
45
|
+
if (typeof summary.name === "string" && summary.name.trim()) {
|
|
46
|
+
return summary.name;
|
|
47
|
+
}
|
|
48
|
+
return "Allure Report";
|
|
49
|
+
};
|
|
50
|
+
const hasPluginSummarySignal = (value) => typeof value.name === "string" ||
|
|
51
|
+
typeof value.plugin === "string" ||
|
|
52
|
+
typeof value.href === "string" ||
|
|
53
|
+
typeof value.remoteHref === "string" ||
|
|
54
|
+
isRecord(value.stats);
|
|
43
55
|
const isPluginSummary = (value) => {
|
|
44
|
-
if (!isRecord(value) ||
|
|
56
|
+
if (!isRecord(value) || !hasPluginSummarySignal(value)) {
|
|
45
57
|
return false;
|
|
46
58
|
}
|
|
47
|
-
return
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
61
|
+
const normalizePluginSummaryStats = (summary) => {
|
|
62
|
+
const rawStats = isRecord(summary.stats) ? summary.stats : {};
|
|
63
|
+
const stats = {
|
|
64
|
+
...rawStats,
|
|
65
|
+
failed: getFiniteNumber(rawStats.failed),
|
|
66
|
+
broken: getFiniteNumber(rawStats.broken),
|
|
67
|
+
passed: getFiniteNumber(rawStats.passed),
|
|
68
|
+
skipped: getFiniteNumber(rawStats.skipped),
|
|
69
|
+
unknown: getFiniteNumber(rawStats.unknown),
|
|
70
|
+
};
|
|
71
|
+
stats.total = getFiniteNumber(rawStats.total, TEST_STATUSES.reduce((acc, status) => acc + getFiniteNumber(stats[status]), 0));
|
|
72
|
+
return stats;
|
|
48
73
|
};
|
|
74
|
+
const normalizePluginSummary = (summary) => ({
|
|
75
|
+
...summary,
|
|
76
|
+
name: getReportName(summary),
|
|
77
|
+
stats: normalizePluginSummaryStats(summary),
|
|
78
|
+
status: isTestStatus(summary.status) ? summary.status : "passed",
|
|
79
|
+
duration: getFiniteNumber(summary.duration),
|
|
80
|
+
});
|
|
49
81
|
const getReportPath = (reportDir, summaryFile) => normalizePath(relative(resolve(reportDir), dirname(resolve(summaryFile))));
|
|
50
82
|
const readPluginSummary = async (reportDir, filePath, onError) => {
|
|
51
83
|
const value = await readOptionalJson(filePath, onError);
|
|
@@ -57,7 +89,7 @@ const readPluginSummary = async (reportDir, filePath, onError) => {
|
|
|
57
89
|
return undefined;
|
|
58
90
|
}
|
|
59
91
|
return {
|
|
60
|
-
...value,
|
|
92
|
+
...normalizePluginSummary(value),
|
|
61
93
|
summaryFile: filePath,
|
|
62
94
|
reportPath: getReportPath(reportDir, filePath),
|
|
63
95
|
};
|
|
@@ -204,8 +236,10 @@ const createEnvironmentContext = (registry, summaries) => {
|
|
|
204
236
|
});
|
|
205
237
|
return [...environmentsByName.values()].toSorted((left, right) => left.name.localeCompare(right.name));
|
|
206
238
|
};
|
|
207
|
-
const createReport = (summary) => (
|
|
208
|
-
const sortReports = (reports) => reports
|
|
239
|
+
const createReport = (summary) => normalizePluginSummary(summary);
|
|
240
|
+
const sortReports = (reports) => reports
|
|
241
|
+
.map((report) => normalizePluginSummary(report))
|
|
242
|
+
.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
209
243
|
const getResolutionStats = (summaries) => {
|
|
210
244
|
const stats = emptyResolutionStats();
|
|
211
245
|
summaries.forEach((summary) => {
|
package/dist/reportMarkdown.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import type { ReportContext } from "./reportContext.js";
|
|
1
|
+
import type { ReportContext, ReportContextFlagStats, ReportContextTestReport, ReportContextResolutionStats, ReportContextStatusStats } from "./reportContext.js";
|
|
2
|
+
export type ReportSummaryMarkdownRow = {
|
|
3
|
+
kind: "total" | "environment" | "report";
|
|
4
|
+
name: string;
|
|
5
|
+
duration: number;
|
|
6
|
+
stats: ReportContextStatusStats;
|
|
7
|
+
flags: ReportContextFlagStats;
|
|
8
|
+
resolutions?: ReportContextResolutionStats;
|
|
9
|
+
report?: ReportContextTestReport;
|
|
10
|
+
};
|
|
2
11
|
export type RenderReportSummaryMarkdownOptions = {
|
|
3
12
|
title?: string;
|
|
4
13
|
includeArtifacts?: boolean;
|
|
14
|
+
getReportFilterHref?: (filter: keyof ReportContextFlagStats, row: ReportSummaryMarkdownRow) => string | undefined;
|
|
5
15
|
};
|
|
6
16
|
export declare const renderReportSummaryMarkdown: (context: ReportContext, options?: RenderReportSummaryMarkdownOptions) => string;
|
package/dist/reportMarkdown.js
CHANGED
|
@@ -10,6 +10,7 @@ const STATUS_ICON_BASE_URL = "https://allurecharts.qameta.workers.dev/dot";
|
|
|
10
10
|
const STATUS_PIE_BASE_URL = "https://allurecharts.qameta.workers.dev/pie";
|
|
11
11
|
const escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
12
12
|
const tableCell = (value) => escapeHtml(String(value)).replaceAll("|", "|").replaceAll("\n", "<br>");
|
|
13
|
+
const inlineCode = (value) => `<code>${escapeHtml(value)}</code>`;
|
|
13
14
|
const isSafeHref = (href) => {
|
|
14
15
|
try {
|
|
15
16
|
const url = new URL(href);
|
|
@@ -82,11 +83,19 @@ const formatResolutions = (resolutions) => {
|
|
|
82
83
|
return lines.join("<br>");
|
|
83
84
|
};
|
|
84
85
|
const hasResolutions = (resolutions) => resolutions.issues > 0 || resolutions.muted > 0 || resolutions.accepted > 0;
|
|
85
|
-
const formatFlag = (
|
|
86
|
-
const
|
|
86
|
+
const formatFlag = (flag, row, options) => {
|
|
87
|
+
const value = row.flags[flag];
|
|
88
|
+
if (value <= 0) {
|
|
89
|
+
return "0";
|
|
90
|
+
}
|
|
91
|
+
const label = String(value);
|
|
92
|
+
const href = options.getReportFilterHref?.(flag, row);
|
|
93
|
+
return href ? link(label, href) : label;
|
|
94
|
+
};
|
|
95
|
+
const renderTable = (rows, includeResolutions, options) => {
|
|
87
96
|
const headers = [
|
|
88
97
|
" ",
|
|
89
|
-
"
|
|
98
|
+
"Environment",
|
|
90
99
|
"Duration",
|
|
91
100
|
"Stats",
|
|
92
101
|
...(includeResolutions ? ["Resolutions"] : []),
|
|
@@ -101,14 +110,31 @@ const renderTable = (rows, includeResolutions) => {
|
|
|
101
110
|
tableCell(formatDuration(row.duration)),
|
|
102
111
|
formatStats(row.stats),
|
|
103
112
|
...(includeResolutions ? [formatResolutions(row.resolutions)] : []),
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
formatFlag("new", row, options),
|
|
114
|
+
formatFlag("flaky", row, options),
|
|
115
|
+
formatFlag("retry", row, options),
|
|
107
116
|
].join(" | "));
|
|
108
117
|
return [`| ${headers.join(" | ")} |`, `| ${divider.join(" | ")} |`, ...body.map((row) => `| ${row} |`)].join("\n");
|
|
109
118
|
};
|
|
110
119
|
const reportHref = (report) => report.remoteHref ?? report.href;
|
|
111
120
|
const reportLabel = (report) => report.plugin ?? report.name;
|
|
121
|
+
const isAwesomeReport = (report) => report?.pluginId?.toLowerCase() === "awesome" || report?.plugin?.toLowerCase() === "awesome";
|
|
122
|
+
const appendReportFilter = (href, filter) => {
|
|
123
|
+
const hashIndex = href.indexOf("#");
|
|
124
|
+
const base = hashIndex === -1 ? href : href.slice(0, hashIndex);
|
|
125
|
+
const hash = hashIndex === -1 ? "" : href.slice(hashIndex);
|
|
126
|
+
const separator = base.includes("?") ? "&" : "?";
|
|
127
|
+
const query = filter === "new" ? "transition=new" : `${filter}=true`;
|
|
128
|
+
return `${base}${separator}${query}${hash}`;
|
|
129
|
+
};
|
|
130
|
+
const createDefaultReportFilterHref = (reports) => {
|
|
131
|
+
const defaultReport = reports.find(isAwesomeReport);
|
|
132
|
+
return (filter, row) => {
|
|
133
|
+
const report = isAwesomeReport(row.report) ? row.report : defaultReport;
|
|
134
|
+
const href = report ? reportHref(report) : undefined;
|
|
135
|
+
return href ? appendReportFilter(href, filter) : undefined;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
112
138
|
const reportLinkKind = (report) => report.plugin?.toLowerCase() === "testops" ? "testops" : "report";
|
|
113
139
|
const toReportLink = (report) => {
|
|
114
140
|
const href = reportHref(report);
|
|
@@ -151,43 +177,46 @@ const pluginSummaryToResolutions = (report) => ({
|
|
|
151
177
|
muted: report.stats.resolutions?.muted ?? 0,
|
|
152
178
|
accepted: report.stats.resolutions?.accepted ?? 0,
|
|
153
179
|
});
|
|
154
|
-
const renderFilteredReports = (reports) => {
|
|
180
|
+
const renderFilteredReports = (reports, options) => {
|
|
155
181
|
const rows = reports.map((report) => ({
|
|
182
|
+
kind: "report",
|
|
156
183
|
name: report.name,
|
|
157
184
|
duration: report.duration,
|
|
158
185
|
stats: pluginSummaryToStatusStats(report),
|
|
159
186
|
flags: pluginSummaryToFlags(report),
|
|
160
187
|
resolutions: pluginSummaryToResolutions(report),
|
|
188
|
+
report,
|
|
161
189
|
}));
|
|
162
190
|
if (!rows.length) {
|
|
163
191
|
return undefined;
|
|
164
192
|
}
|
|
165
193
|
const includeResolutions = rows.some(({ resolutions }) => resolutions && hasResolutions(resolutions));
|
|
166
194
|
const links = renderReportLinks(reports);
|
|
167
|
-
return ["**Filtered Reports**", renderTable(rows, includeResolutions), ...links].join("\n\n");
|
|
195
|
+
return ["**Filtered Reports**", renderTable(rows, includeResolutions, options), ...links].join("\n\n");
|
|
168
196
|
};
|
|
169
197
|
const renderArtifacts = (artifacts) => {
|
|
170
198
|
if (!artifacts.length) {
|
|
171
199
|
return undefined;
|
|
172
200
|
}
|
|
173
|
-
const rows = artifacts.map(({ name, path }) =>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
...rows,
|
|
181
|
-
"",
|
|
182
|
-
"</details>",
|
|
183
|
-
].join("\n");
|
|
201
|
+
const rows = artifacts.map(({ name, path }) => {
|
|
202
|
+
const normalizedPath = path.replaceAll("\\", "/");
|
|
203
|
+
const isDuplicateName = name === path || normalizedPath.endsWith(`/${name}`);
|
|
204
|
+
const label = isDuplicateName ? inlineCode(path) : `${inlineCode(name)} — ${inlineCode(path)}`;
|
|
205
|
+
return `- ${label}`;
|
|
206
|
+
});
|
|
207
|
+
return [`<details>`, `<summary>Artifacts used (${artifacts.length})</summary>`, "", ...rows, "", "</details>"].join("\n");
|
|
184
208
|
};
|
|
185
209
|
export const renderReportSummaryMarkdown = (context, options = {}) => {
|
|
186
210
|
const { title = "Allure Report Summary", includeArtifacts = true } = options;
|
|
187
211
|
const regularReports = context.reports.filter((report) => report.filtered !== true);
|
|
188
212
|
const filteredReports = context.reports.filter((report) => report.filtered === true);
|
|
213
|
+
const renderOptions = {
|
|
214
|
+
...options,
|
|
215
|
+
getReportFilterHref: options.getReportFilterHref ?? createDefaultReportFilterHref(regularReports),
|
|
216
|
+
};
|
|
189
217
|
const aggregateRows = [
|
|
190
218
|
{
|
|
219
|
+
kind: "total",
|
|
191
220
|
name: "All tests",
|
|
192
221
|
duration: context.totals.duration,
|
|
193
222
|
stats: context.totals.stats,
|
|
@@ -195,6 +224,7 @@ export const renderReportSummaryMarkdown = (context, options = {}) => {
|
|
|
195
224
|
resolutions: context.totals.resolutions,
|
|
196
225
|
},
|
|
197
226
|
...context.environments.map((environment) => ({
|
|
227
|
+
kind: "environment",
|
|
198
228
|
name: environment.name,
|
|
199
229
|
duration: environment.duration,
|
|
200
230
|
stats: environment.stats,
|
|
@@ -204,9 +234,9 @@ export const renderReportSummaryMarkdown = (context, options = {}) => {
|
|
|
204
234
|
const includeResolutions = hasResolutions(context.totals.resolutions);
|
|
205
235
|
const sections = [
|
|
206
236
|
`# ${escapeHtml(title)}`,
|
|
207
|
-
renderTable(aggregateRows, includeResolutions),
|
|
237
|
+
renderTable(aggregateRows, includeResolutions, renderOptions),
|
|
208
238
|
...renderReportLinks(regularReports),
|
|
209
|
-
renderFilteredReports(filteredReports),
|
|
239
|
+
renderFilteredReports(filteredReports, renderOptions),
|
|
210
240
|
includeArtifacts ? renderArtifacts(context.artifacts) : undefined,
|
|
211
241
|
].filter((section) => Boolean(section));
|
|
212
242
|
return `${sections.join("\n\n")}\n`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/ci",
|
|
3
|
-
"version": "3.19.
|
|
3
|
+
"version": "3.19.1",
|
|
4
4
|
"description": "Utilities set to get useful report data from different CI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@allurereport/core-api": "3.19.
|
|
36
|
-
"@allurereport/plugin-api": "3.19.
|
|
35
|
+
"@allurereport/core-api": "3.19.1",
|
|
36
|
+
"@allurereport/plugin-api": "3.19.1",
|
|
37
37
|
"axios": "^1.20.0",
|
|
38
38
|
"glob": "^13.0.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^20",
|
|
42
42
|
"@vitest/runner": "^2.1.8",
|
|
43
|
-
"@vitest/snapshot": "^
|
|
43
|
+
"@vitest/snapshot": "^5.0.1",
|
|
44
44
|
"allure-js-commons": "^3",
|
|
45
45
|
"allure-vitest": "^3",
|
|
46
46
|
"rimraf": "^6",
|