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.
Files changed (45) hide show
  1. package/README.md +34 -10
  2. package/dist/commands/agent-run.d.ts +1 -0
  3. package/dist/commands/agent-run.js +11 -6
  4. package/dist/commands/agent.d.ts +1 -0
  5. package/dist/commands/agent.js +19 -8
  6. package/dist/commands/allure2.js +10 -10
  7. package/dist/commands/awesome.js +10 -10
  8. package/dist/commands/check.js +1 -1
  9. package/dist/commands/classic.js +10 -10
  10. package/dist/commands/commons/generate.d.ts +1 -1
  11. package/dist/commands/commons/generate.js +7 -3
  12. package/dist/commands/commons/resultsDiscovery.d.ts +5 -0
  13. package/dist/commands/commons/resultsDiscovery.js +17 -0
  14. package/dist/commands/commons/run.d.ts +3 -0
  15. package/dist/commands/commons/run.js +60 -27
  16. package/dist/commands/csv.js +10 -10
  17. package/dist/commands/dashboard.js +9 -9
  18. package/dist/commands/generate.d.ts +1 -0
  19. package/dist/commands/generate.js +7 -3
  20. package/dist/commands/history.js +6 -4
  21. package/dist/commands/index.d.ts +0 -1
  22. package/dist/commands/index.js +0 -1
  23. package/dist/commands/jira/clear.js +1 -1
  24. package/dist/commands/log.js +9 -9
  25. package/dist/commands/open.js +9 -5
  26. package/dist/commands/qualityGate.js +7 -7
  27. package/dist/commands/results/pack.d.ts +1 -0
  28. package/dist/commands/results/pack.js +12 -14
  29. package/dist/commands/run.d.ts +2 -0
  30. package/dist/commands/run.js +24 -8
  31. package/dist/commands/slack.js +9 -9
  32. package/dist/commands/testplan.js +7 -5
  33. package/dist/commands/watch.d.ts +2 -0
  34. package/dist/commands/watch.js +75 -26
  35. package/dist/index.js +1 -2
  36. package/dist/utils/index.d.ts +2 -0
  37. package/dist/utils/index.js +2 -0
  38. package/dist/utils/process.js +1 -0
  39. package/dist/utils/resultsPatterns.d.ts +12 -0
  40. package/dist/utils/resultsPatterns.js +37 -0
  41. package/dist/utils/signals.d.ts +18 -0
  42. package/dist/utils/signals.js +52 -0
  43. package/package.json +22 -22
  44. package/dist/commands/knownIssue.d.ts +0 -8
  45. package/dist/commands/knownIssue.js +0 -59
@@ -7,12 +7,14 @@ import process from "node:process";
7
7
  import { QualityGateState, stringifyQualityGateResults } from "@allurereport/core";
8
8
  import { createTestPlan } from "@allurereport/core-api";
9
9
  import { allureResultsDirectoriesWatcher, delayedFileProcessingWatcher, newFilesInDirectoryWatcher, } from "@allurereport/directory-watcher";
10
+ import { formatProcessLogAttachmentName } from "@allurereport/plugin-agent";
10
11
  import { BufferResultFile, PathResultFile } from "@allurereport/reader-api";
11
12
  import { KnownError } from "@allurereport/service";
12
13
  import { red } from "yoctocolors";
13
14
  import { logTests, runProcess, terminationOf } from "../../utils/index.js";
14
15
  import { logError } from "../../utils/logs.js";
15
16
  import { stopProcessTree } from "../../utils/process.js";
