allure 3.9.0 → 3.11.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 +16 -1
- package/dist/commands/agent-run.d.ts +21 -0
- package/dist/commands/agent-run.js +151 -0
- package/dist/commands/agent.d.ts +38 -15
- package/dist/commands/agent.js +288 -219
- package/dist/commands/open.js +2 -2
- package/dist/commands/run.js +0 -18
- package/dist/index.js +16 -3
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.js +0 -2
- package/package.json +28 -28
- package/dist/utils/agent-select.d.ts +0 -41
- package/dist/utils/agent-select.js +0 -141
- package/dist/utils/agent-state.d.ts +0 -15
- package/dist/utils/agent-state.js +0 -83
package/dist/commands/agent.js
CHANGED
|
@@ -1,42 +1,20 @@
|
|
|
1
1
|
import * as console from "node:console";
|
|
2
|
-
import { mkdir, mkdtemp, realpath,
|
|
2
|
+
import { mkdir, mkdtemp, realpath, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { dirname, join,
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import process, { exit } from "node:process";
|
|
6
|
-
import {
|
|
6
|
+
import { AGENT_FINDING_CATEGORIES, AGENT_FINDING_SEVERITIES, AGENT_TASK_MAP_HELP, AGENT_TEST_STATUSES, AgentExpectationUsageError, buildAgentInlineExpectations, buildAgentQueryPayload, createAgentCapabilities, formatAgentOutputLinks, isAgentExpectationUsageError, isAgentTaskMapHelpRequest, isAgentUsageError, loadAgentOutput, normalizeAgentQueryLimit, normalizeAgentQueryView, normalizeAgentRerunPreset, normalizeRepeatedEnumValues, normalizeRepeatedStringValues, parseAgentLabelFilters, readLatestAgentState, resolveAgentSelectionOutputDir, resolveAgentStateDir, selectAgentTestPlan, validateAgentExpectationsFile, writeLatestAgentState, writeInvalidAgentExpectationOutput, } from "@allurereport/plugin-agent";
|
|
7
7
|
import { Command, Option, UsageError } from "clipanion";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
previousValues.set(key, process.env[key]);
|
|
17
|
-
if (value === undefined) {
|
|
18
|
-
delete process.env[key];
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
process.env[key] = value;
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
return await fn();
|
|
25
|
-
}
|
|
26
|
-
finally {
|
|
27
|
-
for (const [key, value] of previousValues) {
|
|
28
|
-
if (value === undefined) {
|
|
29
|
-
delete process.env[key];
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
process.env[key] = value;
|
|
33
|
-
}
|
|
8
|
+
export { AGENT_TASK_MAP_HELP, createAgentCapabilities, isAgentTaskMapHelpRequest };
|
|
9
|
+
const readOptionalString = (value) => (typeof value === "string" ? value : undefined);
|
|
10
|
+
const readOptionalBoolean = (value) => value === true;
|
|
11
|
+
const readOptionalStringArray = (value) => (Array.isArray(value) ? value : undefined);
|
|
12
|
+
const formatAgentCommand = (args) => args.join(" ");
|
|
13
|
+
const printAgentOutputLinks = (outputDir) => {
|
|
14
|
+
for (const line of formatAgentOutputLinks(outputDir)) {
|
|
15
|
+
console.log(line);
|
|
34
16
|
}
|
|
35
17
|
};
|
|
36
|
-
const isPathInside = (parentPath, candidatePath) => {
|
|
37
|
-
const rel = relative(parentPath, candidatePath);
|
|
38
|
-
return rel === "" || (!rel.startsWith("..") && rel !== "." && !rel.startsWith("../"));
|
|
39
|
-
};
|
|
40
18
|
const persistLatestAgentState = async (value) => {
|
|
41
19
|
try {
|
|
42
20
|
await writeLatestAgentState(value);
|
|
@@ -45,9 +23,38 @@ const persistLatestAgentState = async (value) => {
|
|
|
45
23
|
console.error(`Could not update latest agent output in ${resolveAgentStateDir(value.cwd)}: ${error.message}`);
|
|
46
24
|
}
|
|
47
25
|
};
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
26
|
+
const agentEnvironmentOption = () => Option.String("--environment,--env", {
|
|
27
|
+
description: "Force specific environment ID to all tests in the run. Given environment has higher priority than the one defined in the config file (default: empty string)",
|
|
28
|
+
});
|
|
29
|
+
const agentEnvironmentNameOption = () => Option.String("--environment-name", {
|
|
30
|
+
description: "Force specific environment display name to all tests in the run. Has lower priority than --environment and higher than the config value (default: empty string)",
|
|
31
|
+
});
|
|
32
|
+
const throwCliUsageError = (error) => {
|
|
33
|
+
if (isAgentUsageError(error)) {
|
|
34
|
+
throw new UsageError(error.message);
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
};
|
|
38
|
+
export class AgentCapabilitiesCommand extends Command {
|
|
39
|
+
constructor() {
|
|
40
|
+
super(...arguments);
|
|
41
|
+
this.json = Option.Boolean("--json", true, {
|
|
42
|
+
description: "Print capabilities as JSON (default: true)",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async execute() {
|
|
46
|
+
console.log(JSON.stringify(createAgentCapabilities(), null, 2));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
AgentCapabilitiesCommand.paths = [["agent", "capabilities"]];
|
|
50
|
+
AgentCapabilitiesCommand.usage = Command.Usage({
|
|
51
|
+
description: "Print structured Allure agent capability information",
|
|
52
|
+
details: "This command prints the locally supported agent-mode commands, expectation controls, output files, rerun support, and known unsupported capability families as JSON.",
|
|
53
|
+
examples: [
|
|
54
|
+
["agent capabilities", "Print agent capabilities as JSON"],
|
|
55
|
+
["agent capabilities --json", "Print agent capabilities as JSON explicitly"],
|
|
56
|
+
],
|
|
57
|
+
});
|
|
51
58
|
export class AgentCommand extends Command {
|
|
52
59
|
constructor() {
|
|
53
60
|
super(...arguments);
|
|
@@ -63,8 +70,44 @@ export class AgentCommand extends Command {
|
|
|
63
70
|
this.expectations = Option.String("--expectations", {
|
|
64
71
|
description: "The path to a YAML or JSON expectations file",
|
|
65
72
|
});
|
|
66
|
-
this.
|
|
67
|
-
|
|
73
|
+
this.goal = Option.Array("--goal", {
|
|
74
|
+
description: "The review goal to record in inline agent expectations",
|
|
75
|
+
});
|
|
76
|
+
this.taskId = Option.Array("--task-id", {
|
|
77
|
+
description: "The task or feature id to record in inline agent expectations",
|
|
78
|
+
});
|
|
79
|
+
this.expectTests = Option.Array("--expect-tests", {
|
|
80
|
+
description: "The expected number of visible logical tests in the intended scope",
|
|
81
|
+
});
|
|
82
|
+
this.expectLabels = Option.Array("--expect-label", {
|
|
83
|
+
description: "Expected label selector in name=value form. Repeat the option for multiple selectors",
|
|
84
|
+
});
|
|
85
|
+
this.expectEnvironments = Option.Array("--expect-env", {
|
|
86
|
+
description: "Expected environment id. Repeat the option for multiple environments",
|
|
87
|
+
});
|
|
88
|
+
this.expectFullNames = Option.Array("--expect-test", {
|
|
89
|
+
description: "Expected full test name. Repeat the option for multiple tests",
|
|
90
|
+
});
|
|
91
|
+
this.expectPrefixes = Option.Array("--expect-prefix", {
|
|
92
|
+
description: "Expected full-name prefix. Repeat the option for multiple prefixes",
|
|
93
|
+
});
|
|
94
|
+
this.forbidLabels = Option.Array("--forbid-label", {
|
|
95
|
+
description: "Forbidden label selector in name=value form. Repeat the option for multiple selectors",
|
|
96
|
+
});
|
|
97
|
+
this.expectStepContains = Option.Array("--expect-step-containing", {
|
|
98
|
+
description: "Require a test-scoped step name containing this text per evidence-target logical test",
|
|
99
|
+
});
|
|
100
|
+
this.expectSteps = Option.Array("--expect-steps", {
|
|
101
|
+
description: "Require at least this many meaningful steps per expected logical test",
|
|
102
|
+
});
|
|
103
|
+
this.expectAttachments = Option.Array("--expect-attachments", {
|
|
104
|
+
description: "Require at least this many non-missing attachments per expected logical test",
|
|
105
|
+
});
|
|
106
|
+
this.expectAttachmentFilters = Option.Array("--expect-attachment", {
|
|
107
|
+
description: "Require a matching non-missing attachment per expected logical test. Use a file name or name=value/content-type=value",
|
|
108
|
+
});
|
|
109
|
+
this.environment = agentEnvironmentOption();
|
|
110
|
+
this.environmentName = agentEnvironmentNameOption();
|
|
68
111
|
this.silent = Option.Boolean("--silent", {
|
|
69
112
|
description: "Don't pipe the process output logs to console (default: false)",
|
|
70
113
|
});
|
|
@@ -87,21 +130,80 @@ export class AgentCommand extends Command {
|
|
|
87
130
|
}
|
|
88
131
|
async execute() {
|
|
89
132
|
const args = this.commandToRun.filter((arg) => arg !== "--");
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
const configPath = readOptionalString(this.config);
|
|
134
|
+
const configuredCwd = readOptionalString(this.cwd);
|
|
135
|
+
const output = readOptionalString(this.output);
|
|
136
|
+
const expectations = readOptionalString(this.expectations);
|
|
137
|
+
if (!args || !args.length) {
|
|
138
|
+
throw new UsageError("expecting command to be specified after --, e.g. allure agent -- npm run test");
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const inlineExpectations = buildAgentInlineExpectations({
|
|
142
|
+
goal: this.goal,
|
|
143
|
+
taskId: this.taskId,
|
|
144
|
+
expectTests: this.expectTests,
|
|
145
|
+
expectLabels: readOptionalStringArray(this.expectLabels),
|
|
146
|
+
expectEnvironments: readOptionalStringArray(this.expectEnvironments),
|
|
147
|
+
expectFullNames: readOptionalStringArray(this.expectFullNames),
|
|
148
|
+
expectPrefixes: readOptionalStringArray(this.expectPrefixes),
|
|
149
|
+
forbidLabels: readOptionalStringArray(this.forbidLabels),
|
|
150
|
+
expectStepContains: readOptionalStringArray(this.expectStepContains),
|
|
151
|
+
expectSteps: this.expectSteps,
|
|
152
|
+
expectAttachments: this.expectAttachments,
|
|
153
|
+
expectAttachmentFilters: readOptionalStringArray(this.expectAttachmentFilters),
|
|
154
|
+
});
|
|
155
|
+
if (expectations && inlineExpectations) {
|
|
156
|
+
throw new AgentExpectationUsageError("Use either --expectations <file> or inline expectation flags, not both", "--expectations");
|
|
157
|
+
}
|
|
158
|
+
await validateAgentExpectationsFile({
|
|
159
|
+
cwd: await realpath(configuredCwd ?? process.cwd()),
|
|
160
|
+
output,
|
|
161
|
+
expectations,
|
|
162
|
+
});
|
|
163
|
+
const { executeAgentMode } = await import("./agent-run.js");
|
|
164
|
+
await executeAgentMode({
|
|
165
|
+
configPath,
|
|
166
|
+
cwd: configuredCwd,
|
|
167
|
+
output,
|
|
168
|
+
expectations,
|
|
169
|
+
inlineExpectations: inlineExpectations,
|
|
170
|
+
environment: readOptionalString(this.environment),
|
|
171
|
+
environmentName: readOptionalString(this.environmentName),
|
|
172
|
+
silent: readOptionalBoolean(this.silent),
|
|
173
|
+
rerunFrom: readOptionalString(this.rerunFrom),
|
|
174
|
+
rerunLatest: readOptionalBoolean(this.rerunLatest),
|
|
175
|
+
rerunPreset: readOptionalString(this.rerunPreset),
|
|
176
|
+
rerunEnvironments: readOptionalStringArray(this.rerunEnvironments),
|
|
177
|
+
rerunLabels: readOptionalStringArray(this.rerunLabels),
|
|
178
|
+
args,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (!isAgentExpectationUsageError(error)) {
|
|
183
|
+
throwCliUsageError(error);
|
|
184
|
+
}
|
|
185
|
+
const expectationError = error;
|
|
186
|
+
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
187
|
+
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
188
|
+
const commandString = formatAgentCommand(args);
|
|
189
|
+
const { generatedAt } = await writeInvalidAgentExpectationOutput({
|
|
190
|
+
outputDir,
|
|
191
|
+
command: commandString,
|
|
192
|
+
error: expectationError,
|
|
193
|
+
});
|
|
194
|
+
await persistLatestAgentState({
|
|
195
|
+
cwd,
|
|
196
|
+
outputDir,
|
|
197
|
+
command: commandString,
|
|
198
|
+
startedAt: generatedAt,
|
|
199
|
+
finishedAt: generatedAt,
|
|
200
|
+
status: "finished",
|
|
201
|
+
exitCode: 1,
|
|
202
|
+
});
|
|
203
|
+
printAgentOutputLinks(outputDir);
|
|
204
|
+
console.error(expectationError.message);
|
|
205
|
+
exit(1);
|
|
206
|
+
}
|
|
105
207
|
}
|
|
106
208
|
}
|
|
107
209
|
AgentCommand.paths = [["agent"]];
|
|
@@ -139,16 +241,19 @@ export class AgentLatestCommand extends Command {
|
|
|
139
241
|
exit(1);
|
|
140
242
|
return;
|
|
141
243
|
}
|
|
142
|
-
|
|
244
|
+
printAgentOutputLinks(latestState.outputDir);
|
|
143
245
|
}
|
|
144
246
|
}
|
|
145
247
|
AgentLatestCommand.paths = [["agent", "latest"]];
|
|
146
248
|
AgentLatestCommand.usage = Command.Usage({
|
|
147
|
-
description: "Print the latest Allure agent output directory for the current project",
|
|
148
|
-
details: "This command prints the latest agent output directory recorded for the resolved project cwd.",
|
|
249
|
+
description: "Print the latest Allure agent output directory and index path for the current project",
|
|
250
|
+
details: "This command prints the latest agent output directory and index.md path recorded for the resolved project cwd.",
|
|
149
251
|
examples: [
|
|
150
|
-
["agent latest", "Print the latest agent output directory for the current project"],
|
|
151
|
-
[
|
|
252
|
+
["agent latest", "Print the latest agent output directory and index path for the current project"],
|
|
253
|
+
[
|
|
254
|
+
"agent latest --cwd ./packages/cli",
|
|
255
|
+
"Print the latest agent output directory and index path for a specific project cwd",
|
|
256
|
+
],
|
|
152
257
|
],
|
|
153
258
|
});
|
|
154
259
|
export class AgentStateDirCommand extends Command {
|
|
@@ -172,6 +277,95 @@ AgentStateDirCommand.usage = Command.Usage({
|
|
|
172
277
|
["agent state-dir --cwd ./packages/cli", "Print the resolved state directory for a specific project cwd"],
|
|
173
278
|
],
|
|
174
279
|
});
|
|
280
|
+
export class AgentQueryCommand extends Command {
|
|
281
|
+
constructor() {
|
|
282
|
+
super(...arguments);
|
|
283
|
+
this.view = Option.String({
|
|
284
|
+
required: false,
|
|
285
|
+
name: "Query view: summary, tests, findings, or test (default: summary)",
|
|
286
|
+
});
|
|
287
|
+
this.cwd = Option.String("--cwd", {
|
|
288
|
+
description: "The project directory used to resolve --latest and relative paths (default: current working directory)",
|
|
289
|
+
});
|
|
290
|
+
this.from = Option.String("--from", {
|
|
291
|
+
description: "The prior agent output directory to query",
|
|
292
|
+
});
|
|
293
|
+
this.latest = Option.Boolean("--latest", {
|
|
294
|
+
description: "Use the latest recorded agent output for the current project cwd",
|
|
295
|
+
});
|
|
296
|
+
this.statuses = Option.Array("--status", {
|
|
297
|
+
description: "Filter tests by status: failed, broken, unknown, skipped, or passed. Repeat for multiple statuses",
|
|
298
|
+
});
|
|
299
|
+
this.environments = Option.Array("--environment", {
|
|
300
|
+
description: "Filter tests by environment id. Repeat the option for multiple environments",
|
|
301
|
+
});
|
|
302
|
+
this.labels = Option.Array("--label", {
|
|
303
|
+
description: "Filter tests by exact label name=value. Repeat the option for multiple filters",
|
|
304
|
+
});
|
|
305
|
+
this.severities = Option.Array("--severity", {
|
|
306
|
+
description: "Filter findings by severity: high, warning, or info. Repeat for multiple severities",
|
|
307
|
+
});
|
|
308
|
+
this.categories = Option.Array("--category", {
|
|
309
|
+
description: "Filter findings by category. Repeat the option for multiple categories",
|
|
310
|
+
});
|
|
311
|
+
this.checks = Option.Array("--check", {
|
|
312
|
+
description: "Filter findings by check name. Repeat the option for multiple checks",
|
|
313
|
+
});
|
|
314
|
+
this.test = Option.String("--test", {
|
|
315
|
+
description: "Filter to one test by full name, test result id, history id, or markdown path",
|
|
316
|
+
});
|
|
317
|
+
this.limit = Option.String("--limit", {
|
|
318
|
+
description: "Limit returned tests or findings to this non-negative count",
|
|
319
|
+
});
|
|
320
|
+
this.includeMarkdown = Option.Boolean("--include-markdown", {
|
|
321
|
+
description: "Include the per-test markdown content for the test view",
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
async execute() {
|
|
325
|
+
try {
|
|
326
|
+
const cwd = await realpath(readOptionalString(this.cwd) ?? process.cwd());
|
|
327
|
+
const view = normalizeAgentQueryView(readOptionalString(this.view));
|
|
328
|
+
const outputDir = await resolveAgentSelectionOutputDir({
|
|
329
|
+
cwd,
|
|
330
|
+
from: readOptionalString(this.from),
|
|
331
|
+
latest: readOptionalBoolean(this.latest),
|
|
332
|
+
});
|
|
333
|
+
const output = await loadAgentOutput(outputDir);
|
|
334
|
+
const payload = await buildAgentQueryPayload(output, view, {
|
|
335
|
+
environments: normalizeRepeatedStringValues(readOptionalStringArray(this.environments)),
|
|
336
|
+
labelFilters: parseAgentLabelFilters(readOptionalStringArray(this.labels)),
|
|
337
|
+
statuses: normalizeRepeatedEnumValues(readOptionalStringArray(this.statuses), AGENT_TEST_STATUSES, "--status"),
|
|
338
|
+
severities: normalizeRepeatedEnumValues(readOptionalStringArray(this.severities), AGENT_FINDING_SEVERITIES, "--severity"),
|
|
339
|
+
categories: normalizeRepeatedEnumValues(readOptionalStringArray(this.categories), AGENT_FINDING_CATEGORIES, "--category"),
|
|
340
|
+
checks: normalizeRepeatedStringValues(readOptionalStringArray(this.checks)),
|
|
341
|
+
test: readOptionalString(this.test),
|
|
342
|
+
limit: normalizeAgentQueryLimit(readOptionalString(this.limit)),
|
|
343
|
+
includeMarkdown: readOptionalBoolean(this.includeMarkdown),
|
|
344
|
+
});
|
|
345
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
throwCliUsageError(error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
AgentQueryCommand.paths = [["agent", "query"]];
|
|
353
|
+
AgentQueryCommand.usage = Command.Usage({
|
|
354
|
+
description: "Query an existing Allure agent output directory as focused JSON",
|
|
355
|
+
details: "This command reads a prior agent output directory and prints focused JSON for a run summary, test list, findings list, or one test. Use --latest to query the latest recorded output for the project, or --from to query a specific output directory.",
|
|
356
|
+
examples: [
|
|
357
|
+
["agent query --latest summary", "Print a summary for the latest agent output"],
|
|
358
|
+
["agent query --from ./out/agent-output tests --status failed", "List failed tests from a prior output"],
|
|
359
|
+
[
|
|
360
|
+
"agent query --from ./out/agent-output findings --severity high",
|
|
361
|
+
"List high-severity findings from a prior output",
|
|
362
|
+
],
|
|
363
|
+
[
|
|
364
|
+
'agent query --latest test --test "suite should pass" --include-markdown',
|
|
365
|
+
"Print one test summary with its per-test markdown",
|
|
366
|
+
],
|
|
367
|
+
],
|
|
368
|
+
});
|
|
175
369
|
export class AgentSelectCommand extends Command {
|
|
176
370
|
constructor() {
|
|
177
371
|
super(...arguments);
|
|
@@ -198,177 +392,52 @@ export class AgentSelectCommand extends Command {
|
|
|
198
392
|
});
|
|
199
393
|
}
|
|
200
394
|
async execute() {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
395
|
+
try {
|
|
396
|
+
const cwd = await realpath(readOptionalString(this.cwd) ?? process.cwd());
|
|
397
|
+
const environments = readOptionalStringArray(this.environments);
|
|
398
|
+
const labels = readOptionalStringArray(this.labels);
|
|
399
|
+
const outputDir = await resolveAgentSelectionOutputDir({
|
|
400
|
+
cwd,
|
|
401
|
+
from: readOptionalString(this.from),
|
|
402
|
+
latest: readOptionalBoolean(this.latest),
|
|
403
|
+
});
|
|
404
|
+
const selection = await selectAgentTestPlan({
|
|
405
|
+
outputDir,
|
|
406
|
+
preset: normalizeAgentRerunPreset(readOptionalString(this.preset)),
|
|
407
|
+
environments: environments?.length ? environments : undefined,
|
|
408
|
+
labelFilters: parseAgentLabelFilters(labels),
|
|
409
|
+
});
|
|
410
|
+
if (!selection.testPlan.tests.length) {
|
|
411
|
+
console.error(`No tests matched selection in ${selection.outputDir}`);
|
|
412
|
+
exit(1);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const serialized = `${JSON.stringify(selection.testPlan, null, 2)}\n`;
|
|
416
|
+
const output = readOptionalString(this.output);
|
|
417
|
+
if (!output) {
|
|
418
|
+
console.log(serialized.trimEnd());
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const outputPath = resolve(cwd, output);
|
|
422
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
423
|
+
await writeFile(outputPath, serialized, "utf-8");
|
|
424
|
+
console.log(`agent testplan: ${outputPath}`);
|
|
425
|
+
console.log(`agent selection source: ${selection.outputDir}`);
|
|
426
|
+
console.log(`agent selection preset: ${selection.preset}`);
|
|
427
|
+
console.log(`agent selection tests: ${selection.selectedTests.length}`);
|
|
219
428
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (!output) {
|
|
223
|
-
console.log(serialized.trimEnd());
|
|
224
|
-
return;
|
|
429
|
+
catch (error) {
|
|
430
|
+
throwCliUsageError(error);
|
|
225
431
|
}
|
|
226
|
-
const outputPath = resolve(cwd, output);
|
|
227
|
-
await mkdir(dirname(outputPath), { recursive: true });
|
|
228
|
-
await writeFile(outputPath, serialized, "utf-8");
|
|
229
|
-
console.log(outputPath);
|
|
230
432
|
}
|
|
231
433
|
}
|
|
232
434
|
AgentSelectCommand.paths = [["agent", "select"]];
|
|
233
435
|
AgentSelectCommand.usage = Command.Usage({
|
|
234
436
|
description: "Select tests from an existing agent output and emit a test plan",
|
|
235
|
-
details: "This command resolves a set of tests from a prior agent run and prints or writes a testplan.json payload.",
|
|
437
|
+
details: "This command resolves a set of tests from a prior agent run and prints or writes a testplan.json payload. When --output is used, stdout contains the written test plan path, source output directory, preset, and selected test count.",
|
|
236
438
|
examples: [
|
|
237
439
|
["agent select --from ./out/agent-output", "Print a test plan for the default review-targeted tests"],
|
|
238
440
|
["agent select --latest --preset failed", "Print a test plan for failed tests from the latest project run"],
|
|
239
441
|
["agent select --from ./out/agent-output --output ./testplan.json", "Write the selected test plan to a file"],
|
|
240
442
|
],
|
|
241
443
|
});
|
|
242
|
-
export const executeAgentMode = async (params) => {
|
|
243
|
-
const { configPath, cwd: configuredCwd, output, expectations, environment, environmentName, silent, rerunFrom, rerunLatest, rerunPreset, rerunEnvironments, rerunLabels, args, } = params;
|
|
244
|
-
if (!args || !args.length) {
|
|
245
|
-
throw new UsageError("expecting command to be specified after --, e.g. allure agent -- npm run test");
|
|
246
|
-
}
|
|
247
|
-
const command = args[0];
|
|
248
|
-
const commandArgs = args.slice(1);
|
|
249
|
-
const cwd = await realpath(configuredCwd ?? process.cwd());
|
|
250
|
-
const commandString = `${command} ${commandArgs.join(" ")}`;
|
|
251
|
-
const hasRerunSource = !!rerunFrom || !!rerunLatest;
|
|
252
|
-
const hasRerunFilters = !!rerunPreset || !!rerunEnvironments?.length || !!rerunLabels?.length;
|
|
253
|
-
if (!hasRerunSource && hasRerunFilters) {
|
|
254
|
-
throw new UsageError("Use rerun filters only together with --rerun-from <path> or --rerun-latest");
|
|
255
|
-
}
|
|
256
|
-
const rerunContext = await createAgentTestPlanContext({
|
|
257
|
-
cwd,
|
|
258
|
-
from: rerunFrom,
|
|
259
|
-
latest: rerunLatest,
|
|
260
|
-
preset: normalizeAgentRerunPreset(rerunPreset),
|
|
261
|
-
environments: rerunEnvironments?.length ? rerunEnvironments : undefined,
|
|
262
|
-
labelFilters: parseAgentLabelFilters(rerunLabels),
|
|
263
|
-
});
|
|
264
|
-
const childEnvironmentVariables = {
|
|
265
|
-
...createChildAllureCliEnvironment("agent"),
|
|
266
|
-
...(rerunContext ? { ALLURE_TESTPLAN_PATH: rerunContext.testPlanPath } : {}),
|
|
267
|
-
};
|
|
268
|
-
try {
|
|
269
|
-
if (getActiveAllureCliCommand()) {
|
|
270
|
-
console.log(commandString);
|
|
271
|
-
const exitCode = await executeNestedAllureCommand({
|
|
272
|
-
command,
|
|
273
|
-
commandArgs,
|
|
274
|
-
cwd,
|
|
275
|
-
...(rerunContext ? { environmentVariables: { ALLURE_TESTPLAN_PATH: rerunContext.testPlanPath } } : {}),
|
|
276
|
-
silent,
|
|
277
|
-
});
|
|
278
|
-
exit(exitCode ?? -1);
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
const outputDir = output ? resolve(cwd, output) : await mkdtemp(join(tmpdir(), "allure-agent-"));
|
|
282
|
-
const expectationsPath = expectations ? resolve(cwd, expectations) : undefined;
|
|
283
|
-
const environmentOptions = {
|
|
284
|
-
environment,
|
|
285
|
-
environmentName,
|
|
286
|
-
};
|
|
287
|
-
normalizeCommandEnvironmentOptions(environmentOptions);
|
|
288
|
-
if (expectationsPath && isPathInside(outputDir, expectationsPath)) {
|
|
289
|
-
throw new UsageError(`--expectations path ${JSON.stringify(expectationsPath)} must not be inside the agent output directory ${JSON.stringify(outputDir)}`);
|
|
290
|
-
}
|
|
291
|
-
const config = await readConfig(cwd, configPath, {
|
|
292
|
-
output: outputDir,
|
|
293
|
-
plugins: {
|
|
294
|
-
agent: {
|
|
295
|
-
options: {
|
|
296
|
-
outputDir,
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
},
|
|
300
|
-
});
|
|
301
|
-
const resolvedEnvironment = resolveCommandEnvironment(config, environmentOptions);
|
|
302
|
-
try {
|
|
303
|
-
await rm(outputDir, { recursive: true });
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
if (!isFileNotFoundError(error)) {
|
|
307
|
-
console.error("could not clean output directory", error);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
const startedAt = new Date().toISOString();
|
|
311
|
-
await persistLatestAgentState({
|
|
312
|
-
cwd,
|
|
313
|
-
outputDir,
|
|
314
|
-
expectationsPath,
|
|
315
|
-
command: commandString,
|
|
316
|
-
startedAt,
|
|
317
|
-
status: "running",
|
|
318
|
-
});
|
|
319
|
-
console.log(`agent output: ${outputDir}`);
|
|
320
|
-
if (expectationsPath) {
|
|
321
|
-
console.log(`agent expectations: ${expectationsPath}`);
|
|
322
|
-
}
|
|
323
|
-
console.log(commandString);
|
|
324
|
-
const allureReport = new AllureReport({
|
|
325
|
-
...config,
|
|
326
|
-
output: outputDir,
|
|
327
|
-
environment: resolvedEnvironment?.id,
|
|
328
|
-
open: false,
|
|
329
|
-
port: undefined,
|
|
330
|
-
qualityGate: undefined,
|
|
331
|
-
allureService: undefined,
|
|
332
|
-
realTime: false,
|
|
333
|
-
plugins: config.plugins,
|
|
334
|
-
});
|
|
335
|
-
const knownIssues = await allureReport.store.allKnownIssues();
|
|
336
|
-
const { globalExitCode } = await withProcessEnv({
|
|
337
|
-
ALLURE_AGENT_OUTPUT: outputDir,
|
|
338
|
-
ALLURE_AGENT_EXPECTATIONS: expectationsPath,
|
|
339
|
-
ALLURE_AGENT_COMMAND: commandString,
|
|
340
|
-
ALLURE_AGENT_PROJECT_ROOT: cwd,
|
|
341
|
-
ALLURE_AGENT_NAME: undefined,
|
|
342
|
-
ALLURE_AGENT_LOOP_ID: undefined,
|
|
343
|
-
ALLURE_AGENT_TASK_ID: undefined,
|
|
344
|
-
ALLURE_AGENT_CONVERSATION_ID: undefined,
|
|
345
|
-
}, async () => await executeAllureRun({
|
|
346
|
-
allureReport,
|
|
347
|
-
knownIssues,
|
|
348
|
-
cwd,
|
|
349
|
-
command,
|
|
350
|
-
commandArgs,
|
|
351
|
-
environmentVariables: childEnvironmentVariables,
|
|
352
|
-
environment: resolvedEnvironment?.id,
|
|
353
|
-
withQualityGate: false,
|
|
354
|
-
logs: "pipe",
|
|
355
|
-
silent,
|
|
356
|
-
ignoreLogs: false,
|
|
357
|
-
logProcessExit: false,
|
|
358
|
-
}));
|
|
359
|
-
await persistLatestAgentState({
|
|
360
|
-
cwd,
|
|
361
|
-
outputDir,
|
|
362
|
-
expectationsPath,
|
|
363
|
-
command: commandString,
|
|
364
|
-
startedAt,
|
|
365
|
-
finishedAt: new Date().toISOString(),
|
|
366
|
-
status: "finished",
|
|
367
|
-
exitCode: globalExitCode.actual ?? globalExitCode.original,
|
|
368
|
-
});
|
|
369
|
-
exit(globalExitCode.actual ?? globalExitCode.original);
|
|
370
|
-
}
|
|
371
|
-
finally {
|
|
372
|
-
await rerunContext?.cleanup();
|
|
373
|
-
}
|
|
374
|
-
};
|
package/dist/commands/open.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
5
|
import { cwd as processCwd, exit } from "node:process";
|
|
6
6
|
import { readConfig } from "@allurereport/core";
|
|
7
7
|
import { serve } from "@allurereport/static-server";
|
|
@@ -76,7 +76,7 @@ export class OpenCommand extends Command {
|
|
|
76
76
|
resolveReportPath(cwd, inputs, fallback = "allure-report") {
|
|
77
77
|
if (inputs.length <= 1) {
|
|
78
78
|
const [maybeRelativeReportPath = fallback] = inputs;
|
|
79
|
-
return
|
|
79
|
+
return resolve(cwd, maybeRelativeReportPath);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
async reportExists(reportPath) {
|
package/dist/commands/run.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as console from "node:console";
|
|
2
2
|
import { realpath, rm } from "node:fs/promises";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
3
|
import process, { exit } from "node:process";
|
|
5
4
|
import { AllureReport, isFileNotFoundError, readConfig } from "@allurereport/core";
|
|
6
5
|
import Awesome from "@allurereport/plugin-awesome";
|
|
@@ -9,7 +8,6 @@ import { Command, Option, UsageError } from "clipanion";
|
|
|
9
8
|
import { red } from "yoctocolors";
|
|
10
9
|
import { environmentNameOption, environmentOption, normalizeCommandEnvironmentOptions, resolveCommandEnvironment, } from "../utils/environment.js";
|
|
11
10
|
import { createChildAllureCliEnvironment, getActiveAllureCliCommand } from "../utils/execution-context.js";
|
|
12
|
-
import { executeAgentMode } from "./agent.js";
|
|
13
11
|
import { executeAllureRun, executeNestedAllureCommand } from "./commons/run.js";
|
|
14
12
|
export class RunCommand extends Command {
|
|
15
13
|
constructor() {
|
|
@@ -65,22 +63,6 @@ export class RunCommand extends Command {
|
|
|
65
63
|
if (!args || !args.length) {
|
|
66
64
|
throw new UsageError("expecting command to be specified after --, e.g. allure run -- npm run test");
|
|
67
65
|
}
|
|
68
|
-
const legacyAgentOutput = process.env.ALLURE_AGENT_OUTPUT;
|
|
69
|
-
if (legacyAgentOutput) {
|
|
70
|
-
await executeAgentMode({
|
|
71
|
-
configPath: this.config,
|
|
72
|
-
cwd: this.cwd,
|
|
73
|
-
output: resolve(process.cwd(), legacyAgentOutput),
|
|
74
|
-
expectations: process.env.ALLURE_AGENT_EXPECTATIONS
|
|
75
|
-
? resolve(process.cwd(), process.env.ALLURE_AGENT_EXPECTATIONS)
|
|
76
|
-
: undefined,
|
|
77
|
-
environment: this.environment,
|
|
78
|
-
environmentName: this.environmentName,
|
|
79
|
-
silent: this.silent,
|
|
80
|
-
args,
|
|
81
|
-
});
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
66
|
const before = new Date().getTime();
|
|
85
67
|
process.on("exit", (exitCode) => {
|
|
86
68
|
const after = new Date().getTime();
|