allure 3.20.1 → 3.22.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.
Files changed (48) hide show
  1. package/README.md +64 -3
  2. package/dist/commands/{agent.js → agents/index.js} +13 -109
  3. package/dist/commands/agents/lifecycle.d.ts +20 -0
  4. package/dist/commands/agents/lifecycle.js +91 -0
  5. package/dist/commands/{agent-run.d.ts → agents/run.d.ts} +1 -13
  6. package/dist/commands/{agent-run.js → agents/run.js} +12 -67
  7. package/dist/commands/commons/generate.d.ts +1 -0
  8. package/dist/commands/commons/generate.js +28 -2
  9. package/dist/commands/generate.d.ts +2 -0
  10. package/dist/commands/generate.js +19 -1
  11. package/dist/commands/ghWorkflow.d.ts +11 -0
  12. package/dist/commands/ghWorkflow.js +77 -0
  13. package/dist/commands/index.d.ts +4 -1
  14. package/dist/commands/index.js +4 -1
  15. package/dist/commands/jira/clear.js +3 -3
  16. package/dist/commands/migrate.d.ts +6 -0
  17. package/dist/commands/migrate.js +15 -0
  18. package/dist/commands/open.d.ts +3 -10
  19. package/dist/commands/open.js +20 -99
  20. package/dist/commands/qualityGate.js +11 -1
  21. package/dist/commands/run.d.ts +4 -0
  22. package/dist/commands/run.js +27 -5
  23. package/dist/commands/serve.d.ts +19 -0
  24. package/dist/commands/serve.js +168 -0
  25. package/dist/commands/slack.d.ts +3 -2
  26. package/dist/commands/slack.js +17 -5
  27. package/dist/commands/testopsPlan.js +4 -4
  28. package/dist/commands/watch.d.ts +1 -0
  29. package/dist/commands/watch.js +10 -1
  30. package/dist/index.js +32 -9
  31. package/dist/utils/allure2Hints.d.ts +8 -0
  32. package/dist/utils/allure2Hints.js +90 -0
  33. package/dist/utils/cleanOutput.d.ts +9 -0
  34. package/dist/utils/cleanOutput.js +62 -0
  35. package/dist/utils/fileSystem.d.ts +4 -1
  36. package/dist/utils/fileSystem.js +3 -3
  37. package/dist/utils/ghWorkflow.d.ts +10 -0
  38. package/dist/utils/ghWorkflow.js +75 -0
  39. package/dist/utils/serverOptions.d.ts +5 -0
  40. package/dist/utils/serverOptions.js +4 -0
  41. package/dist/utils/updateNotifier.d.ts +6 -0
  42. package/dist/utils/updateNotifier.js +58 -0
  43. package/dist/utils/verbosity.d.ts +7 -0
  44. package/dist/utils/verbosity.js +32 -0
  45. package/package.json +35 -28
  46. /package/dist/commands/{agent-human-report.d.ts → agents/human-report.d.ts} +0 -0
  47. /package/dist/commands/{agent-human-report.js → agents/human-report.js} +0 -0
  48. /package/dist/commands/{agent.d.ts → agents/index.d.ts} +0 -0
@@ -2,6 +2,7 @@ import { cwd as processCwd } from "node:process";
2
2
  import { readConfig } from "@allurereport/core";
3
3
  import { serve } from "@allurereport/static-server";
4
4
  import { Command, Option } from "clipanion";
5
+ import { serverOptionsFromConfig } from "../utils/serverOptions.js";
5
6
  import { generate } from "./commons/generate.js";