17
+ import { allureResultsDirectoriesGlobWatcher } from "./resultsDiscovery.js";
16
18
  export const executeNestedAllureCommand = async (params) => {
17
19
  const nestedProcess = runProcess({
18
20
  command: params.command,
@@ -23,8 +25,16 @@ export const executeNestedAllureCommand = async (params) => {
23
25
  });
24
26
  return await terminationOf(nestedProcess);
25
27
  };
28
+ const attachResultsDirectoryWatchers = (params) => {
29
+ if (params.resultsPatterns.length > 0) {
30
+ return allureResultsDirectoriesGlobWatcher(params.cwd, params.resultsPatterns, params.onUpdate, {
31
+ indexDelay: 600,
32
+ });
33
+ }
34
+ return allureResultsDirectoriesWatcher(params.cwd, params.onUpdate, { indexDelay: 600 });
35
+ };
26
36
  export const runTests = async (params) => {
27
- const { allureReport, knownIssues, cwd, command, commandArgs, logs, environmentVariables, environment, withQualityGate, silent, logProcessExit = true, } = params;
37
+ const { allureReport, knownIssues, cwd, command, commandArgs, logs, environmentVariables, environment, withQualityGate, silent, logProcessExit = true, resultsPatterns = [], } = params;
28
38
  let testProcessStarted = false;
29
39
  const allureResultsWatchers = new Map();
30
40
  const processWatcher = delayedFileProcessingWatcher(async (path) => {
@@ -33,28 +43,32 @@ export const runTests = async (params) => {
33
43
  indexDelay: 200,
34
44
  minProcessingDelay: 1000,
35
45
  });
36
- const allureResultsWatch = allureResultsDirectoriesWatcher(cwd, async (newAllureResults, deletedAllureResults) => {
37
- for (const delAr of deletedAllureResults) {
38
- const watcher = allureResultsWatchers.get(delAr);
39
- if (watcher) {
40
- await watcher.abort();
46
+ const allureResultsWatch = attachResultsDirectoryWatchers({
47
+ cwd,
48
+ resultsPatterns,
49
+ onUpdate: async (newAllureResults, deletedAllureResults) => {
50
+ for (const delAr of deletedAllureResults) {
51
+ const watcher = allureResultsWatchers.get(delAr);
52
+ if (watcher) {
53
+ await watcher.abort();
54
+ }
55
+ allureResultsWatchers.delete(delAr);
41
56
  }
42
- allureResultsWatchers.delete(delAr);
43
- }
44
- for (const newAr of newAllureResults) {
45
- if (allureResultsWatchers.has(newAr)) {
46
- continue;
57
+ for (const newAr of newAllureResults) {
58
+ if (allureResultsWatchers.has(newAr)) {
59
+ continue;
60
+ }
61
+ const watcher = newFilesInDirectoryWatcher(newAr, async (path) => {
62
+ await processWatcher.addFile(path);
63
+ }, {
64
+ ignoreInitial: !testProcessStarted,
65
+ indexDelay: 300,
66
+ });
67
+ allureResultsWatchers.set(newAr, watcher);
68
+ await watcher.initialScan();
47
69
  }
48
- const watcher = newFilesInDirectoryWatcher(newAr, async (path) => {
49
- await processWatcher.addFile(path);
50
- }, {
51
- ignoreInitial: !testProcessStarted,
52
- indexDelay: 300,
53
- });
54
- allureResultsWatchers.set(newAr, watcher);
55
- await watcher.initialScan();
56
- }
57
- }, { indexDelay: 600 });
70
+ },
71
+ });
58
72
  await allureResultsWatch.initialScan();
59
73
  testProcessStarted = true;
60
74
  const beforeProcess = Date.now();
@@ -68,10 +82,14 @@ export const runTests = async (params) => {
68
82
  const qualityGateState = new QualityGateState();
69
83
  let qualityGateUnsub;
70
84
  let qualityGateResults = [];
85
+ let fastFailTriggered = false;
71
86
  let testProcessStdout = "";
72
87
  let testProcessStderr = "";
73
88
  if (withQualityGate) {
74
89
  qualityGateUnsub = allureReport.realtimeSubscriber.onTestResults(async (testResults) => {
90
+ if (fastFailTriggered) {
91
+ return;
92
+ }
75
93
  const trs = await Promise.all(testResults.map((tr) => allureReport.store.testResultById(tr)));
76
94
  const filteredTrs = trs.filter((tr) => tr !== undefined);
77
95
  if (!filteredTrs.length) {
@@ -86,8 +104,10 @@ export const runTests = async (params) => {
86
104
  if (!fastFailed) {
87
105
  return;
88
106
  }
89
- allureReport.realtimeDispatcher.sendQualityGateResults(results);
107
+ qualityGateUnsub?.();
108
+ fastFailTriggered = true;
90
109
  qualityGateResults = results;
110
+ qualityGateUnsub = undefined;
91
111
  try {
92
112
  await stopProcessTree(testProcess.pid);
93
113
  }
@@ -137,10 +157,11 @@ export const runTests = async (params) => {
137
157
  stdout: testProcessStdout,
138
158
  stderr: testProcessStderr,
139
159
  qualityGateResults,
160
+ fastFailed: fastFailTriggered,
140
161
  };
141
162
  };
142
163
  export const executeAllureRun = async (params) => {
143
- const { allureReport, knownIssues, cwd, command, commandArgs, environmentVariables = {}, environment, withQualityGate, silent, logs, ignoreLogs, maxRerun = 0, logProcessExit = true, } = params;
164
+ const { allureReport, knownIssues, cwd, command, commandArgs, environmentVariables = {}, environment, withQualityGate, silent, logs, ignoreLogs, maxRerun = 0, logProcessExit = true, resultsPatterns = [], } = params;
144
165
  await allureReport.start();
145
166
  const globalExitCode = {
146
167
  original: 0,
@@ -161,9 +182,15 @@ export const executeAllureRun = async (params) => {
161
182
  environmentVariables,
162
183
  withQualityGate,
163
184
  logProcessExit,
185
+ resultsPatterns,
164
186
  });
165
- for (let rerun = 0; rerun < maxRerun; rerun++) {
166
- const failed = await allureReport.store.failedTestResults();
187
+ const allFailuresAfterInitialRun = await allureReport.store.failedTestResults();
188
+ const blockingFailuresAfterInitialRun = await allureReport.store.blockingFailedTestResults();
189
+ if (allFailuresAfterInitialRun.length > 0 && blockingFailuresAfterInitialRun.length === 0 && testProcessResult) {
190
+ globalExitCode.actual = 0;
191
+ }
192
+ for (let rerun = 0; rerun < maxRerun && testProcessResult && !testProcessResult.fastFailed; rerun++) {
193
+ const failed = await allureReport.store.blockingFailedTestResults();
167
194
  if (failed.length === 0) {
168
195
  console.log("no failed tests is detected.");
169
196
  break;
@@ -190,10 +217,16 @@ export const executeAllureRun = async (params) => {
190
217
  },
191
218
  withQualityGate,
192
219
  logProcessExit,
220
+ resultsPatterns,
193
221
  });
194
222
  await rm(tmpDir, { recursive: true });
195
223
  logTests(await allureReport.store.allTestResults());
196
224
  }
225
+ const allFailuresAfterReruns = await allureReport.store.failedTestResults();
226
+ const blockingFailuresAfterReruns = await allureReport.store.blockingFailedTestResults();
227
+ if (allFailuresAfterReruns.length > 0 && blockingFailuresAfterReruns.length === 0 && testProcessResult) {
228
+ globalExitCode.actual = 0;
229
+ }
197
230
  const trs = await allureReport.store.allTestResults({ includeRetries: false });
198
231
  qualityGateResults = testProcessResult?.qualityGateResults ?? [];
199
232
  if (withQualityGate && !qualityGateResults.length) {
@@ -235,13 +268,13 @@ export const executeAllureRun = async (params) => {
235
268
  const fileName = randomUUID();
236
269
  const stdoutResultFile = new BufferResultFile(Buffer.from(testProcessResult.stdout, "utf8"), `${fileName}`);
237
270
  stdoutResultFile.contentType = "text/plain";
238
- allureReport.realtimeDispatcher.sendGlobalAttachment(stdoutResultFile, `${command}-stdout.txt`);
271
+ allureReport.realtimeDispatcher.sendGlobalAttachment(stdoutResultFile, formatProcessLogAttachmentName([command, ...commandArgs].join(" "), "stdout"));
239
272
  }
240
273
  if (!ignoreLogs && testProcessResult?.stderr) {
241
274
  const fileName = randomUUID();
242
275
  const stderrResultFile = new BufferResultFile(Buffer.from(testProcessResult.stderr, "utf8"), fileName);
243
276
  stderrResultFile.contentType = "text/plain";
244
- allureReport.realtimeDispatcher.sendGlobalAttachment(stderrResultFile, `${command}-stderr.txt`);
277
+ allureReport.realtimeDispatcher.sendGlobalAttachment(stderrResultFile, formatProcessLogAttachmentName([command, ...commandArgs].join(" "), "stderr"));
245
278
  if (processFailed) {
246
279
  allureReport.realtimeDispatcher.sendGlobalError({
247
280
  message: "Test process has failed",
@@ -6,15 +6,15 @@ import { AllureReport, readConfig } from "@allurereport/core";
6
6
  import CsvPlugin from "@allurereport/plugin-csv";
7
7
  import { Command, Option } from "clipanion";
8
8
  import { red } from "yoctocolors";
9
- import { findAllureResultDirectories } from "../utils/fileSystem.js";
9
+ import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
10
10
  export class CsvCommand extends Command {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.resultsDir = Option.Rest({
14
- name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
14
+ name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
15
15
  });
16
16
  this.config = Option.String("--config,-c", {
17
- description: "The path Allure config file",
17
+ description: "The path to Allure config file",
18
18
  });
19
19
  this.cwd = Option.String("--cwd", {
20
20
  description: "The working directory for the command to run (default: current working directory)",
@@ -29,17 +29,11 @@ export class CsvCommand extends Command {
29
29
  description: "The csv separator",
30
30
  });
31
31
  this.knownIssues = Option.String("--known-issues", {
32
- description: "Path to the known issues file. Updates the file and quarantines failed tests when specified",
32
+ description: "Path to known issues file",
33
33
  });
34
34
  }
35
35
  async execute() {
36
36
  const cwd = await realpath(this.cwd ?? process.cwd());
37
- const { resultDirectories, patterns } = await findAllureResultDirectories(cwd, this.resultsDir);
38
- if (!resultDirectories.length) {
39
- console.error(red(`No test results directories found matching pattern: ${patterns}`));
40
- exit(1);
41
- return;
42
- }
43
37
  const before = new Date().getTime();
44
38
  const defaultCsvOptions = {
45
39
  separator: this.separator ?? ",",
@@ -49,6 +43,12 @@ export class CsvCommand extends Command {
49
43
  const config = await readConfig(cwd, this.config, {
50
44
  knownIssuesPath: this.knownIssues,
51
45
  });
46
+ const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
47
+ if (!resultDirectories.length) {
48
+ console.error(red(`No test results directories found matching pattern: ${patterns}`));
49
+ exit(1);
50
+ return;
51
+ }
52
52
  config.plugins = [
53
53
  {
54
54
  id: "csv",
@@ -5,15 +5,15 @@ import { AllureReport, readConfig } from "@allurereport/core";
5
5
  import DashboardPlugin from "@allurereport/plugin-dashboard";
6
6
  import { Command, Option } from "clipanion";
7
7
  import { red } from "yoctocolors";
8
- import { findAllureResultDirectories } from "../utils/fileSystem.js";
8
+ import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
9
9
  export class DashboardCommand extends Command {
10
10
  constructor() {
11
11
  super(...arguments);
12
12
  this.resultsDir = Option.Rest({
13
- name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
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)",
@@ -39,12 +39,6 @@ export class DashboardCommand extends Command {
39
39
  }
40
40
  async execute() {
41
41
  const cwd = await realpath(this.cwd ?? process.cwd());
42
- const { resultDirectories, patterns } = await findAllureResultDirectories(cwd, this.resultsDir);
43
- if (!resultDirectories.length) {
44
- console.error(red(`No test results directories found matching pattern: ${patterns}`));
45
- exit(1);
46
- return;
47
- }
48
42
  const before = new Date().getTime();
49
43
  const defaultDashboardOptions = {
50
44
  singleFile: this.singleFile ?? false,
@@ -56,6 +50,12 @@ export class DashboardCommand extends Command {
56
50
  output: this.output,
57
51
  name: this.reportName,
58
52
  });
53
+ const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
54
+ if (!resultDirectories.length) {
55
+ console.error(red(`No test results directories found matching pattern: ${patterns}`));
56
+ exit(1);
57
+ return;
58
+ }
59
59
  config.plugins = [
60
60
  {
61
61
  id: "dashboard",
@@ -12,5 +12,6 @@ export declare class GenerateCommand extends Command {
12
12
  port: string | undefined;
13
13
  historyLimit: string | undefined;
14
14
  hideLabels: string[] | undefined;
15
+ knownIssues: string | undefined;
15
16
  execute(): Promise<void>;
16
17
  }
@@ -7,10 +7,10 @@ export class GenerateCommand extends Command {
7
7
  constructor() {
8
8
  super(...arguments);
9
9
  this.resultsDir = Option.Rest({
10
- name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
10
+ name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
11
11
  });
12
12
  this.config = Option.String("--config,-c", {
13
- description: "The path Allure config file",
13
+ description: "The path to Allure config file",
14
14
  });
15
15
  this.output = Option.String("--output,-o", {
16
16
  description: "The output directory name. Absolute paths are accepted as well (default: allure-report)",
@@ -38,6 +38,9 @@ export class GenerateCommand extends Command {
38
38
  this.hideLabels = Option.Array("--hide-labels", {
39
39
  description: "Hide labels by exact name in generated reports. Repeat the option for multiple labels",
40
40
  });
41
+ this.knownIssues = Option.String("--known-issues", {
42
+ description: "Path to known issues file",
43
+ });
41
44
  }
42
45
  async execute() {
43
46
  const cwd = this.cwd ?? processCwd();
@@ -48,7 +51,8 @@ export class GenerateCommand extends Command {
48
51
  open: this.open,
49
52
  port: this.port,
50
53
  hideLabels,
51
- historyLimit: this.historyLimit ? parseInt(this.historyLimit, 10) : undefined,
54
+ historyLimit: this.historyLimit !== undefined ? parseInt(this.historyLimit, 10) : undefined,
55
+ knownIssuesPath: this.knownIssues,
52
56
  });
53
57
  await generate({
54
58
  dump: this.dump,
@@ -1,14 +1,14 @@
1
1
  import * as console from "node:console";
2
2
  import process, { exit } from "node:process";
3
- import { AllureReport, resolveConfig } from "@allurereport/core";
3
+ import { AllureReport, readRawConfig, resolveConfig } from "@allurereport/core";
4
4
  import { Command, Option } from "clipanion";
5
5
  import { red } from "yoctocolors";
6
- import { findAllureResultDirectories } from "../utils/fileSystem.js";
6
+ import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
7
7
  export class HistoryCommand extends Command {
8
8
  constructor() {
9
9
  super(...arguments);
10
10
  this.resultsDir = Option.Rest({
11
- name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
11
+ name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
12
12
  });
13
13
  this.historyPath = Option.String("--history-path,-h", {
14
14
  description: "The path to history file",
@@ -21,7 +21,9 @@ export class HistoryCommand extends Command {
21
21
  });
22
22
  }
23
23
  async execute() {
24
- const { resultDirectories, patterns } = await findAllureResultDirectories(process.cwd(), this.resultsDir);
24
+ const cwd = process.cwd();
25
+ const rawConfig = await readRawConfig(cwd);
26
+ const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, rawConfig.resultsDir);
25
27
  if (!resultDirectories.length) {
26
28
  console.error(red(`No test results directories found matching pattern: ${patterns}`));
27
29
  exit(1);
@@ -7,7 +7,6 @@ export * from "./awesome.js";
7
7
  export * from "./csv.js";
8
8
  export * from "./check.js";
9
9
  export * from "./history.js";
10
- export * from "./knownIssue.js";
11
10
  export * from "./run.js";
12
11
  export * from "./slack.js";
13
12
  export * from "./testplan.js";
@@ -7,7 +7,6 @@ export * from "./awesome.js";
7
7
  export * from "./csv.js";
8
8
  export * from "./check.js";
9
9
  export * from "./history.js";
10
- export * from "./knownIssue.js";
11
10
  export * from "./run.js";
12
11
  export * from "./slack.js";
13
12
  export * from "./testplan.js";
@@ -8,7 +8,7 @@ class BaseJiraCommand extends Command {
8
8
  constructor() {
9
9
  super(...arguments);
10
10
  this.config = Option.String("--config,-c", {
11
- description: "The path Allure config file",
11
+ description: "The path to Allure config file",
12
12
  });
13
13
  this.cwd = Option.String("--cwd", {
14
14
  description: "The working directory for the command to run (default: current working directory)",
@@ -5,15 +5,15 @@ import { AllureReport, readConfig } from "@allurereport/core";
5
5
  import LogPlugin from "@allurereport/plugin-log";
6
6
  import { Command, Option } from "clipanion";
7
7
  import { red } from "yoctocolors";
8
- import { findAllureResultDirectories } from "../utils/fileSystem.js";
8
+ import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
9
9
  export class LogCommand extends Command {
10
10
  constructor() {
11
11
  super(...arguments);
12
12
  this.resultsDir = Option.Rest({
13
- name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
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)",
@@ -30,12 +30,6 @@ export class LogCommand extends Command {
30
30
  }
31
31
  async execute() {
32
32
  const cwd = await realpath(this.cwd ?? process.cwd());
33
- const { resultDirectories, patterns } = await findAllureResultDirectories(cwd, this.resultsDir);
34
- if (!resultDirectories.length) {
35
- console.error(red(`No test results directories found matching pattern: ${patterns}`));
36
- exit(1);
37
- return;
38
- }
39
33
  const before = new Date().getTime();
40
34
  const defaultLogOptions = {
41
35
  allSteps: this.allSteps ?? false,
@@ -43,6 +37,12 @@ export class LogCommand extends Command {
43
37
  groupBy: this.groupBy ?? "suite",
44
38
  };
45
39
  const config = await readConfig(cwd, this.config);
40
+ const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
41
+ if (!resultDirectories.length) {
42
+ console.error(red(`No test results directories found matching pattern: ${patterns}`));
43
+ exit(1);
44
+ return;
45
+ }
46
46
  config.plugins = [
47
47
  {
48
48
  id: "log",
@@ -8,15 +8,17 @@ import { serve } from "@allurereport/static-server";
8
8
  import { Command, Option } from "clipanion";
9
9
  import { red } from "yoctocolors";
10
10
  import { findFilesByGlobs } from "./../utils/fileSystem.js";
11
+ import { resolveResultsPatterns } from "./../utils/resultsPatterns.js";
12
+ import { notifySignals, waitForAbort } from "./../utils/signals.js";
11
13
  import { generate } from "./commons/generate.js";
12
14
  export class OpenCommand extends Command {
13
15
  constructor() {
14
16
  super(...arguments);
15
17
  this.resultsDir = Option.Rest({
16
- name: "A report to open or a pattern to match test results directories in the current working directory (default: configured output)",
18
+ name: "A report path to open, or results patterns. Overrides config.resultsDir. Empty Rest serves the configured output.",
17
19
  });
18
20
  this.config = Option.String("--config,-c", {
19
- description: "The path Allure config file",
21
+ description: "The path to Allure config file",
20
22
  });
21
23
  this.port = Option.String("--port", {
22
24
  description: "The port to serve the reports on. If not set, the server starts on a random port",
@@ -35,6 +37,7 @@ export class OpenCommand extends Command {
35
37
  port: this.port,
36
38
  });
37
39
  const servePath = this.resolveReportPath(cwd, this.resultsDir, config.output);
40
+ const resolvedPatterns = resolveResultsPatterns(this.resultsDir, config.resultsDir);
38
41
  if (await this.reportExists(servePath)) {
39
42
  await serve({
40
43
  port: config.port ? parseInt(config.port, 10) : undefined,
@@ -42,7 +45,7 @@ export class OpenCommand extends Command {
42
45
  open: true,
43
46
  });
44
47
  }
45
- else if (this.resultsDir.length) {
48
+ else if (resolvedPatterns.length) {
46
49
  const tmpDir = await mkdtemp(join(tmpdir(), "allure-report-"));
47
50
  const config = await readConfig(cwd, this.config, {
48
51
  port: this.port,
@@ -54,12 +57,13 @@ export class OpenCommand extends Command {
54
57
  cwd,
55
58
  config,
56
59
  });
57
- process.on("SIGINT", async () => {
60
+ const notifier = notifySignals(["SIGINT", "SIGTERM"]);
61
+ void waitForAbort(notifier.signal).then(async () => {
58
62
  try {
59
63
  await rm(config.output, { recursive: true });
60
64
  }
61
65
  catch { }
62
- process.exit(0);
66
+ process.exit(notifier.info()?.code ?? 0);
63
67
  });
64
68
  await serve({
65
69
  port: config.port ? parseInt(config.port, 10) : undefined,
@@ -6,15 +6,15 @@ import { Command, Option } from "clipanion";
6
6
  import * as typanion from "typanion";
7
7
  import { red } from "yoctocolors";
8
8
  import { environmentNameOption, environmentOption, normalizeCommandEnvironmentOptions, resolveCommandEnvironment, } from "../utils/environment.js";
9
- import { findAllureResultDirectories } from "../utils/fileSystem.js";
9
+ import { resolveAndFindResultsDirs } from "../utils/resultsPatterns.js";
10
10
  export class QualityGateCommand extends Command {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.resultsDir = Option.Rest({
14
- name: "Patterns to match test results directories in the current working directory (default: ./**/allure-results)",
14
+ name: "Patterns to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
15
15
  });
16
16
  this.config = Option.String("--config,-c", {
17
- description: "The path Allure config file",
17
+ description: "The path to Allure config file",
18
18
  });
19
19
  this.fastFail = Option.Boolean("--fast-fail", {
20
20
  description: "Force the command to fail if there are any rule failures",
@@ -32,7 +32,7 @@ export class QualityGateCommand extends Command {
32
32
  validator: typanion.isNumber(),
33
33
  });
34
34
  this.knownIssues = Option.String("--known-issues", {
35
- description: "Path to the known issues file. Updates the file and quarantines failed tests when specified",
35
+ description: "Path to known issues file",
36
36
  });
37
37
  this.environment = environmentOption();
38
38
  this.environmentName = environmentNameOption();
@@ -47,9 +47,9 @@ export class QualityGateCommand extends Command {
47
47
  };
48
48
  normalizeCommandEnvironmentOptions(environmentOptions);
49
49
  const cwd = await realpath(this.cwd ?? processCwd());
50
- const { maxFailures, minTestsCount, successRate, fastFail, knownIssues: knownIssuesPath } = this;
50
+ const { maxFailures, minTestsCount, successRate, fastFail } = this;
51
51
  const config = await readConfig(cwd, this.config, {
52
- knownIssuesPath,
52
+ knownIssuesPath: this.knownIssues,
53
53
  });
54
54
  const resolvedEnvironment = resolveCommandEnvironment(config, environmentOptions);
55
55
  const rules = {};
@@ -81,7 +81,7 @@ export class QualityGateCommand extends Command {
81
81
  exit(-1);
82
82
  return;
83
83
  }
84
- const { resultDirectories, patterns } = await findAllureResultDirectories(cwd, this.resultsDir);
84
+ const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, this.resultsDir, config.resultsDir);
85
85
  if (!resultDirectories.length) {
86
86
  console.error(red(`No test results directories found matching pattern: ${patterns}`));
87
87
  exit(1);
@@ -5,6 +5,7 @@ export declare class ResultsPackCommand extends Command {
5
5
  static usage: import("clipanion").Usage;
6
6
  resultsDir: string | undefined;
7
7
  name: string | undefined;
8
+ config: string | undefined;
8
9
  cwd: string | undefined;
9
10
  execute(): Promise<void>;
10
11
  }
@@ -9,44 +9,42 @@ import * as fs from "node:fs/promises";
9
9
  import { realpath } from "node:fs/promises";
10
10
  import { basename, join, resolve } from "node:path";
11
11
  import { exit } from "node:process";
12
+ import { readRawConfig } from "@allurereport/core";
12
13
  import AdmZip from "adm-zip";
13
14
  import { Command, Option } from "clipanion";
14
- import { glob } from "glob";
15
15
  import { green, red } from "yoctocolors";
16
+ import { resolveAndFindResultsDirs } from "../../utils/resultsPatterns.js";
16
17
  export class ResultsPackCommand extends Command {
17
18
  constructor() {
18
19
  super(...arguments);
19
20
  _ResultsPackCommand_instances.add(this);
20
21
  this.resultsDir = Option.String({
21
22
  required: false,
22
- name: "Pattern to match test results directories in the current working directory (default: ./**/allure-results)",
23
+ name: "Pattern to match test results directories. Overrides config.resultsDir. Defaults to ./**/allure-results when neither is set.",
23
24
  });
24
25
  this.name = Option.String("--name", {
25
26
  description: "The archive name (default: allure-results.zip)",
26
27
  });
28
+ this.config = Option.String("--config,-c", {
29
+ description: "The path to Allure config file",
30
+ });
27
31
  this.cwd = Option.String("--cwd", {
28
32
  description: "The working directory for the command to run (default: current working directory)",
29
33
  });
30
34
  }
31
35
  async execute() {
32
36
  const cwd = await realpath(this.cwd ?? process.cwd());
33
- const resultsDir = (this.resultsDir ?? "./**/allure-results").replace(/[\\/]$/, "");
37
+ const rawConfig = await readRawConfig(cwd, this.config);
38
+ const cliPatterns = this.resultsDir ? [this.resultsDir] : [];
39
+ const { resultDirectories, patterns } = await resolveAndFindResultsDirs(cwd, cliPatterns, rawConfig.resultsDir);
34
40
  const archiveName = this.name ?? "allure-results.zip";
35
- const resultsDirectories = (await glob(resultsDir, {
36
- mark: true,
37
- nodir: false,
38
- dot: true,
39
- absolute: true,
40
- windowsPathsNoEscape: true,
41
- cwd,
42
- })).filter((p) => /(\/|\\)$/.test(p));
43
41
  const resultsFiles = new Set();
44
- if (resultsDirectories.length === 0) {
45
- console.error(red(`No test results directories found matching pattern: ${resultsDir}`));
42
+ if (resultDirectories.length === 0) {
43
+ console.error(red(`No test results directories found matching pattern: ${patterns}`));
46
44
  exit(1);
47
45
  return;
48
46
  }
49
- for (const dir of resultsDirectories) {
47
+ for (const dir of resultDirectories) {
50
48
  const files = await fs.readdir(dir);
51
49
  if (files.length === 0) {
52
50
  continue;
@@ -16,6 +16,8 @@ export declare class RunCommand extends Command {
16
16
  environmentName: string | undefined;
17
17
  historyLimit: string | undefined;
18
18
  hideLabels: string[] | undefined;
19
+ knownIssues: string | undefined;
20
+ resultsDir: string[] | undefined;
19
21
  commandToRun: string[];
20
22
  get logs(): "pipe" | "inherit" | "ignore";
21
23
  execute(): Promise<void>;