allure 3.17.0 → 3.19.0
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/commands/allure2.d.ts +1 -0
- package/dist/commands/allure2.js +4 -0
- package/dist/commands/awesome.d.ts +1 -0
- package/dist/commands/awesome.js +4 -0
- package/dist/commands/classic.d.ts +1 -0
- package/dist/commands/classic.js +4 -0
- package/dist/commands/commons/generate.d.ts +6 -1
- package/dist/commands/commons/generate.js +18 -0
- package/dist/commands/commons/run.js +58 -20
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.js +4 -0
- package/dist/commands/gitlab/generate.d.ts +15 -0
- package/dist/commands/gitlab/generate.js +137 -0
- package/dist/commands/gitlab/index.d.ts +1 -0
- package/dist/commands/gitlab/index.js +1 -0
- package/dist/commands/history.d.ts +1 -0
- package/dist/commands/history.js +5 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/log.d.ts +2 -0
- package/dist/commands/log.js +21 -1
- package/dist/commands/qualityGate.js +2 -2
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +5 -1
- package/dist/index.js +2 -1
- package/package.json +22 -22
|
@@ -10,6 +10,7 @@ export declare class Allure2Command extends Command {
|
|
|
10
10
|
reportLanguage: string | undefined;
|
|
11
11
|
singleFile: boolean | undefined;
|
|
12
12
|
historyPath: string | undefined;
|
|
13
|
+
historyBaseUrl: string | undefined;
|
|
13
14
|
knownIssues: string | undefined;
|
|
14
15
|
execute(): Promise<void>;
|
|
15
16
|
}
|
package/dist/commands/allure2.js
CHANGED
|
@@ -33,6 +33,9 @@ export class Allure2Command extends Command {
|
|
|
33
33
|
this.historyPath = Option.String("--history-path,-h", {
|
|
34
34
|
description: "The path to history file",
|
|
35
35
|
});
|
|
36
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
37
|
+
description: "The public base URL of the generated report directory",
|
|
38
|
+
});
|
|
36
39
|
this.knownIssues = Option.String("--known-issues", {
|
|
37
40
|
description: "Path to known issues file",
|
|
38
41
|
});
|
|
@@ -49,6 +52,7 @@ export class Allure2Command extends Command {
|
|
|
49
52
|
name: this.reportName,
|
|
50
53
|
resolutions: { knownIssuesPath: this.knownIssues },
|
|
51
54
|
historyPath: this.historyPath,
|
|
55
|
+
...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
|
|
52
56
|
});
|
|
53
57
|
const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
|
|
54
58
|
if (!resultDirectories.length) {
|
|
@@ -12,6 +12,7 @@ export declare class AwesomeCommand extends Command {
|
|
|
12
12
|
theme: string | undefined;
|
|
13
13
|
reportLanguage: string | undefined;
|
|
14
14
|
historyPath: string | undefined;
|
|
15
|
+
historyBaseUrl: string | undefined;
|
|
15
16
|
knownIssues: string | undefined;
|
|
16
17
|
groupBy: string | undefined;
|
|
17
18
|
hideLabels: string[] | undefined;
|
package/dist/commands/awesome.js
CHANGED
|
@@ -39,6 +39,9 @@ export class AwesomeCommand extends Command {
|
|
|
39
39
|
this.historyPath = Option.String("--history-path,-h", {
|
|
40
40
|
description: "The path to history file",
|
|
41
41
|
});
|
|
42
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
43
|
+
description: "The public base URL of the generated report directory",
|
|
44
|
+
});
|
|
42
45
|
this.knownIssues = Option.String("--known-issues", {
|
|
43
46
|
description: "Path to known issues file",
|
|
44
47
|
});
|
|
@@ -58,6 +61,7 @@ export class AwesomeCommand extends Command {
|
|
|
58
61
|
name: this.reportName,
|
|
59
62
|
resolutions: { knownIssuesPath: this.knownIssues },
|
|
60
63
|
historyPath: this.historyPath,
|
|
64
|
+
...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
|
|
61
65
|
hideLabels,
|
|
62
66
|
});
|
|
63
67
|
const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
|
|
@@ -10,6 +10,7 @@ export declare class ClassicCommand extends Command {
|
|
|
10
10
|
reportLanguage: string | undefined;
|
|
11
11
|
singleFile: boolean | undefined;
|
|
12
12
|
historyPath: string | undefined;
|
|
13
|
+
historyBaseUrl: string | undefined;
|
|
13
14
|
knownIssues: string | undefined;
|
|
14
15
|
execute(): Promise<void>;
|
|
15
16
|
}
|
package/dist/commands/classic.js
CHANGED
|
@@ -33,6 +33,9 @@ export class ClassicCommand extends Command {
|
|
|
33
33
|
this.historyPath = Option.String("--history-path,-h", {
|
|
34
34
|
description: "The path to history file",
|
|
35
35
|
});
|
|
36
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
37
|
+
description: "The public base URL of the generated report directory",
|
|
38
|
+
});
|
|
36
39
|
this.knownIssues = Option.String("--known-issues", {
|
|
37
40
|
description: "Path to known issues file",
|
|
38
41
|
});
|
|
@@ -49,6 +52,7 @@ export class ClassicCommand extends Command {
|
|
|
49
52
|
name: this.reportName,
|
|
50
53
|
resolutions: { knownIssuesPath: this.knownIssues },
|
|
51
54
|
historyPath: this.historyPath,
|
|
55
|
+
...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
|
|
52
56
|
});
|
|
53
57
|
const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
|
|
54
58
|
if (!resultDirectories.length) {
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { type ReportContext } from "@allurereport/ci";
|
|
1
2
|
import type { FullConfig } from "@allurereport/core";
|
|
3
|
+
export type GenerateResult = {
|
|
4
|
+
summary?: ReportContext;
|
|
5
|
+
};
|
|
2
6
|
export declare const generate: (params: {
|
|
3
7
|
cwd: string;
|
|
4
8
|
config: FullConfig;
|
|
5
9
|
resultsDir?: string[];
|
|
6
10
|
dump?: string[];
|
|
7
|
-
|
|
11
|
+
collectSummary?: boolean;
|
|
12
|
+
}) => Promise<GenerateResult | undefined>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { exit } from "node:process";
|
|
2
|
+
import { createReportContext } from "@allurereport/ci";
|
|
2
3
|
import { AllureReport } from "@allurereport/core";
|
|
3
4
|
import { KnownError } from "@allurereport/service";
|
|
4
5
|
import { red } from "yoctocolors";
|
|
@@ -26,6 +27,23 @@ export const generate = async (params) => {
|
|
|
26
27
|
await allureReport.readDirectory(dir);
|
|
27
28
|
}
|
|
28
29
|
await allureReport.done();
|
|
30
|
+
if (!params.collectSummary) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const result = {};
|
|
34
|
+
try {
|
|
35
|
+
const summary = await createReportContext(params.config.output, {
|
|
36
|
+
onError: (message) => console.warn(message),
|
|
37
|
+
});
|
|
38
|
+
if (!summary.testResults && summary.reports.length === 0) {
|
|
39
|
+
throw new Error("no report context data found");
|
|
40
|
+
}
|
|
41
|
+
result.summary = summary;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.warn(`Report summary skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
29
47
|
}
|
|
30
48
|
catch (error) {
|
|
31
49
|
if (error instanceof KnownError) {
|
|
@@ -4,7 +4,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
6
|
import process from "node:process";
|
|
7
|
-
import { QualityGateState, stringifyQualityGateResults } from "@allurereport/core";
|
|
7
|
+
import { QualityGateState, filterFailedQualityGateResults, stringifyQualityGateResults, } from "@allurereport/core";
|
|
8
8
|
import { createTestPlan } from "@allurereport/core-api";
|
|
9
9
|
import { allureResultsDirectoriesWatcher, delayedFileProcessingWatcher, newFilesInDirectoryWatcher, } from "@allurereport/directory-watcher";
|
|
10
10
|
import { formatProcessLogAttachmentName } from "@allurereport/plugin-agent";
|
|
@@ -159,6 +159,27 @@ export const runTests = async (params) => {
|
|
|
159
159
|
fastFailed: fastFailTriggered,
|
|
160
160
|
};
|
|
161
161
|
};
|
|
162
|
+
const publishProcessGlobals = (params) => {
|
|
163
|
+
const { allureReport, command, commandArgs, ignoreLogs, processFailed, testProcessResult } = params;
|
|
164
|
+
if (!ignoreLogs && testProcessResult?.stdout) {
|
|
165
|
+
const fileName = randomUUID();
|
|
166
|
+
const stdoutResultFile = new BufferResultFile(Buffer.from(testProcessResult.stdout, "utf8"), `${fileName}`);
|
|
167
|
+
stdoutResultFile.contentType = "text/plain";
|
|
168
|
+
allureReport.realtimeDispatcher.sendProcessGlobalAttachment(stdoutResultFile, formatProcessLogAttachmentName([command, ...commandArgs].join(" "), "stdout"));
|
|
169
|
+
}
|
|
170
|
+
if (!ignoreLogs && testProcessResult?.stderr) {
|
|
171
|
+
const fileName = randomUUID();
|
|
172
|
+
const stderrResultFile = new BufferResultFile(Buffer.from(testProcessResult.stderr, "utf8"), fileName);
|
|
173
|
+
stderrResultFile.contentType = "text/plain";
|
|
174
|
+
allureReport.realtimeDispatcher.sendProcessGlobalAttachment(stderrResultFile, formatProcessLogAttachmentName([command, ...commandArgs].join(" "), "stderr"));
|
|
175
|
+
if (processFailed) {
|
|
176
|
+
allureReport.realtimeDispatcher.sendProcessGlobalError({
|
|
177
|
+
message: "Test process has failed",
|
|
178
|
+
trace: testProcessResult.stderr,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
162
183
|
export const executeAllureRun = async (params) => {
|
|
163
184
|
const { allureReport, cwd, command, commandArgs, environmentVariables = {}, environment, withQualityGate, silent, logs, ignoreLogs, maxRerun = 0, logProcessExit = true, resultsPatterns = [], } = params;
|
|
164
185
|
await allureReport.start();
|
|
@@ -187,6 +208,16 @@ export const executeAllureRun = async (params) => {
|
|
|
187
208
|
if (allFailuresAfterInitialRun.length > 0 && blockingFailuresAfterInitialRun.length === 0 && testProcessResult) {
|
|
188
209
|
globalExitCode.actual = 0;
|
|
189
210
|
}
|
|
211
|
+
if (maxRerun > 0) {
|
|
212
|
+
publishProcessGlobals({
|
|
213
|
+
allureReport,
|
|
214
|
+
command,
|
|
215
|
+
commandArgs,
|
|
216
|
+
ignoreLogs,
|
|
217
|
+
processFailed: Math.abs(globalExitCode.actual ?? testProcessResult?.code ?? -1) !== 0,
|
|
218
|
+
testProcessResult,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
190
221
|
for (let rerun = 0; rerun < maxRerun && testProcessResult && !testProcessResult.fastFailed; rerun++) {
|
|
191
222
|
const failed = await allureReport.store.blockingFailedTestResults();
|
|
192
223
|
if (failed.length === 0) {
|
|
@@ -199,6 +230,7 @@ export const executeAllureRun = async (params) => {
|
|
|
199
230
|
const tmpDir = await mkdtemp(join(tmpdir(), "allure-run-"));
|
|
200
231
|
const testPlanPath = resolve(tmpDir, `${rerun}-testplan.json`);
|
|
201
232
|
await writeFile(testPlanPath, JSON.stringify(testPlan));
|
|
233
|
+
allureReport.realtimeDispatcher.sendProcessGlobalsReset();
|
|
202
234
|
testProcessResult = await runTests({
|
|
203
235
|
silent,
|
|
204
236
|
logs,
|
|
@@ -217,6 +249,18 @@ export const executeAllureRun = async (params) => {
|
|
|
217
249
|
resultsPatterns,
|
|
218
250
|
});
|
|
219
251
|
await rm(tmpDir, { recursive: true });
|
|
252
|
+
const allFailuresAfterRerun = await allureReport.store.failedTestResults();
|
|
253
|
+
const blockingFailuresAfterRerun = await allureReport.store.blockingFailedTestResults();
|
|
254
|
+
publishProcessGlobals({
|
|
255
|
+
allureReport,
|
|
256
|
+
command,
|
|
257
|
+
commandArgs,
|
|
258
|
+
ignoreLogs,
|
|
259
|
+
processFailed: Math.abs(allFailuresAfterRerun.length > 0 && blockingFailuresAfterRerun.length === 0
|
|
260
|
+
? 0
|
|
261
|
+
: (testProcessResult?.code ?? -1)) !== 0,
|
|
262
|
+
testProcessResult,
|
|
263
|
+
});
|
|
220
264
|
logTests(await allureReport.store.allTestResults());
|
|
221
265
|
}
|
|
222
266
|
const allFailuresAfterReruns = await allureReport.store.failedTestResults();
|
|
@@ -235,12 +279,14 @@ export const executeAllureRun = async (params) => {
|
|
|
235
279
|
}
|
|
236
280
|
if (qualityGateResults.length) {
|
|
237
281
|
const qualityGateMessage = stringifyQualityGateResults(qualityGateResults);
|
|
238
|
-
|
|
282
|
+
if (qualityGateMessage) {
|
|
283
|
+
console.error(qualityGateMessage);
|
|
284
|
+
}
|
|
239
285
|
allureReport.realtimeDispatcher.sendQualityGateResults(qualityGateResults);
|
|
240
286
|
}
|
|
241
287
|
globalExitCode.original = testProcessResult?.code ?? -1;
|
|
242
288
|
if (withQualityGate) {
|
|
243
|
-
globalExitCode.actual = qualityGateResults.length > 0 ? 1 : 0;
|
|
289
|
+
globalExitCode.actual = filterFailedQualityGateResults(qualityGateResults).length > 0 ? 1 : 0;
|
|
244
290
|
}
|
|
245
291
|
}
|
|
246
292
|
catch (error) {
|
|
@@ -260,23 +306,15 @@ export const executeAllureRun = async (params) => {
|
|
|
260
306
|
}
|
|
261
307
|
}
|
|
262
308
|
const processFailed = Math.abs(globalExitCode.actual ?? globalExitCode.original) !== 0;
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
stderrResultFile.contentType = "text/plain";
|
|
273
|
-
allureReport.realtimeDispatcher.sendGlobalAttachment(stderrResultFile, formatProcessLogAttachmentName([command, ...commandArgs].join(" "), "stderr"));
|
|
274
|
-
if (processFailed) {
|
|
275
|
-
allureReport.realtimeDispatcher.sendGlobalError({
|
|
276
|
-
message: "Test process has failed",
|
|
277
|
-
trace: testProcessResult.stderr,
|
|
278
|
-
});
|
|
279
|
-
}
|
|
309
|
+
if (maxRerun === 0) {
|
|
310
|
+
publishProcessGlobals({
|
|
311
|
+
allureReport,
|
|
312
|
+
command,
|
|
313
|
+
commandArgs,
|
|
314
|
+
ignoreLogs,
|
|
315
|
+
processFailed,
|
|
316
|
+
testProcessResult,
|
|
317
|
+
});
|
|
280
318
|
}
|
|
281
319
|
allureReport.realtimeDispatcher.sendGlobalExitCode(globalExitCode);
|
|
282
320
|
await allureReport.done();
|
|
@@ -11,6 +11,7 @@ export declare class GenerateCommand extends Command {
|
|
|
11
11
|
open: boolean | undefined;
|
|
12
12
|
port: string | undefined;
|
|
13
13
|
historyLimit: string | undefined;
|
|
14
|
+
historyBaseUrl: string | undefined;
|
|
14
15
|
hideLabels: string[] | undefined;
|
|
15
16
|
knownIssues: string | undefined;
|
|
16
17
|
execute(): Promise<void>;
|
|
@@ -35,6 +35,9 @@ export class GenerateCommand extends Command {
|
|
|
35
35
|
this.historyLimit = Option.String("--history-limit", {
|
|
36
36
|
description: "Limits the number of history entries to keep (default: unlimited)",
|
|
37
37
|
});
|
|
38
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
39
|
+
description: "The public base URL of the generated report directory",
|
|
40
|
+
});
|
|
38
41
|
this.hideLabels = Option.Array("--hide-labels", {
|
|
39
42
|
description: "Hide labels by exact name in generated reports. Repeat the option for multiple labels",
|
|
40
43
|
});
|
|
@@ -53,6 +56,7 @@ export class GenerateCommand extends Command {
|
|
|
53
56
|
port: this.port,
|
|
54
57
|
hideLabels,
|
|
55
58
|
historyLimit: this.historyLimit !== undefined ? parseInt(this.historyLimit, 10) : undefined,
|
|
59
|
+
...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
|
|
56
60
|
resolutions: { knownIssuesPath: this.knownIssues },
|
|
57
61
|
});
|
|
58
62
|
await generate({
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from "clipanion";
|
|
2
|
+
export declare class GitlabGenerateCommand extends Command {
|
|
3
|
+
static paths: string[][];
|
|
4
|
+
static usage: import("clipanion").Usage;
|
|
5
|
+
resultsDir: string[];
|
|
6
|
+
config: string | undefined;
|
|
7
|
+
output: string | undefined;
|
|
8
|
+
reportName: string | undefined;
|
|
9
|
+
dump: string[] | undefined;
|
|
10
|
+
historyPath: string | undefined;
|
|
11
|
+
historyLimit: string | undefined;
|
|
12
|
+
historyBaseUrl: string | undefined;
|
|
13
|
+
gitlabToken: string | undefined;
|
|
14
|
+
execute(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { cwd as processCwd } from "node:process";
|
|
4
|
+
import { restoreGitlabHistory, upsertGitlabJobNote, detect } from "@allurereport/ci";
|
|
5
|
+
import { readConfig } from "@allurereport/core";
|
|
6
|
+
import { CiType } from "@allurereport/core-api";
|
|
7
|
+
import { Command, Option } from "clipanion";
|
|
8
|
+
import { generate } from "../commons/generate.js";
|
|
9
|
+
const DEFAULT_HISTORY_LIMIT = 100;
|
|
10
|
+
const isDecimalString = (value) => /^[0-9]+$/.test(value);
|
|
11
|
+
const parseHistoryLimit = (value) => {
|
|
12
|
+
if (typeof value === "string") {
|
|
13
|
+
if (!isDecimalString(value)) {
|
|
14
|
+
return DEFAULT_HISTORY_LIMIT;
|
|
15
|
+
}
|
|
16
|
+
const parsed = Number(value);
|
|
17
|
+
return Number.isFinite(parsed) && Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_HISTORY_LIMIT;
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "number") {
|
|
20
|
+
return Number.isFinite(value) && Number.isInteger(value) && value >= 0 ? value : DEFAULT_HISTORY_LIMIT;
|
|
21
|
+
}
|
|
22
|
+
return DEFAULT_HISTORY_LIMIT;
|
|
23
|
+
};
|
|
24
|
+
const reportBaseUrl = (historyBaseUrl, output) => {
|
|
25
|
+
const gitlab = detect();
|
|
26
|
+
const isGitlabCiDescriptor = (ci) => ci.type === CiType.Gitlab;
|
|
27
|
+
if (!isGitlabCiDescriptor(gitlab)) {
|
|
28
|
+
throw new Error("GitLab CI environment was not detected");
|
|
29
|
+
}
|
|
30
|
+
if (isAbsolute(output)) {
|
|
31
|
+
const projectDirectory = resolve(gitlab.projectDirectory);
|
|
32
|
+
const absoluteOutput = resolve(output);
|
|
33
|
+
if (absoluteOutput !== projectDirectory && !absoluteOutput.startsWith(join(projectDirectory, sep))) {
|
|
34
|
+
throw new Error("Absolute output path must be within CI_PROJECT_DIR");
|
|
35
|
+
}
|
|
36
|
+
output = relative(projectDirectory, absoluteOutput).split(sep).join("/");
|
|
37
|
+
}
|
|
38
|
+
return historyBaseUrl || `${gitlab.jobArtifactsUrlBase}/${output}`;
|
|
39
|
+
};
|
|
40
|
+
export class GitlabGenerateCommand extends Command {
|
|
41
|
+
constructor() {
|
|
42
|
+
super(...arguments);
|
|
43
|
+
this.resultsDir = Option.Rest({
|
|
44
|
+
name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
|
|
45
|
+
});
|
|
46
|
+
this.config = Option.String("--config,-c", {
|
|
47
|
+
description: "The path to Allure config file",
|
|
48
|
+
});
|
|
49
|
+
this.output = Option.String("--output,-o", {
|
|
50
|
+
description: "The output directory name. Absolute paths are accepted as well (default: allure-report)",
|
|
51
|
+
});
|
|
52
|
+
this.reportName = Option.String("--report-name,--name", {
|
|
53
|
+
description: "The report name (default: Allure Report)",
|
|
54
|
+
});
|
|
55
|
+
this.dump = Option.Array("--dump", {
|
|
56
|
+
description: "Path or pattern that matches one or more archives created by `allure run --dump ...`. " +
|
|
57
|
+
"Allure loads the matched archives before generating the report. " +
|
|
58
|
+
"This option can be specified multiple times.",
|
|
59
|
+
});
|
|
60
|
+
this.historyPath = Option.String("--history-path", {
|
|
61
|
+
description: "History file path",
|
|
62
|
+
});
|
|
63
|
+
this.historyLimit = Option.String("--history-limit", {
|
|
64
|
+
description: "Limits the number of history entries to keep (default: 100)",
|
|
65
|
+
});
|
|
66
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
67
|
+
description: "The public base URL of the generated report directory",
|
|
68
|
+
});
|
|
69
|
+
this.gitlabToken = Option.String("--gitlab-token", {
|
|
70
|
+
description: "GitLab api token with api write permissions",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async execute() {
|
|
74
|
+
const cwd = processCwd();
|
|
75
|
+
const token = this.gitlabToken?.trim() || process.env.GITLAB_TOKEN?.trim() || undefined;
|
|
76
|
+
const output = this.output ?? "allure-report";
|
|
77
|
+
const configPath = this.config && existsSync(this.config) ? this.config : undefined;
|
|
78
|
+
const historyPath = this.historyPath === undefined ? "history.jsonl" : this.historyPath;
|
|
79
|
+
const config = await readConfig(cwd, configPath, {
|
|
80
|
+
output,
|
|
81
|
+
name: this.reportName,
|
|
82
|
+
historyPath: historyPath,
|
|
83
|
+
historyBaseUrl: reportBaseUrl(this.historyBaseUrl, output),
|
|
84
|
+
historyLimit: parseHistoryLimit(this.historyLimit),
|
|
85
|
+
});
|
|
86
|
+
const runGitlabOperation = async (errPrefix, operation) => {
|
|
87
|
+
try {
|
|
88
|
+
await operation();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error(`${errPrefix}: ${error instanceof Error ? error.message : String(error)}`);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
console.log("Generating allure report");
|
|
95
|
+
if (!config.allureService?.accessToken) {
|
|
96
|
+
console.log(" fetching previous run history");
|
|
97
|
+
await runGitlabOperation(" history fetch failed", () => restoreGitlabHistory({
|
|
98
|
+
token,
|
|
99
|
+
historyPath,
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
const result = await generate({
|
|
103
|
+
cwd,
|
|
104
|
+
config,
|
|
105
|
+
dump: this.dump,
|
|
106
|
+
resultsDir: this.resultsDir,
|
|
107
|
+
collectSummary: true,
|
|
108
|
+
});
|
|
109
|
+
if (!result) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const reportUrl = `${config.historyBaseUrl}/index.html`;
|
|
113
|
+
console.log(`GitLab report URL: ${reportUrl}`);
|
|
114
|
+
if (result.summary && existsSync(join(config.output, "index.html"))) {
|
|
115
|
+
console.log("Posting report summary comment");
|
|
116
|
+
if (!token) {
|
|
117
|
+
console.log(" no API token provided, skipping");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const { summary } = result;
|
|
121
|
+
await runGitlabOperation(" failed to add summary comment", () => upsertGitlabJobNote({
|
|
122
|
+
token,
|
|
123
|
+
summary,
|
|
124
|
+
reportUrl,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
GitlabGenerateCommand.paths = [["gitlab"]];
|
|
130
|
+
GitlabGenerateCommand.usage = Command.Usage({
|
|
131
|
+
category: "Integrations",
|
|
132
|
+
description: "Generate test report and post report summary in merge request comments",
|
|
133
|
+
details: "This command generates a report from the provided Allure Results directories. When api access token is configured, " +
|
|
134
|
+
"integration will post summary as comment for merge request pipelines and attempt to lookup history file from previously executed job ." +
|
|
135
|
+
"This integration is designed to be executed from within GitLab CI job.",
|
|
136
|
+
examples: [["gitlab ./allure-results", "Generate a report from the ./allure-results directory"]],
|
|
137
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./generate.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./generate.js";
|
package/dist/commands/history.js
CHANGED
|
@@ -16,6 +16,9 @@ export class HistoryCommand extends Command {
|
|
|
16
16
|
this.historyLimit = Option.String("--history-limit", {
|
|
17
17
|
description: "Limits the number of history entries to keep (default: unlimited)",
|
|
18
18
|
});
|
|
19
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
20
|
+
description: "The public base URL of the generated report directory",
|
|
21
|
+
});
|
|
19
22
|
this.reportName = Option.String("--report-name,--name", {
|
|
20
23
|
description: "The report name",
|
|
21
24
|
});
|
|
@@ -31,10 +34,11 @@ export class HistoryCommand extends Command {
|
|
|
31
34
|
}
|
|
32
35
|
const config = await resolveConfig({
|
|
33
36
|
historyPath: this.historyPath ?? "history.jsonl",
|
|
37
|
+
historyBaseUrl: this.historyBaseUrl ?? rawConfig.historyBaseUrl,
|
|
34
38
|
historyLimit: this.historyLimit ? Number(this.historyLimit) : undefined,
|
|
35
39
|
name: this.reportName ?? "Allure Report",
|
|
36
40
|
plugins: {},
|
|
37
|
-
});
|
|
41
|
+
}, { plugins: {} });
|
|
38
42
|
const allureReport = new AllureReport(config);
|
|
39
43
|
await allureReport.start();
|
|
40
44
|
for (const directory of resultDirectories) {
|
package/dist/commands/index.d.ts
CHANGED
package/dist/commands/index.js
CHANGED
package/dist/commands/log.d.ts
CHANGED
package/dist/commands/log.js
CHANGED
|
@@ -5,6 +5,7 @@ import { AllureReport, readConfig } from "@allurereport/core";
|
|
|
5
5
|
import LogPlugin from "@allurereport/plugin-log";
|
|
6
6
|
import { Command, Option } from "clipanion";
|
|
7
7
|
import { red } from "yoctocolors";
|
|
8
|
+
import { resolveCommandEnvironment } from "../utils/environment.js";
|
|
8
9
|
import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
|
|
9
10
|
export class LogCommand extends Command {
|
|
10
11
|
constructor() {
|
|
@@ -27,6 +28,12 @@ export class LogCommand extends Command {
|
|
|
27
28
|
this.withTrace = Option.Boolean("--with-trace", {
|
|
28
29
|
description: "Print stack trace for failed tests",
|
|
29
30
|
});
|
|
31
|
+
this.qualityGateResults = Option.Boolean("--quality-gate-results", true, {
|
|
32
|
+
description: "Print quality gate validation results after tests (default: true). Pass --no-quality-gate-results to hide them",
|
|
33
|
+
});
|
|
34
|
+
this.allQualityGateResults = Option.Boolean("--all-quality-gate-results", false, {
|
|
35
|
+
description: "Include successful quality gate validation results in addition to failures",
|
|
36
|
+
});
|
|
30
37
|
}
|
|
31
38
|
async execute() {
|
|
32
39
|
const cwd = await realpath(this.cwd ?? process.cwd());
|
|
@@ -35,8 +42,11 @@ export class LogCommand extends Command {
|
|
|
35
42
|
allSteps: this.allSteps ?? false,
|
|
36
43
|
withTrace: this.withTrace ?? false,
|
|
37
44
|
groupBy: this.groupBy ?? "suite",
|
|
45
|
+
qualityGateResults: this.qualityGateResults,
|
|
46
|
+
qualityGateFilter: this.allQualityGateResults ? () => true : undefined,
|
|
38
47
|
};
|
|
39
48
|
const config = await readConfig(cwd, this.config);
|
|
49
|
+
const resolvedEnvironment = resolveCommandEnvironment(config, {});
|
|
40
50
|
const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
|
|
41
51
|
if (!resultDirectories.length) {
|
|
42
52
|
console.error(red(`No test results directories found matching pattern: ${patterns}`));
|
|
@@ -51,11 +61,21 @@ export class LogCommand extends Command {
|
|
|
51
61
|
plugin: new LogPlugin(defaultLogOptions),
|
|
52
62
|
},
|
|
53
63
|
];
|
|
54
|
-
const allureReport = new AllureReport(
|
|
64
|
+
const allureReport = new AllureReport({
|
|
65
|
+
...config,
|
|
66
|
+
environment: resolvedEnvironment?.id,
|
|
67
|
+
});
|
|
55
68
|
await allureReport.start();
|
|
56
69
|
for (const directory of resultDirectories) {
|
|
57
70
|
await allureReport.readDirectory(directory);
|
|
58
71
|
}
|
|
72
|
+
if (this.qualityGateResults && allureReport.hasQualityGate) {
|
|
73
|
+
const allTrs = await allureReport.store.allTestResults({ includeRetries: false });
|
|
74
|
+
const validationResults = await allureReport.validate({ trs: allTrs, environment: resolvedEnvironment?.id });
|
|
75
|
+
if (validationResults.results.length > 0) {
|
|
76
|
+
allureReport.realtimeDispatcher.sendQualityGateResults(validationResults.results);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
59
79
|
await allureReport.done();
|
|
60
80
|
const after = new Date().getTime();
|
|
61
81
|
console.log(`the report successfully generated (${after - before}ms)`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as console from "node:console";
|
|
2
2
|
import { realpath } from "node:fs/promises";
|
|
3
3
|
import { exit, cwd as processCwd } from "node:process";
|
|
4
|
-
import { AllureReport, QualityGateState, readConfig, stringifyQualityGateResults } from "@allurereport/core";
|
|
4
|
+
import { AllureReport, QualityGateState, filterFailedQualityGateResults, readConfig, stringifyQualityGateResults, } from "@allurereport/core";
|
|
5
5
|
import { Command, Option } from "clipanion";
|
|
6
6
|
import * as typanion from "typanion";
|
|
7
7
|
import { red } from "yoctocolors";
|
|
@@ -112,7 +112,7 @@ export class QualityGateCommand extends Command {
|
|
|
112
112
|
trs: allTrs,
|
|
113
113
|
environment: resolvedEnvironment?.id,
|
|
114
114
|
});
|
|
115
|
-
if (validationResults.results.length === 0) {
|
|
115
|
+
if (filterFailedQualityGateResults(validationResults.results).length === 0) {
|
|
116
116
|
exit(0);
|
|
117
117
|
return;
|
|
118
118
|
}
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare class RunCommand extends Command {
|
|
|
15
15
|
environment: string | undefined;
|
|
16
16
|
environmentName: string | undefined;
|
|
17
17
|
historyLimit: string | undefined;
|
|
18
|
+
historyBaseUrl: string | undefined;
|
|
18
19
|
hideLabels: string[] | undefined;
|
|
19
20
|
knownIssues: string | undefined;
|
|
20
21
|
resultsDir: string[] | undefined;
|
package/dist/commands/run.js
CHANGED
|
@@ -48,6 +48,9 @@ export class RunCommand extends Command {
|
|
|
48
48
|
this.historyLimit = Option.String("--history-limit", {
|
|
49
49
|
description: "Limits the number of history entries to keep (default: unlimited)",
|
|
50
50
|
});
|
|
51
|
+
this.historyBaseUrl = Option.String("--history-base-url", {
|
|
52
|
+
description: "The public base URL of the generated report directory",
|
|
53
|
+
});
|
|
51
54
|
this.hideLabels = Option.Array("--hide-labels", {
|
|
52
55
|
description: "Hide labels by exact name in generated reports. Repeat the option for multiple labels",
|
|
53
56
|
});
|
|
@@ -101,6 +104,7 @@ export class RunCommand extends Command {
|
|
|
101
104
|
port: this.port,
|
|
102
105
|
hideLabels,
|
|
103
106
|
historyLimit: this.historyLimit !== undefined ? parseInt(this.historyLimit, 10) : undefined,
|
|
107
|
+
...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
|
|
104
108
|
resolutions: { knownIssuesPath: this.knownIssues },
|
|
105
109
|
});
|
|
106
110
|
const resultsPatterns = resolveResultsPatterns(this.resultsDir ?? [], config.resultsDir);
|
|
@@ -122,7 +126,7 @@ export class RunCommand extends Command {
|
|
|
122
126
|
...config,
|
|
123
127
|
environment: resolvedEnvironment?.id,
|
|
124
128
|
qualityGate: withQualityGate ? config.qualityGate : undefined,
|
|
125
|
-
dump: this.dump,
|
|
129
|
+
dump: this.dump ?? config.dump,
|
|
126
130
|
realTime: false,
|
|
127
131
|
plugins: [
|
|
128
132
|
...(config.plugins?.length
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import process, { argv } from "node:process";
|
|
3
3
|
import { Builtins, Cli } from "clipanion";
|
|
4
|
-
import { AgentCommand, AGENT_TASK_MAP_HELP, AgentCapabilitiesCommand, AgentInspectCommand, AgentLatestCommand, AgentQueryCommand, AgentSelectCommand, AgentStateDirCommand, Allure2Command, AwesomeCommand, CheckCommand, ClassicCommand, CsvCommand, DashboardCommand, GenerateCommand, HistoryCommand, JiraClearCommand, LogCommand, OpenCommand, QualityGateCommand, ResultsPackCommand, ResultsUnpackCommand, RunCommand, SlackCommand, TestOpsPlanCommand, TestPlanCommand, WatchCommand, isAgentTaskMapHelpRequest, } from "./commands/index.js";
|
|
4
|
+
import { AgentCommand, AGENT_TASK_MAP_HELP, AgentCapabilitiesCommand, AgentInspectCommand, AgentLatestCommand, AgentQueryCommand, AgentSelectCommand, AgentStateDirCommand, Allure2Command, AwesomeCommand, CheckCommand, ClassicCommand, CsvCommand, DashboardCommand, GenerateCommand, GitlabGenerateCommand, HistoryCommand, JiraClearCommand, LogCommand, OpenCommand, QualityGateCommand, ResultsPackCommand, ResultsUnpackCommand, RunCommand, SlackCommand, TestOpsPlanCommand, TestPlanCommand, WatchCommand, isAgentTaskMapHelpRequest, } from "./commands/index.js";
|
|
5
5
|
const [node, app, ...args] = argv;
|
|
6
6
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
7
7
|
const cli = new Cli({
|
|
@@ -35,6 +35,7 @@ cli.register(TestPlanCommand);
|
|
|
35
35
|
cli.register(WatchCommand);
|
|
36
36
|
cli.register(ResultsPackCommand);
|
|
37
37
|
cli.register(ResultsUnpackCommand);
|
|
38
|
+
cli.register(GitlabGenerateCommand);
|
|
38
39
|
cli.register(Builtins.HelpCommand);
|
|
39
40
|
cli.register(Builtins.VersionCommand);
|
|
40
41
|
void cli
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allure",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"description": "Allure Commandline Tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -32,33 +32,33 @@
|
|
|
32
32
|
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@allurereport/charts-api": "3.
|
|
36
|
-
"@allurereport/ci": "3.
|
|
37
|
-
"@allurereport/core": "3.
|
|
38
|
-
"@allurereport/core-api": "3.
|
|
39
|
-
"@allurereport/directory-watcher": "3.
|
|
40
|
-
"@allurereport/plugin-agent": "3.
|
|
41
|
-
"@allurereport/plugin-allure2": "3.
|
|
42
|
-
"@allurereport/plugin-api": "3.
|
|
43
|
-
"@allurereport/plugin-awesome": "3.
|
|
44
|
-
"@allurereport/plugin-classic": "3.
|
|
45
|
-
"@allurereport/plugin-csv": "3.
|
|
46
|
-
"@allurereport/plugin-dashboard": "3.
|
|
47
|
-
"@allurereport/plugin-jira": "3.
|
|
48
|
-
"@allurereport/plugin-log": "3.
|
|
49
|
-
"@allurereport/plugin-progress": "3.
|
|
50
|
-
"@allurereport/plugin-server-reload": "3.
|
|
51
|
-
"@allurereport/plugin-slack": "3.
|
|
52
|
-
"@allurereport/reader-api": "3.
|
|
53
|
-
"@allurereport/service": "3.
|
|
54
|
-
"@allurereport/static-server": "3.
|
|
35
|
+
"@allurereport/charts-api": "3.19.0",
|
|
36
|
+
"@allurereport/ci": "3.19.0",
|
|
37
|
+
"@allurereport/core": "3.19.0",
|
|
38
|
+
"@allurereport/core-api": "3.19.0",
|
|
39
|
+
"@allurereport/directory-watcher": "3.19.0",
|
|
40
|
+
"@allurereport/plugin-agent": "3.19.0",
|
|
41
|
+
"@allurereport/plugin-allure2": "3.19.0",
|
|
42
|
+
"@allurereport/plugin-api": "3.19.0",
|
|
43
|
+
"@allurereport/plugin-awesome": "3.19.0",
|
|
44
|
+
"@allurereport/plugin-classic": "3.19.0",
|
|
45
|
+
"@allurereport/plugin-csv": "3.19.0",
|
|
46
|
+
"@allurereport/plugin-dashboard": "3.19.0",
|
|
47
|
+
"@allurereport/plugin-jira": "3.19.0",
|
|
48
|
+
"@allurereport/plugin-log": "3.19.0",
|
|
49
|
+
"@allurereport/plugin-progress": "3.19.0",
|
|
50
|
+
"@allurereport/plugin-server-reload": "3.19.0",
|
|
51
|
+
"@allurereport/plugin-slack": "3.19.0",
|
|
52
|
+
"@allurereport/reader-api": "3.19.0",
|
|
53
|
+
"@allurereport/service": "3.19.0",
|
|
54
|
+
"@allurereport/static-server": "3.19.0",
|
|
55
55
|
"adm-zip": "^0.6.0",
|
|
56
56
|
"clipanion": "^4.0.0-rc.4",
|
|
57
57
|
"glob": "^13.0.6",
|
|
58
58
|
"lodash.omit": "^4.18.0",
|
|
59
59
|
"prompts": "^2.4.2",
|
|
60
60
|
"typanion": "^3.14.0",
|
|
61
|
-
"yoctocolors": "^2.
|
|
61
|
+
"yoctocolors": "^2.2.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/adm-zip": "^0",
|