6
7
  export class GenerateCommand extends Command {
7
8
  constructor() {
@@ -26,12 +27,19 @@ export class GenerateCommand extends Command {
26
27
  "Allure loads the matched archives before generating the report. " +
27
28
  "This option can be specified multiple times.",
28
29
  });
30
+ this.clean = Option.Boolean("--clean", {
31
+ description: "Remove the output directory before generating the report (default: false). " +
32
+ "Refuses to remove the working directory, the home directory, a filesystem root, or the results directories",
33
+ });
29
34
  this.open = Option.Boolean("--open", {
30
35
  description: "Open the report in the default browser after generation (default: false)",
31
36
  });
32
37
  this.port = Option.String("--port", {
33
38
  description: "The port to serve the reports on. If not set, the server starts on a random port",
34
39
  });
40
+ this.host = Option.String("--host", {
41
+ description: "The host (network interface) to serve the reports on, e.g. 127.0.0.1 or 0.0.0.0 (default: all interfaces)",
42
+ });
35
43
  this.historyLimit = Option.String("--history-limit", {
36
44
  description: "Limits the number of history entries to keep (default: unlimited)",
37
45
  });
@@ -54,6 +62,7 @@ export class GenerateCommand extends Command {
54
62
  output: this.output,
55
63
  open: this.open,
56
64
  port: this.port,
65
+ host: this.host,
57
66
  hideLabels,
58
67
  historyLimit: this.historyLimit !== undefined ? parseInt(this.historyLimit, 10) : undefined,
59
68
  ...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
@@ -64,13 +73,14 @@ export class GenerateCommand extends Command {
64
73
  resultsDir: this.resultsDir,
65
74
  cwd,
66
75
  config,
76
+ clean: this.clean,
67
77
  });
68
78
  if (!result) {
69
79
  return;
70
80
  }
71
81
  if (config.open) {
72
82
  await serve({
73
- port: config.port ? parseInt(config.port, 10) : undefined,
83
+ ...serverOptionsFromConfig(config),
74
84
  servePath: config.output,
75
85
  open: true,
76
86
  });
@@ -84,11 +94,19 @@ GenerateCommand.usage = Command.Usage({
84
94
  details: "This command generates a report from the provided Allure Results directories. " +
85
95
  "When a quality gate is configured, the command validates the loaded results and finishes writing the report or dump before returning its status.",
86
96
  examples: [
97
+ [
98
+ "generate ./allure-results --open --host 0.0.0.0",
99
+ "Generate a report and serve it on all network interfaces after generation",
100
+ ],
87
101
  ["generate ./allure-results", "Generate a report from the ./allure-results directory"],
88
102
  [
89
103
  "generate ./allure-results --output custom-report",
90
104
  "Generate a report from the ./allure-results directory to the custom-report directory",
91
105
  ],
106
+ [
107
+ "generate ./allure-results --clean",
108
+ "Remove the existing report directory before generating a fresh report from ./allure-results",
109
+ ],
92
110
  [
93
111
  "generate --dump=windows.zip --dump=macos.zip ./allure-results",
94
112
  "Generate a report using data from windows.zip and macos.zip archives and using results from the ./allure-results directory",
@@ -0,0 +1,11 @@
1
+ import { Command } from "clipanion";
2
+ export declare class GhWorkflowCommand extends Command {
3
+ #private;
4
+ static paths: string[][];
5
+ static usage: import("clipanion").Usage;
6
+ environments: string[] | undefined;
7
+ config: string | undefined;
8
+ cwd: string | undefined;
9
+ testCommand: string[];
10
+ execute(): Promise<0 | 1>;
11
+ }
@@ -0,0 +1,77 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _GhWorkflowCommand_instances, _GhWorkflowCommand_readConfiguredEnvironmentIds;
7
+ import { realpath } from "node:fs/promises";
8
+ import process from "node:process";
9
+ import { readConfig } from "@allurereport/core";
10
+ import { Command, Option, UsageError } from "clipanion";
11
+ import { buildGhWorkflowYaml, configuredEnvironmentIds, normalizeEnvironmentIds } from "../utils/ghWorkflow.js";
12
+ const describeError = (error) => {
13
+ if (error instanceof UsageError) {
14
+ return error.message;
15
+ }
16
+ return error instanceof Error ? (error.stack ?? error.message) : String(error);
17
+ };
18
+ export class GhWorkflowCommand extends Command {
19
+ constructor() {
20
+ super(...arguments);
21
+ _GhWorkflowCommand_instances.add(this);
22
+ this.environments = Option.Array("--environment,--env", {
23
+ description: "Environment id to include. Can be repeated. Default: every environment from the config",
24
+ });
25
+ this.config = Option.String("--config,-c", {
26
+ description: "The path to Allure config file. Also passed to the generated allure commands",
27
+ });
28
+ this.cwd = Option.String("--cwd", {
29
+ description: "The working directory for the command to run (default: current working directory)",
30
+ });
31
+ this.testCommand = Option.Rest({
32
+ name: "Test command to run. Pass it after `--` (default: npm test)",
33
+ });
34
+ }
35
+ async execute() {
36
+ try {
37
+ const environmentIds = this.environments !== undefined && this.environments.length > 0
38
+ ? normalizeEnvironmentIds(this.environments)
39
+ : await __classPrivateFieldGet(this, _GhWorkflowCommand_instances, "m", _GhWorkflowCommand_readConfiguredEnvironmentIds).call(this);
40
+ const yaml = buildGhWorkflowYaml({
41
+ environmentIds,
42
+ testCommand: this.testCommand.length > 0 ? this.testCommand : undefined,
43
+ config: this.config,
44
+ });
45
+ this.context.stdout.write(yaml);
46
+ return 0;
47
+ }
48
+ catch (error) {
49
+ this.context.stderr.write(`${describeError(error)}\n`);
50
+ return 1;
51
+ }
52
+ }
53
+ }
54
+ _GhWorkflowCommand_instances = new WeakSet(), _GhWorkflowCommand_readConfiguredEnvironmentIds = async function _GhWorkflowCommand_readConfiguredEnvironmentIds() {
55
+ const cwd = await realpath(this.cwd ?? process.cwd());
56
+ const config = await readConfig(cwd, this.config, { plugins: {} });
57
+ return configuredEnvironmentIds(config.environments);
58
+ };
59
+ GhWorkflowCommand.paths = [["gh-workflow"]];
60
+ GhWorkflowCommand.usage = Command.Usage({
61
+ category: "Integrations",
62
+ description: "Prints GitHub Actions jobs that run tests per environment and build one Allure report",
63
+ details: `
64
+ Prints a \`test\` job (a matrix over the environments: \`allure run\` with a dump, then the dump is uploaded)
65
+ and a \`report\` job (downloads all dumps and runs \`allure generate\`). Paste the output under \`jobs:\` of a
66
+ workflow file and fill in the TODO steps. The names of the environment, the dump file, the artifact and the
67
+ download pattern are kept consistent for you.
68
+ Environments come from \`--env\`; without it, the \`environments\` declared in the Allure config are used.
69
+ Errors are written to stderr and the exit code is non-zero. Command-line syntax errors are reported by the CLI
70
+ parser on stdout. Versions of the GitHub actions in the output are examples, pin the ones you use.
71
+ `,
72
+ examples: [
73
+ ["gh-workflow --env foo --env bar --env baz", "Print the jobs for three environments (test command: npm test)"],
74
+ ["gh-workflow --env linux --env windows -- yarn test", "Use a custom test command"],
75
+ ["gh-workflow --config ./allurerc.mjs", "Use the environments declared in the config"],
76
+ ],
77
+ });
@@ -2,7 +2,7 @@ export * from "./history.js";
2
2
  export * from "./testplan.js";
3
3
  export * from "./classic.js";
4
4
  export * from "./allure2.js";
5
- export * from "./agent.js";
5
+ export * from "./agents/index.js";
6
6
  export * from "./awesome.js";
7
7
  export * from "./csv.js";
8
8
  export * from "./check.js";
@@ -16,7 +16,10 @@ export * from "./open.js";
16
16
  export * from "./qualityGate.js";
17
17
  export * from "./log.js";
18
18
  export * from "./generate.js";
19
+ export * from "./ghWorkflow.js";
19
20
  export * from "./dashboard.js";
20
21
  export * from "./results/index.js";
21
22
  export * from "./jira/index.js";
22
23
  export * from "./gitlab/index.js";
24
+ export * from "./migrate.js";
25
+ export * from "./serve.js";
@@ -2,7 +2,7 @@ export * from "./history.js";
2
2
  export * from "./testplan.js";
3
3
  export * from "./classic.js";
4
4
  export * from "./allure2.js";
5
- export * from "./agent.js";
5
+ export * from "./agents/index.js";
6
6
  export * from "./awesome.js";
7
7
  export * from "./csv.js";
8
8
  export * from "./check.js";
@@ -16,7 +16,10 @@ export * from "./open.js";
16
16
  export * from "./qualityGate.js";
17
17
  export * from "./log.js";
18
18
  export * from "./generate.js";
19
+ export * from "./ghWorkflow.js";
19
20
  export * from "./dashboard.js";
20
21
  export * from "./results/index.js";
21
22
  export * from "./jira/index.js";
22
23
  export * from "./gitlab/index.js";
24
+ export * from "./migrate.js";
25
+ export * from "./serve.js";
@@ -63,7 +63,7 @@ export class JiraClearCommand extends BaseJiraCommand {
63
63
  await jiraPlugin.clearAll(this.issues);
64
64
  this.context.stdout.write(green("All reports and test results have been unlinked from the specified Jira issue"));
65
65
  }
66
- catch (error) {
66
+ catch {
67
67
  this.context.stderr.write(red("Failed to unlink reports and test results from the specified Jira issue"));
68
68
  exit(1);
69
69
  }
@@ -73,7 +73,7 @@ export class JiraClearCommand extends BaseJiraCommand {
73
73
  await jiraPlugin.clearReports(this.issues);
74
74
  this.context.stdout.write(green("All reports have been unlinked from the specified Jira issue"));
75
75
  }
76
- catch (error) {
76
+ catch {
77
77
  this.context.stderr.write(red("Failed to unlink reports from the specified Jira issue"));
78
78
  exit(1);
79
79
  }
@@ -83,7 +83,7 @@ export class JiraClearCommand extends BaseJiraCommand {
83
83
  await jiraPlugin.clearResults(this.issues);
84
84
  this.context.stdout.write(green("All test results have been unlinked from the specified Jira issue"));
85
85
  }
86
- catch (error) {
86
+ catch {
87
87
  this.context.stderr.write(red("Failed to unlink test results from the specified Jira issue"));
88
88
  exit(1);
89
89
  }
@@ -0,0 +1,6 @@
1
+ import { Command } from "clipanion";
2
+ export declare class MigrateCommand extends Command {
3
+ static paths: string[][];
4
+ static usage: import("clipanion").Usage;
5
+ execute(): Promise<void>;
6
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from "clipanion";
2
+ import { renderAllure2CommandMap } from "../utils/allure2Hints.js";
3
+ export class MigrateCommand extends Command {
4
+ async execute() {
5
+ this.context.stdout.write(`${renderAllure2CommandMap()}\n`);
6
+ }
7
+ }
8
+ MigrateCommand.paths = [["migrate"]];
9
+ MigrateCommand.usage = Command.Usage({
10
+ category: "Reports",
11
+ description: "Shows how Allure 2 commands map to Allure 3",
12
+ details: "This command prints the Allure 3 equivalents of the Allure 2 commands and options " +
13
+ "(serve, generate --clean, --host, --profile, etc.) for users who are moving from Allure 2.",
14
+ examples: [["migrate", "Print the Allure 2 to Allure 3 command mapping"]],
15
+ });
@@ -1,13 +1,6 @@
1
- import { Command } from "clipanion";
2
- export declare class OpenCommand extends Command {
1
+ import { BaseServeCommand } from "./serve.js";
2
+ export declare class OpenCommand extends BaseServeCommand {
3
3
  static paths: string[][];
4
4
  static usage: import("clipanion").Usage;
5
- resultsDir: string[];
6
- config: string | undefined;
7
- port: string | undefined;
8
- cwd: string | undefined;
9
- hideLabels: string[] | undefined;
10
- execute(): Promise<void>;
11
- private resolveReportPath;
12
- private reportExists;
5
+ protected get shouldOpen(): boolean;
13
6
  }
@@ -1,110 +1,31 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdtemp, rm } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { cwd as processCwd, exit } from "node:process";
6
- import { readConfig } from "@allurereport/core";
7
- import { serve } from "@allurereport/static-server";
8
- import { Command, Option } from "clipanion";
9
- import { red } from "yoctocolors";
10
- import { findFilesByGlobs } from "./../utils/fileSystem.js";
11
- import { resolveResultsPatterns } from "./../utils/resultsPatterns.js";
12
- import { notifySignals, waitForAbort } from "./../utils/signals.js";
13
- import { generate } from "./commons/generate.js";
14
- export class OpenCommand extends Command {
15
- constructor() {
16
- super(...arguments);
17
- this.resultsDir = Option.Rest({
18
- name: "A report path to open, or results patterns. Overrides config.resultsDir. Empty Rest serves the configured output.",
19
- });
20
- this.config = Option.String("--config,-c", {
21
- description: "The path to Allure config file",
22
- });
23
- this.port = Option.String("--port", {
24
- description: "The port to serve the reports on. If not set, the server starts on a random port",
25
- });
26
- this.cwd = Option.String("--cwd", {
27
- description: "The working directory for the command to run (default: current working directory)",
28
- });
29
- this.hideLabels = Option.Array("--hide-labels", {
30
- description: "Hide labels by exact name in generated reports. Repeat the option for multiple labels",
31
- });
32
- }
33
- async execute() {
34
- const cwd = this.cwd ?? processCwd();
35
- const hideLabels = this.hideLabels?.length ? this.hideLabels : undefined;
36
- const config = await readConfig(cwd, this.config, {
37
- port: this.port,
38
- });
39
- const servePath = this.resolveReportPath(cwd, this.resultsDir, config.output);
40
- const resolvedPatterns = resolveResultsPatterns(this.resultsDir, config.resultsDir);
41
- if (await this.reportExists(servePath)) {
42
- await serve({
43
- port: config.port ? parseInt(config.port, 10) : undefined,
44
- servePath,
45
- open: true,
46
- });
47
- }
48
- else if (resolvedPatterns.length) {
49
- const tmpDir = await mkdtemp(join(tmpdir(), "allure-report-"));
50
- const config = await readConfig(cwd, this.config, {
51
- port: this.port,
52
- output: tmpDir,
53
- hideLabels,
54
- });
55
- await generate({
56
- resultsDir: this.resultsDir,
57
- cwd,
58
- config,
59
- });
60
- const notifier = notifySignals(["SIGINT", "SIGTERM"]);
61
- void waitForAbort(notifier.signal).then(async () => {
62
- try {
63
- await rm(config.output, { recursive: true });
64
- }
65
- catch { }
66
- process.exit(notifier.info()?.code ?? 0);
67
- });
68
- await serve({
69
- port: config.port ? parseInt(config.port, 10) : undefined,
70
- servePath: config.output,
71
- open: true,
72
- });
73
- }
74
- else {
75
- console.error(red(`A report doesn't exist in ${servePath} and no input was provided to generate.`));
76
- exit(1);
77
- return;
78
- }
79
- }
80
- resolveReportPath(cwd, inputs, fallback = "allure-report") {
81
- if (inputs.length <= 1) {
82
- const [maybeRelativeReportPath = fallback] = inputs;
83
- return resolve(cwd, maybeRelativeReportPath);
84
- }
85
- }
86
- async reportExists(reportPath) {
87
- if (reportPath && existsSync(reportPath)) {
88
- const summaryFiles = await findFilesByGlobs(reportPath, [join("**", "summary.json")]);
89
- return summaryFiles.length > 0;
90
- }
91
- return false;
1
+ import { Command } from "clipanion";
2
+ import { BaseServeCommand } from "./serve.js";
3
+ export class OpenCommand extends BaseServeCommand {
4
+ get shouldOpen() {
5
+ return true;
92
6
  }
93
7
  }
94
- OpenCommand.paths = [["open"], ["serve"]];
8
+ OpenCommand.paths = [["open"]];
95
9
  OpenCommand.usage = Command.Usage({
96
- description: "Serves specified directory",
97
- details: "This command generates report with the given test results and opens it in the default browser.",
10
+ description: "Serves a report and opens it in the default browser, generating it from test results if necessary.",
11
+ details: "This command starts a report server and opens the report in the default browser. If test results are" +
12
+ " provided, it generates the report in a temporary directory before serving and opening it. With no arguments," +
13
+ " it serves the configured output directory or, if no report exists there, generates a report from the" +
14
+ " configured test results.",
98
15
  examples: [
99
- ["open ./allure-results", "Generate and serve the report based on given test results directory"],
100
- ["open --port 8080 ./allure-report", "Serve the report on port 8080"],
16
+ ["open ./allure-results", "Generate, serve, and open the report from the given test results directory"],
17
+ ["open --port 8080 ./allure-report", "Serve the report on port 8080 and open it"],
18
+ [
19
+ "open --host 127.0.0.1 ./allure-report",
20
+ "Serve the report on the loopback interface only and open it in the default browser.",
21
+ ],
101
22
  [
102
23
  "open ./packages/*/allure-results",
103
- "Generate and serve the report from all Allure result directories matching the pattern",
24
+ "Generate, serve, and open the report from all test results directories matching the pattern",
104
25
  ],
105
26
  [
106
- "open ./packages/foo/allure-results /packages/bar/allure-results",
107
- "Generate and serve the report from two Allure result directories",
27
+ "open ./packages/foo/allure-results ./packages/bar/allure-results",
28
+ "Generate, serve, and open the report from two test results directories",
108
29
  ],
109
30
  ],
110
31
  });
@@ -1,6 +1,6 @@
1
1
  import * as console from "node:console";
2
2
  import { realpath } from "node:fs/promises";
3
- import { exit, cwd as processCwd } from "node:process";
3
+ import process, { exit, cwd as processCwd } from "node:process";
4
4
  import { AllureReport, QualityGateState, filterFailedQualityGateResults, readConfig, stringifyQualityGateResults, } from "@allurereport/core";
5
5
  import { Command, Option } from "clipanion";
6
6
  import * as typanion from "typanion";
@@ -88,6 +88,14 @@ export class QualityGateCommand extends Command {
88
88
  return;
89
89
  }
90
90
  const state = new QualityGateState();
91
+ let validated = false;
92
+ process.once("beforeExit", () => {
93
+ if (validated || (process.exitCode ?? 0) !== 0) {
94
+ return;
95
+ }
96
+ console.error(red("Quality gate hasn't been validated, the report generation has been interrupted"));
97
+ process.exitCode = 1;
98
+ });
91
99
  allureReport.realtimeSubscriber.onTestResults(async (trsIds) => {
92
100
  const trs = await Promise.all(trsIds.map((id) => allureReport.store.testResultById(id)));
93
101
  const nonRetryTrs = trs.filter((tr) => !tr.isRetry);
@@ -99,6 +107,7 @@ export class QualityGateCommand extends Command {
99
107
  if (!fastFailed) {
100
108
  return;
101
109
  }
110
+ validated = true;
102
111
  console.error(stringifyQualityGateResults(results));
103
112
  exit(1);
104
113
  });
@@ -112,6 +121,7 @@ export class QualityGateCommand extends Command {
112
121
  trs: allTrs,
113
122
  environment: resolvedEnvironment?.id,
114
123
  });
124
+ validated = true;
115
125
  if (filterFailedQualityGateResults(validationResults.results).length === 0) {
116
126
  exit(0);
117
127
  return;
@@ -7,9 +7,12 @@ export declare class RunCommand extends Command {
7
7
  output: string | undefined;
8
8
  open: boolean | undefined;
9
9
  port: string | undefined;
10
+ host: string | undefined;
10
11
  reportName: string | undefined;
11
12
  rerun: string | undefined;
12
13
  silent: boolean | undefined;
14
+ verbose: boolean | undefined;
15
+ quiet: boolean | undefined;
13
16
  ignoreLogs: boolean | undefined;
14
17
  dump: string | undefined;
15
18
  environment: string | undefined;
@@ -20,6 +23,7 @@ export declare class RunCommand extends Command {
20
23
  knownIssues: string | undefined;
21
24
  resultsDir: string[] | undefined;
22
25
  commandToRun: string[];
26
+ get silentOutput(): boolean;
23
27
  get logs(): "pipe" | "inherit" | "ignore";
24
28
  execute(): Promise<void>;
25
29
  }
@@ -9,6 +9,8 @@ import { Command, Option, UsageError } from "clipanion";
9
9
  import { environmentNameOption, environmentOption, normalizeCommandEnvironmentOptions, resolveCommandEnvironment, } from "../utils/environment.js";
10
10
  import { createChildAllureCliEnvironment, getActiveAllureCliCommand } from "../utils/execution-context.js";
11
11
  import { parseRunCommand, resolveResultsPatterns } from "../utils/resultsPatterns.js";
12
+ import { serverOptionsFromConfig } from "../utils/serverOptions.js";
13
+ import { isQuiet } from "../utils/verbosity.js";
12
14
  import { executeAllureRun, executeNestedAllureCommand } from "./commons/run.js";
13
15
  const missingRunCommandUsageError = () => new UsageError("expecting command to be specified after --, e.g. allure run -- npm run test");
14
16
  const runLogger = new Logger("AllureRun");
@@ -30,6 +32,9 @@ export class RunCommand extends Command {
30
32
  this.port = Option.String("--port", {
31
33
  description: "The port to serve the reports on. If not set, the server starts on a random port",
32
34
  });
35
+ this.host = Option.String("--host", {
36
+ description: "The host (network interface) to serve the reports on, e.g. 127.0.0.1 or 0.0.0.0 (default: all interfaces)",
37
+ });
33
38
  this.reportName = Option.String("--report-name,--name", {
34
39
  description: "The report name (default: Allure Report)",
35
40
  });
@@ -39,6 +44,12 @@ export class RunCommand extends Command {
39
44
  this.silent = Option.Boolean("--silent", {
40
45
  description: "Don't pipe the process output logs to console (default: 0)",
41
46
  });
47
+ this.verbose = Option.Boolean("--verbose,-v", {
48
+ description: "Print verbose Allure messages (global flag, cannot be combined with --quiet)",
49
+ });
50
+ this.quiet = Option.Boolean("--quiet,-q", {
51
+ description: "Print errors only and hide the test process output, implies --silent (global flag)",
52
+ });
42
53
  this.ignoreLogs = Option.Boolean("--ignore-logs", {
43
54
  description: "Prevent logs attaching to the report (default: false)",
44
55
  });
@@ -64,8 +75,11 @@ export class RunCommand extends Command {
64
75
  });
65
76
  this.commandToRun = Option.Rest();
66
77
  }
78
+ get silentOutput() {
79
+ return !!(this.silent || this.quiet) || isQuiet();
80
+ }
67
81
  get logs() {
68
- if (this.silent) {
82
+ if (this.silentOutput) {
69
83
  return this.ignoreLogs ? "ignore" : "pipe";
70
84
  }
71
85
  return this.ignoreLogs ? "inherit" : "pipe";
@@ -84,7 +98,7 @@ export class RunCommand extends Command {
84
98
  command,
85
99
  commandArgs,
86
100
  cwd,
87
- silent: this.silent,
101
+ silent: this.silentOutput,
88
102
  });
89
103
  exit(exitCode ?? -1);
90
104
  return;
@@ -100,6 +114,7 @@ export class RunCommand extends Command {
100
114
  name: this.reportName,
101
115
  open: this.open,
102
116
  port: this.port,
117
+ host: this.host,
103
118
  hideLabels,
104
119
  historyLimit: this.historyLimit !== undefined ? parseInt(this.historyLimit, 10) : undefined,
105
120
  ...(this.historyBaseUrl !== undefined ? { historyBaseUrl: this.historyBaseUrl } : {}),
@@ -146,7 +161,7 @@ export class RunCommand extends Command {
146
161
  environment: resolvedEnvironment?.id,
147
162
  withQualityGate,
148
163
  logs: this.logs,
149
- silent: this.silent,
164
+ silent: this.silentOutput,
150
165
  ignoreLogs: this.ignoreLogs,
151
166
  maxRerun,
152
167
  resultsPatterns,
@@ -155,7 +170,7 @@ export class RunCommand extends Command {
155
170
  runLogger.info(`Completed with exit code ${finalExitCode} after ${formatDuration(Date.now() - before)}`);
156
171
  if (config.open) {
157
172
  await serve({
158
- port: config.port ? parseInt(config.port, 10) : undefined,
173
+ ...serverOptionsFromConfig(config),
159
174
  servePath: config.output,
160
175
  open: true,
161
176
  });
@@ -171,7 +186,12 @@ RunCommand.usage = Command.Usage({
171
186
  details: "This command runs the specified command and collects Allure results. " +
172
187
  "Override results discovery with repeated `--results-dir` (CLI overrides `config.resultsDir`). " +
173
188
  "When neither is set, directories named `allure-results` are discovered dynamically. " +
174
- "Quote globs in the shell so they are not expanded early.",
189
+ "Quote globs in the shell so they are not expanded early.\n\n" +
190
+ "Global flags: `--verbose`/`-v` prints verbose Allure messages, `--quiet`/`-q` prints errors only. " +
191
+ "They cannot be combined and must come before `--`. " +
192
+ "`--quiet` also implies `--silent`: the test process output is not printed, but is still attached to the report " +
193
+ "unless `--ignore-logs` is set. `--verbose` does not change `--silent` or `--ignore-logs`. " +
194
+ "Without either flag the log level comes from `ALLURE_LOG_LEVEL` (default: info).",
175
195
  examples: [
176
196
  ["run -- npm run test", "Run npm run test and collect Allure results"],
177
197
  ["run --rerun 3 -- npm run test", "Run npm run test and rerun failed tests up to 3 times"],
@@ -179,6 +199,8 @@ RunCommand.usage = Command.Usage({
179
199
  "run --dump=my-dump -- npm run test",
180
200
  "Run npm run test and pack inner report state into my-dump.zip archive to restore the state in the next run",
181
201
  ],
202
+ ["run --quiet -- npm run test", "Print only errors and hide the test process output"],
203
+ ["run --verbose -- npm run test", "Print verbose Allure messages while running the tests"],
182
204
  [
183
205
  "run --results-dir './artifacts/**/allure-results' -- npm test",
184
206
  "Override results discovery with a quoted glob",
@@ -0,0 +1,19 @@
1
+ import { Command } from "clipanion";
2
+ export declare abstract class BaseServeCommand extends Command {
3
+ static paths: string[][];
4
+ static usage: import("clipanion").Usage;
5
+ resultsDir: string[];
6
+ config: string | undefined;
7
+ port: string | undefined;
8
+ host: string | undefined;
9
+ cwd: string | undefined;
10
+ hideLabels: string[] | undefined;
11
+ protected abstract get shouldOpen(): boolean;
12
+ execute(): Promise<number>;
13
+ private resolveReportPath;
14
+ private reportExists;
15
+ }
16
+ export declare class ServeCommand extends BaseServeCommand {
17
+ open: boolean | undefined;
18
+ protected get shouldOpen(): boolean;
19
+ }