allure 3.14.3 → 3.16.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/README.md +34 -10
- package/dist/commands/agent-run.d.ts +1 -0
- package/dist/commands/agent-run.js +11 -6
- package/dist/commands/agent.d.ts +1 -0
- package/dist/commands/agent.js +19 -8
- package/dist/commands/allure2.js +10 -10
- package/dist/commands/awesome.js +10 -10
- package/dist/commands/check.js +1 -1
- package/dist/commands/classic.js +10 -10
- package/dist/commands/commons/generate.d.ts +1 -1
- package/dist/commands/commons/generate.js +7 -3
- package/dist/commands/commons/resultsDiscovery.d.ts +5 -0
- package/dist/commands/commons/resultsDiscovery.js +17 -0
- package/dist/commands/commons/run.d.ts +3 -0
- package/dist/commands/commons/run.js +60 -27
- package/dist/commands/csv.js +10 -10
- package/dist/commands/dashboard.js +9 -9
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.js +7 -3
- package/dist/commands/history.js +6 -4
- package/dist/commands/index.d.ts +0 -1
- package/dist/commands/index.js +0 -1
- package/dist/commands/jira/clear.js +1 -1
- package/dist/commands/log.js +9 -9
- package/dist/commands/open.js +9 -5
- package/dist/commands/qualityGate.js +7 -7
- package/dist/commands/results/pack.d.ts +1 -0
- package/dist/commands/results/pack.js +12 -14
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +24 -8
- package/dist/commands/slack.js +9 -9
- package/dist/commands/testplan.js +7 -5
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +75 -26
- package/dist/index.js +1 -2
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/process.js +1 -0
- package/dist/utils/resultsPatterns.d.ts +12 -0
- package/dist/utils/resultsPatterns.js +37 -0
- package/dist/utils/signals.d.ts +18 -0
- package/dist/utils/signals.js +52 -0
- package/package.json +22 -22
- package/dist/commands/knownIssue.d.ts +0 -8
- package/dist/commands/knownIssue.js +0 -59
package/dist/commands/run.js
CHANGED
|
@@ -7,12 +7,14 @@ import { serve } from "@allurereport/static-server";
|
|
|
7
7
|
import { Command, Option, UsageError } from "clipanion";
|
|
8
8
|
import { environmentNameOption, environmentOption, normalizeCommandEnvironmentOptions, resolveCommandEnvironment, } from "../utils/environment.js";
|
|
9
9
|
import { createChildAllureCliEnvironment, getActiveAllureCliCommand } from "../utils/execution-context.js";
|
|
10
|
+
import { parseRunCommand, resolveResultsPatterns } from "../utils/resultsPatterns.js";
|
|
10
11
|
import { executeAllureRun, executeNestedAllureCommand } from "./commons/run.js";
|
|
12
|
+
const missingRunCommandUsageError = () => new UsageError("expecting command to be specified after --, e.g. allure run -- npm run test");
|
|
11
13
|
export class RunCommand extends Command {
|
|
12
14
|
constructor() {
|
|
13
15
|
super(...arguments);
|
|
14
16
|
this.config = Option.String("--config,-c", {
|
|
15
|
-
description: "The path Allure config file",
|
|
17
|
+
description: "The path to Allure config file",
|
|
16
18
|
});
|
|
17
19
|
this.cwd = Option.String("--cwd", {
|
|
18
20
|
description: "The working directory for the command to run (default: current working directory)",
|
|
@@ -49,6 +51,12 @@ export class RunCommand extends Command {
|
|
|
49
51
|
this.hideLabels = Option.Array("--hide-labels", {
|
|
50
52
|
description: "Hide labels by exact name in generated reports. Repeat the option for multiple labels",
|
|
51
53
|
});
|
|
54
|
+
this.knownIssues = Option.String("--known-issues", {
|
|
55
|
+
description: "Path to known issues file",
|
|
56
|
+
});
|
|
57
|
+
this.resultsDir = Option.Array("--results-dir", {
|
|
58
|
+
description: "Glob pattern or path for Allure results directories (repeatable). Overrides config.resultsDir. Quote globs in the shell",
|
|
59
|
+
});
|
|
52
60
|
this.commandToRun = Option.Rest();
|
|
53
61
|
}
|
|
54
62
|
get logs() {
|
|
@@ -58,17 +66,15 @@ export class RunCommand extends Command {
|
|
|
58
66
|
return this.ignoreLogs ? "inherit" : "pipe";
|
|
59
67
|
}
|
|
60
68
|
async execute() {
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
63
|
-
throw
|
|
69
|
+
const { command, commandArgs } = parseRunCommand(this.commandToRun);
|
|
70
|
+
if (!command) {
|
|
71
|
+
throw missingRunCommandUsageError();
|
|
64
72
|
}
|
|
65
73
|
const before = new Date().getTime();
|
|
66
74
|
process.on("exit", (exitCode) => {
|
|
67
75
|
const after = new Date().getTime();
|
|
68
76
|
console.log(`exit code ${exitCode} (${after - before}ms)`);
|
|
69
77
|
});
|
|
70
|
-
const command = args[0];
|
|
71
|
-
const commandArgs = args.slice(1);
|
|
72
78
|
const cwd = await realpath(this.cwd ?? process.cwd());
|
|
73
79
|
const hideLabels = this.hideLabels?.length ? this.hideLabels : undefined;
|
|
74
80
|
console.log(`${command} ${commandArgs.join(" ")}`);
|
|
@@ -94,8 +100,10 @@ export class RunCommand extends Command {
|
|
|
94
100
|
open: this.open,
|
|
95
101
|
port: this.port,
|
|
96
102
|
hideLabels,
|
|
97
|
-
historyLimit: this.historyLimit ? parseInt(this.historyLimit, 10) : undefined,
|
|
103
|
+
historyLimit: this.historyLimit !== undefined ? parseInt(this.historyLimit, 10) : undefined,
|
|
104
|
+
knownIssuesPath: this.knownIssues,
|
|
98
105
|
});
|
|
106
|
+
const resultsPatterns = resolveResultsPatterns(this.resultsDir ?? [], config.resultsDir);
|
|
99
107
|
const resolvedEnvironment = resolveCommandEnvironment(config, environmentOptions);
|
|
100
108
|
const withRerun = maxRerun > 0;
|
|
101
109
|
const withQualityGate = !!config.qualityGate && !withRerun;
|
|
@@ -145,6 +153,7 @@ export class RunCommand extends Command {
|
|
|
145
153
|
silent: this.silent,
|
|
146
154
|
ignoreLogs: this.ignoreLogs,
|
|
147
155
|
maxRerun,
|
|
156
|
+
resultsPatterns,
|
|
148
157
|
});
|
|
149
158
|
if (config.open) {
|
|
150
159
|
await serve({
|
|
@@ -161,7 +170,10 @@ export class RunCommand extends Command {
|
|
|
161
170
|
RunCommand.paths = [["run"]];
|
|
162
171
|
RunCommand.usage = Command.Usage({
|
|
163
172
|
description: "Run specified command",
|
|
164
|
-
details: "This command runs the specified command and collects Allure results."
|
|
173
|
+
details: "This command runs the specified command and collects Allure results. " +
|
|
174
|
+
"Override results discovery with repeated `--results-dir` (CLI overrides `config.resultsDir`). " +
|
|
175
|
+
"When neither is set, directories named `allure-results` are discovered dynamically. " +
|
|
176
|
+
"Quote globs in the shell so they are not expanded early.",
|
|
165
177
|
examples: [
|
|
166
178
|
["run -- npm run test", "Run npm run test and collect Allure results"],
|
|
167
179
|
["run --rerun 3 -- npm run test", "Run npm run test and rerun failed tests up to 3 times"],
|
|
@@ -169,5 +181,9 @@ RunCommand.usage = Command.Usage({
|
|
|
169
181
|
"run --dump=my-dump -- npm run test",
|
|
170
182
|
"Run npm run test and pack inner report state into my-dump.zip archive to restore the state in the next run",
|
|
171
183
|
],
|
|
184
|
+
[
|
|
185
|
+
"run --results-dir './artifacts/**/allure-results' -- npm test",
|
|
186
|
+
"Override results discovery with a quoted glob",
|
|
187
|
+
],
|
|
172
188
|
],
|
|
173
189
|
});
|
package/dist/commands/slack.js
CHANGED
|
@@ -5,15 +5,15 @@ import { AllureReport, readConfig } from "@allurereport/core";
|
|
|
5
5
|
import SlackPlugin from "@allurereport/plugin-slack";
|
|
6
6
|
import { Command, Option } from "clipanion";
|
|
7
7
|
import { red } from "yoctocolors";
|
|
8
|
-
import {
|
|
8
|
+
import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
|
|
9
9
|
export class SlackCommand extends Command {
|
|
10
10
|
constructor() {
|
|
11
11
|
super(...arguments);
|
|
12
12
|
this.resultsDir = Option.Rest({
|
|
13
|
-
name: "Patterns to match test results directories
|
|
13
|
+
name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
|
|
14
14
|
});
|
|
15
15
|
this.config = Option.String("--config,-c", {
|
|
16
|
-
description: "The path Allure config file",
|
|
16
|
+
description: "The path to Allure config file",
|
|
17
17
|
});
|
|
18
18
|
this.cwd = Option.String("--cwd", {
|
|
19
19
|
description: "The working directory for the command to run (default: current working directory)",
|
|
@@ -29,18 +29,18 @@ export class SlackCommand extends Command {
|
|
|
29
29
|
}
|
|
30
30
|
async execute() {
|
|
31
31
|
const cwd = await realpath(this.cwd ?? process.cwd());
|
|
32
|
-
const { resultDirectories, patterns } = await findAllureResultDirectories(cwd, this.resultsDir);
|
|
33
|
-
if (!resultDirectories.length) {
|
|
34
|
-
console.error(red(`No test results directories found matching pattern: ${patterns}`));
|
|
35
|
-
exit(1);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
32
|
const before = new Date().getTime();
|
|
39
33
|
const defaultSlackOptions = {
|
|
40
34
|
token: this.token,
|
|
41
35
|
channel: this.channel,
|
|
42
36
|
};
|
|
43
37
|
const config = await readConfig(cwd, this.config);
|
|
38
|
+
const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
|
|
39
|
+
if (!resultDirectories.length) {
|
|
40
|
+
console.error(red(`No test results directories found matching pattern: ${patterns}`));
|
|
41
|
+
exit(1);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
44
|
config.plugins = [
|
|
45
45
|
{
|
|
46
46
|
id: "slack",
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import * as console from "node:console";
|
|
2
2
|
import { basename, dirname, resolve } from "node:path";
|
|
3
|
-
import { exit } from "node:process";
|
|
4
|
-
import { AllureReport, resolveConfig } from "@allurereport/core";
|
|
3
|
+
import { cwd as processCwd, exit } from "node:process";
|
|
4
|
+
import { AllureReport, readRawConfig, resolveConfig } from "@allurereport/core";
|
|
5
5
|
import { Command, Option } from "clipanion";
|
|
6
6
|
import { red } from "yoctocolors";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
|
|
8
8
|
export class TestPlanCommand extends Command {
|
|
9
9
|
constructor() {
|
|
10
10
|
super(...arguments);
|
|
11
11
|
this.resultsDir = Option.Rest({
|
|
12
|
-
name: "Patterns to match test results directories
|
|
12
|
+
name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
|
|
13
13
|
});
|
|
14
14
|
this.output = Option.String("--output,-o", {
|
|
15
15
|
description: "The output file name. Absolute paths are accepted as well",
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
async execute() {
|
|
19
|
-
const
|
|
19
|
+
const cwd = processCwd();
|
|
20
|
+
const rawConfig = await readRawConfig(cwd);
|
|
21
|
+
const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, rawConfig.resultsDir);
|
|
20
22
|
if (!resultDirectories.length) {
|
|
21
23
|
console.error(red(`No test results directories found matching pattern: ${patterns}`));
|
|
22
24
|
exit(1);
|
package/dist/commands/watch.d.ts
CHANGED
package/dist/commands/watch.js
CHANGED
|
@@ -2,25 +2,26 @@ import * as console from "node:console";
|
|
|
2
2
|
import { realpath } from "node:fs/promises";
|
|
3
3
|
import { rm } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import process
|
|
5
|
+
import process from "node:process";
|
|
6
6
|
import { AllureReport, isFileNotFoundError, readConfig } from "@allurereport/core";
|
|
7
|
-
import { newFilesInDirectoryWatcher } from "@allurereport/directory-watcher";
|
|
7
|
+
import { allureResultsDirectoriesWatcher, newFilesInDirectoryWatcher } from "@allurereport/directory-watcher";
|
|
8
8
|
import Awesome from "@allurereport/plugin-awesome";
|
|
9
9
|
import ProgressPlugin from "@allurereport/plugin-progress";
|
|
10
10
|
import ServerReloadPlugin from "@allurereport/plugin-server-reload";
|
|
11
11
|
import { PathResultFile } from "@allurereport/reader-api";
|
|
12
12
|
import { serve } from "@allurereport/static-server";
|
|
13
13
|
import { Command, Option } from "clipanion";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { resolveResultsPatterns } from "../utils/resultsPatterns.js";
|
|
15
|
+
import { boundedTerminationSignal, notifySignals, waitForAbort } from "../utils/signals.js";
|
|
16
|
+
import { allureResultsDirectoriesGlobWatcher } from "./commons/resultsDiscovery.js";
|
|
16
17
|
export class WatchCommand extends Command {
|
|
17
18
|
constructor() {
|
|
18
19
|
super(...arguments);
|
|
19
20
|
this.resultsDir = Option.Rest({
|
|
20
|
-
name: "Patterns to match test results directories
|
|
21
|
+
name: "Patterns to match test results directories. Overrides config.resultsDir. Empty patterns use name-based discovery.",
|
|
21
22
|
});
|
|
22
23
|
this.config = Option.String("--config,-c", {
|
|
23
|
-
description: "The path Allure config file",
|
|
24
|
+
description: "The path to Allure config file",
|
|
24
25
|
});
|
|
25
26
|
this.cwd = Option.String("--cwd", {
|
|
26
27
|
description: "The working directory for the command to run (default: current working directory)",
|
|
@@ -37,15 +38,15 @@ export class WatchCommand extends Command {
|
|
|
37
38
|
this.port = Option.String("--port", {
|
|
38
39
|
description: "The port to serve the reports on (default: random port)",
|
|
39
40
|
});
|
|
41
|
+
this.preserve = Option.Boolean("--preserve", {
|
|
42
|
+
description: "Don't clear terminal output on the data refresh",
|
|
43
|
+
});
|
|
44
|
+
this.newOnly = Option.Boolean("--new-only", true, {
|
|
45
|
+
description: "Skip whatever test results already exist on disk at startup and only react to results written after the watch has started, instead of ingesting the existing backlog first (default: true). Pass --no-new-only to ingest the existing backlog too",
|
|
46
|
+
});
|
|
40
47
|
}
|
|
41
48
|
async execute() {
|
|
42
49
|
const cwd = await realpath(this.cwd ?? process.cwd());
|
|
43
|
-
const { resultDirectories, patterns } = await findAllureResultDirectories(cwd, this.resultsDir);
|
|
44
|
-
if (!resultDirectories.length) {
|
|
45
|
-
console.error(red(`No test results directories found matching pattern: ${patterns}`));
|
|
46
|
-
exit(1);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
50
|
const before = new Date().getTime();
|
|
50
51
|
process.on("exit", (code) => {
|
|
51
52
|
const after = new Date().getTime();
|
|
@@ -57,6 +58,8 @@ export class WatchCommand extends Command {
|
|
|
57
58
|
open: this.open,
|
|
58
59
|
port: this.port,
|
|
59
60
|
});
|
|
61
|
+
const resultsPatterns = resolveResultsPatterns(this.resultsDir ?? [], config.resultsDir);
|
|
62
|
+
const useDynamicNameDiscovery = resultsPatterns.length === 0;
|
|
60
63
|
try {
|
|
61
64
|
await rm(config.output, { recursive: true });
|
|
62
65
|
}
|
|
@@ -93,7 +96,7 @@ export class WatchCommand extends Command {
|
|
|
93
96
|
id: "watch log",
|
|
94
97
|
enabled: true,
|
|
95
98
|
options: {},
|
|
96
|
-
plugin: new ProgressPlugin(),
|
|
99
|
+
plugin: new ProgressPlugin({ preserve: this.preserve }),
|
|
97
100
|
},
|
|
98
101
|
{
|
|
99
102
|
id: "server reload",
|
|
@@ -107,39 +110,85 @@ export class WatchCommand extends Command {
|
|
|
107
110
|
});
|
|
108
111
|
await allureReport.start();
|
|
109
112
|
const abortFunctions = [];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
const perDirectoryWatchers = new Map();
|
|
114
|
+
let isInitialDiscovery = true;
|
|
115
|
+
const onDiscoveryUpdate = async (newDirectories, deletedDirectories) => {
|
|
116
|
+
for (const deletedDir of deletedDirectories) {
|
|
117
|
+
const watcher = perDirectoryWatchers.get(deletedDir);
|
|
118
|
+
if (watcher) {
|
|
119
|
+
await watcher.abort();
|
|
120
|
+
}
|
|
121
|
+
perDirectoryWatchers.delete(deletedDir);
|
|
122
|
+
}
|
|
123
|
+
for (const newDir of newDirectories) {
|
|
124
|
+
if (perDirectoryWatchers.has(newDir)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const watcher = newFilesInDirectoryWatcher(newDir, async (path) => {
|
|
128
|
+
await allureReport.readResult(new PathResultFile(path));
|
|
129
|
+
}, { ignoreInitial: this.newOnly && isInitialDiscovery });
|
|
130
|
+
perDirectoryWatchers.set(newDir, watcher);
|
|
131
|
+
await watcher.initialScan();
|
|
132
|
+
}
|
|
133
|
+
isInitialDiscovery = false;
|
|
134
|
+
};
|
|
135
|
+
const discoveryWatcher = useDynamicNameDiscovery
|
|
136
|
+
? allureResultsDirectoriesWatcher(cwd, onDiscoveryUpdate)
|
|
137
|
+
: allureResultsDirectoriesGlobWatcher(cwd, resultsPatterns, onDiscoveryUpdate, { indexDelay: 600 });
|
|
138
|
+
await discoveryWatcher.initialScan();
|
|
139
|
+
abortFunctions.push(discoveryWatcher.abort);
|
|
140
|
+
abortFunctions.push(async (immediately) => {
|
|
141
|
+
for (const watcher of perDirectoryWatchers.values()) {
|
|
142
|
+
await watcher.abort(immediately);
|
|
143
|
+
}
|
|
144
|
+
perDirectoryWatchers.clear();
|
|
145
|
+
});
|
|
116
146
|
const pluginIdToOpen = config.plugins?.find((plugin) => !!plugin.options.open)?.id;
|
|
117
147
|
if (pluginIdToOpen) {
|
|
118
148
|
await server.open(join(server.url, pluginIdToOpen));
|
|
119
149
|
}
|
|
120
150
|
console.info("Press Ctrl+C to exit");
|
|
121
|
-
|
|
122
|
-
console.log(
|
|
151
|
+
const notifier = notifySignals(["SIGINT", "SIGTERM"], (signal) => {
|
|
152
|
+
console.log(`\nreceived another ${signal}, force exiting...`);
|
|
153
|
+
process.exit(130);
|
|
154
|
+
});
|
|
155
|
+
await waitForAbort(notifier.signal);
|
|
156
|
+
const signalInfo = notifier.info();
|
|
157
|
+
console.log(`\nreceived ${signalInfo?.signal}, stopping (press again to force exit)...`);
|
|
158
|
+
const terminationSignal = boundedTerminationSignal(signalInfo, 5000);
|
|
159
|
+
const cleanup = (async () => {
|
|
123
160
|
for (const abort of abortFunctions) {
|
|
124
|
-
await abort();
|
|
161
|
+
await abort(true);
|
|
125
162
|
}
|
|
126
163
|
await server.stop();
|
|
127
164
|
await allureReport.done();
|
|
128
|
-
|
|
129
|
-
|
|
165
|
+
})();
|
|
166
|
+
const timedOut = await Promise.race([cleanup.then(() => false), waitForAbort(terminationSignal).then(() => true)]);
|
|
167
|
+
if (timedOut) {
|
|
168
|
+
console.log("shutdown is taking too long, force exiting...");
|
|
169
|
+
process.exit(130);
|
|
170
|
+
}
|
|
171
|
+
notifier.dispose();
|
|
172
|
+
process.exit(signalInfo?.code ?? 0);
|
|
130
173
|
}
|
|
131
174
|
}
|
|
132
175
|
WatchCommand.paths = [["watch"]];
|
|
133
176
|
WatchCommand.usage = Command.Usage({
|
|
134
177
|
description: "Watches Allure Results changes in Real-time",
|
|
135
|
-
details: "This command watches for changes in
|
|
178
|
+
details: "This command watches for changes in Allure Results directories and updates the report in real-time. " +
|
|
179
|
+
"CLI patterns and config.resultsDir use live re-glob discovery. " +
|
|
180
|
+
"When both are empty, directories named `allure-results` are discovered dynamically (unlike generate, which defaults to `./**/allure-results`). " +
|
|
181
|
+
"Quote globs in the shell so they are not expanded early.",
|
|
136
182
|
examples: [
|
|
137
183
|
["watch ./allure-results", "Watch for changes in the ./allure-results directory"],
|
|
138
184
|
[
|
|
139
185
|
"watch ./allure-results --port 8080",
|
|
140
186
|
"Watch for changes in the ./allure-results directory and serve the report on port 8080",
|
|
141
187
|
],
|
|
142
|
-
[
|
|
188
|
+
[
|
|
189
|
+
"watch './packages/*/allure-results'",
|
|
190
|
+
"Watch for changes in all Allure result directories matching the pattern",
|
|
191
|
+
],
|
|
143
192
|
[
|
|
144
193
|
"watch ./packages/foo/allure-results ./packages/bar/allure-results",
|
|
145
194
|
"Watch for changes in two Allure result directories",
|
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,
|
|
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, 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({
|
|
@@ -25,7 +25,6 @@ cli.register(DashboardCommand);
|
|
|
25
25
|
cli.register(GenerateCommand);
|
|
26
26
|
cli.register(HistoryCommand);
|
|
27
27
|
cli.register(JiraClearCommand);
|
|
28
|
-
cli.register(KnownIssueCommand);
|
|
29
28
|
cli.register(LogCommand);
|
|
30
29
|
cli.register(OpenCommand);
|
|
31
30
|
cli.register(QualityGateCommand);
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/dist/utils/process.js
CHANGED
|
@@ -74,6 +74,7 @@ export const runProcess = (params) => {
|
|
|
74
74
|
};
|
|
75
75
|
export const terminationOf = (testProcess) => new Promise((resolve) => {
|
|
76
76
|
testProcess.on("exit", (code) => resolve(code));
|
|
77
|
+
testProcess.on("error", () => resolve(null));
|
|
77
78
|
});
|
|
78
79
|
export const stopProcessTree = async (pid, { signal = "SIGTERM" } = {}) => {
|
|
79
80
|
const tree = new Map();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const normalizeResultsDir: (value?: string | readonly string[]) => string[];
|
|
2
|
+
export declare const resolveResultsPatterns: (cliPatterns?: string | readonly string[] | null, configResultsDir?: string | readonly string[]) => string[];
|
|
3
|
+
export declare const normalizeResultsDirectoryPath: (path: string) => string;
|
|
4
|
+
export type ParsedRunCommand = {
|
|
5
|
+
command: string | undefined;
|
|
6
|
+
commandArgs: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare const parseRunCommand: (args: string[]) => ParsedRunCommand;
|
|
9
|
+
export declare const resolveAndFindResultsDirs: (cwd: string, cliPatterns: readonly string[], configResultsDir?: string | string[]) => Promise<{
|
|
10
|
+
patterns: readonly string[];
|
|
11
|
+
resultDirectories: string[];
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { findAllureResultDirectories } from "./fileSystem.js";
|
|
3
|
+
export const normalizeResultsDir = (value) => {
|
|
4
|
+
if (value === undefined) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
const values = typeof value === "string" ? [value] : [...value];
|
|
8
|
+
return values.filter((entry) => entry.length > 0);
|
|
9
|
+
};
|
|
10
|
+
export const resolveResultsPatterns = (cliPatterns, configResultsDir) => {
|
|
11
|
+
const fromCli = normalizeResultsDir(cliPatterns ?? undefined);
|
|
12
|
+
if (fromCli.length) {
|
|
13
|
+
return fromCli;
|
|
14
|
+
}
|
|
15
|
+
return normalizeResultsDir(configResultsDir);
|
|
16
|
+
};
|
|
17
|
+
export const normalizeResultsDirectoryPath = (path) => {
|
|
18
|
+
return resolve(path.replace(/[/\\]+$/, ""));
|
|
19
|
+
};
|
|
20
|
+
export const parseRunCommand = (args) => {
|
|
21
|
+
let start = 0;
|
|
22
|
+
while (start < args.length && args[start] === "--") {
|
|
23
|
+
start += 1;
|
|
24
|
+
}
|
|
25
|
+
const commandParts = args.slice(start);
|
|
26
|
+
if (!commandParts.length) {
|
|
27
|
+
return { command: undefined, commandArgs: [] };
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
command: commandParts[0],
|
|
31
|
+
commandArgs: commandParts.slice(1),
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export const resolveAndFindResultsDirs = async (cwd, cliPatterns, configResultsDir) => {
|
|
35
|
+
const patterns = resolveResultsPatterns(cliPatterns, configResultsDir);
|
|
36
|
+
return findAllureResultDirectories(cwd, patterns);
|
|
37
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const SIGNAL_EXIT_CODES: Partial<Record<NodeJS.Signals, number>>;
|
|
2
|
+
export declare const GRACEFUL_SHUTDOWN_TIMEOUT_MS = 60000;
|
|
3
|
+
export declare const COMMAND_TERMINATION_GRACE_MS = 5000;
|
|
4
|
+
export type SignalInfo = {
|
|
5
|
+
signal: NodeJS.Signals;
|
|
6
|
+
code: number;
|
|
7
|
+
receivedAt: number;
|
|
8
|
+
deadline: number;
|
|
9
|
+
};
|
|
10
|
+
export type SignalNotifier = {
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
info: () => SignalInfo | undefined;
|
|
13
|
+
dispose: () => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const notifySignals: (signals?: NodeJS.Signals[], onRepeat?: (signal: NodeJS.Signals) => void) => SignalNotifier;
|
|
16
|
+
export declare const gracefulShutdownRemaining: (info: SignalInfo | undefined) => number | undefined;
|
|
17
|
+
export declare const boundedTerminationSignal: (info: SignalInfo | undefined, graceMs?: number) => AbortSignal;
|
|
18
|
+
export declare const waitForAbort: (signal: AbortSignal) => Promise<void>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
export const SIGNAL_EXIT_CODES = {
|
|
3
|
+
SIGINT: 130,
|
|
4
|
+
SIGTERM: 143,
|
|
5
|
+
};
|
|
6
|
+
export const GRACEFUL_SHUTDOWN_TIMEOUT_MS = 60000;
|
|
7
|
+
export const COMMAND_TERMINATION_GRACE_MS = 5000;
|
|
8
|
+
const exitCodeForSignal = (signal) => SIGNAL_EXIT_CODES[signal] ?? 1;
|
|
9
|
+
export const notifySignals = (signals = ["SIGINT", "SIGTERM"], onRepeat) => {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
let info;
|
|
12
|
+
const handler = (signal) => {
|
|
13
|
+
if (info) {
|
|
14
|
+
onRepeat?.(signal);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const receivedAt = Date.now();
|
|
18
|
+
info = {
|
|
19
|
+
signal,
|
|
20
|
+
code: exitCodeForSignal(signal),
|
|
21
|
+
receivedAt,
|
|
22
|
+
deadline: receivedAt + GRACEFUL_SHUTDOWN_TIMEOUT_MS,
|
|
23
|
+
};
|
|
24
|
+
controller.abort();
|
|
25
|
+
};
|
|
26
|
+
for (const signal of signals) {
|
|
27
|
+
process.on(signal, handler);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
info: () => info,
|
|
32
|
+
dispose: () => {
|
|
33
|
+
for (const signal of signals) {
|
|
34
|
+
process.off(signal, handler);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export const gracefulShutdownRemaining = (info) => {
|
|
40
|
+
if (!info) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return Math.max(0, info.deadline - Date.now());
|
|
44
|
+
};
|
|
45
|
+
export const boundedTerminationSignal = (info, graceMs = COMMAND_TERMINATION_GRACE_MS) => {
|
|
46
|
+
const remaining = gracefulShutdownRemaining(info);
|
|
47
|
+
const timeoutMs = remaining === undefined ? graceMs : Math.min(remaining, graceMs);
|
|
48
|
+
return AbortSignal.timeout(timeoutMs);
|
|
49
|
+
};
|
|
50
|
+
export const waitForAbort = (signal) => signal.aborted
|
|
51
|
+
? Promise.resolve()
|
|
52
|
+
: new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allure",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "Allure Commandline Tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -32,27 +32,27 @@
|
|
|
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.
|
|
55
|
-
"adm-zip": "^0.
|
|
35
|
+
"@allurereport/charts-api": "3.16.0",
|
|
36
|
+
"@allurereport/ci": "3.16.0",
|
|
37
|
+
"@allurereport/core": "3.16.0",
|
|
38
|
+
"@allurereport/core-api": "3.16.0",
|
|
39
|
+
"@allurereport/directory-watcher": "3.16.0",
|
|
40
|
+
"@allurereport/plugin-agent": "3.16.0",
|
|
41
|
+
"@allurereport/plugin-allure2": "3.16.0",
|
|
42
|
+
"@allurereport/plugin-api": "3.16.0",
|
|
43
|
+
"@allurereport/plugin-awesome": "3.16.0",
|
|
44
|
+
"@allurereport/plugin-classic": "3.16.0",
|
|
45
|
+
"@allurereport/plugin-csv": "3.16.0",
|
|
46
|
+
"@allurereport/plugin-dashboard": "3.16.0",
|
|
47
|
+
"@allurereport/plugin-jira": "3.16.0",
|
|
48
|
+
"@allurereport/plugin-log": "3.16.0",
|
|
49
|
+
"@allurereport/plugin-progress": "3.16.0",
|
|
50
|
+
"@allurereport/plugin-server-reload": "3.16.0",
|
|
51
|
+
"@allurereport/plugin-slack": "3.16.0",
|
|
52
|
+
"@allurereport/reader-api": "3.16.0",
|
|
53
|
+
"@allurereport/service": "3.16.0",
|
|
54
|
+
"@allurereport/static-server": "3.16.0",
|
|
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",
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import * as console from "node:console";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import process, { exit } from "node:process";
|
|
4
|
-
import { AllureReport, resolveConfig, writeKnownIssues } from "@allurereport/core";
|
|
5
|
-
import { Command, Option } from "clipanion";
|
|
6
|
-
import { red } from "yoctocolors";
|
|
7
|
-
import { findAllureResultDirectories } from "../utils/fileSystem.js";
|
|
8
|
-
export class KnownIssueCommand extends Command {
|
|
9
|
-
constructor() {
|
|
10
|
-
super(...arguments);
|
|
11
|
-
this.resultsDir = Option.Rest({
|
|
12
|
-
name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
|
|
13
|
-
});
|
|
14
|
-
this.output = Option.String("--output,-o", {
|
|
15
|
-
description: "The output file name. Absolute paths are accepted as well",
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
async execute() {
|
|
19
|
-
const { resultDirectories, patterns } = await findAllureResultDirectories(process.cwd(), this.resultsDir);
|
|
20
|
-
if (!resultDirectories.length) {
|
|
21
|
-
console.error(red(`No test results directories found matching pattern: ${patterns}`));
|
|
22
|
-
exit(1);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const outputPath = this.output ?? "known-issues.json";
|
|
26
|
-
const config = await resolveConfig({
|
|
27
|
-
plugins: {},
|
|
28
|
-
});
|
|
29
|
-
const allureReport = new AllureReport(config);
|
|
30
|
-
await allureReport.start();
|
|
31
|
-
for (const directory of resultDirectories) {
|
|
32
|
-
await allureReport.readDirectory(directory);
|
|
33
|
-
}
|
|
34
|
-
await allureReport.done();
|
|
35
|
-
const targetPath = resolve(outputPath);
|
|
36
|
-
await writeKnownIssues(allureReport.store, outputPath);
|
|
37
|
-
console.log(`writing known-issues.json to ${targetPath}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
KnownIssueCommand.paths = [["known-issue"]];
|
|
41
|
-
KnownIssueCommand.usage = Command.Usage({
|
|
42
|
-
description: "Generates a known issue list",
|
|
43
|
-
details: "This command generates a known issue list from the provided Allure Results directory.",
|
|
44
|
-
examples: [
|
|
45
|
-
["known-issue ./allure-results", "Generate a known issue list from the ./allure-results directory"],
|
|
46
|
-
[
|
|
47
|
-
"known-issue ./allure-results --output custom-issues.json",
|
|
48
|
-
"Generate a known issue list from the ./allure-results directory to the custom-issues.json file",
|
|
49
|
-
],
|
|
50
|
-
[
|
|
51
|
-
"known-issue ./packages/*/allure-results",
|
|
52
|
-
"Generate a known issue list from all Allure result directories matching the pattern",
|
|
53
|
-
],
|
|
54
|
-
[
|
|
55
|
-
"known-issue ./packages/foo/allure-results ./packages/bar/allure-results",
|
|
56
|
-
"Generate a known issue list from two Allure result directories",
|
|
57
|
-
],
|
|
58
|
-
],
|
|
59
|
-
});
|