@allurereport/core 3.14.3 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.d.ts CHANGED
@@ -7,18 +7,20 @@ export interface PluginInstance {
7
7
  plugin: Plugin;
8
8
  options: Record<string, any>;
9
9
  }
10
- type FullConfigRequiredFromConfig = Required<Pick<Config, "name" | "output" | "open" | "knownIssuesPath">>;
11
- export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "allureService" | "knownIssuesPath" | "plugins" | "port">, FullConfigRequiredFromConfig {
10
+ type FullConfigRequiredFromConfig = Required<Pick<Config, "name" | "output" | "open">>;
11
+ export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "allureService" | "knownIssuesPath" | "plugins" | "port" | "resultsDir">, FullConfigRequiredFromConfig {
12
12
  port: Config["port"] | undefined;
13
13
  allowedEnvironments?: Config["allowedEnvironments"];
14
14
  reportFiles: ReportFiles;
15
15
  readers?: ResultsReader[];
16
16
  plugins?: PluginInstance[];
17
17
  known?: KnownTestFailure[];
18
+ knownIssuesPath?: Config["knownIssuesPath"];
18
19
  realTime?: any;
19
20
  qualityGate?: Config["qualityGate"];
20
21
  allureService?: ResolvedAllureServiceConfig;
21
22
  categories?: CategoriesConfig;
22
23
  globalAttachments?: string[];
24
+ resultsDir?: string[];
23
25
  }
24
26
  export {};
package/dist/config.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare const validateConfig: (config: Config) => {
25
25
  export declare const loadYamlConfig: (configPath: string) => Promise<Config>;
26
26
  export declare const loadJsonConfig: (configPath: string) => Promise<Config>;
27
27
  export declare const loadJsConfig: (configPath: string) => Promise<Config>;
28
+ export declare const loadTsConfig: (configPath: string) => Promise<Config>;
28
29
  export declare const resolveConfig: (config: Config, override?: ConfigOverride) => Promise<FullConfig>;
29
30
  export declare const readConfig: (cwd?: string, configPath?: string, override?: ConfigOverride) => Promise<FullConfig>;
30
31
  export declare const readRawConfig: (cwd?: string, configPath?: string) => Promise<Config>;
package/dist/config.js CHANGED
@@ -2,17 +2,22 @@ import { readFile, stat } from "node:fs/promises";
2
2
  import { extname, resolve } from "node:path";
3
3
  import * as process from "node:process";
4
4
  import { validateEnvironmentName } from "@allurereport/core-api";
5
+ import { createJiti } from "jiti";
5
6
  import { parse } from "yaml";
6
- import { readKnownIssues } from "./known.js";
7
+ import { DEFAULT_KNOWN_ISSUES_PATH, hasKnownIssueRules, readKnownIssues, resolveExactIssuesFilePath } from "./known.js";
7
8
  import { FileSystemReportFiles } from "./plugin.js";
8
9
  import { environmentIdentityById, environmentIdentityByName, validateAllowedEnvironmentIds, normalizeEnvironmentDescriptorMap, validateAllowedEnvironmentId, } from "./utils/environment.js";
9
10
  import { importWrapper } from "./utils/module.js";
10
11
  import { normalizeImportPath } from "./utils/path.js";
12
+ import { normalizeResultsDir } from "./utils/resultsDir.js";
11
13
  import { assertValidPluginIdForWindows, isWindows } from "./utils/windows.js";
12
14
  const CONFIG_FILENAMES = [
13
15
  "allurerc.js",
14
16
  "allurerc.mjs",
15
17
  "allurerc.cjs",
18
+ "allurerc.ts",
19
+ "allurerc.mts",
20
+ "allurerc.cts",
16
21
  "allurerc.json",
17
22
  "allurerc.yaml",
18
23
  "allurerc.yml",
@@ -80,7 +85,7 @@ export const findConfig = async (cwd, configPath) => {
80
85
  return resolved;
81
86
  }
82
87
  }
83
- catch (ignored) {
88
+ catch {
84
89
  }
85
90
  }
86
91
  };
@@ -94,6 +99,7 @@ export const validateConfig = (config) => {
94
99
  "historyPath",
95
100
  "historyLimit",
96
101
  "knownIssuesPath",
102
+ "knownIssues",
97
103
  "plugins",
98
104
  "defaultLabels",
99
105
  "variables",
@@ -105,6 +111,7 @@ export const validateConfig = (config) => {
105
111
  "allureService",
106
112
  "categories",
107
113
  "globalAttachments",
114
+ "resultsDir",
108
115
  ];
109
116
  const unsupportedFields = Object.keys(config).filter((key) => !supportedFields.includes(key));
110
117
  return {
@@ -141,6 +148,10 @@ export const loadJsonConfig = async (configPath) => {
141
148
  export const loadJsConfig = async (configPath) => {
142
149
  return (await import(normalizeImportPath(configPath))).default;
143
150
  };
151
+ export const loadTsConfig = async (configPath) => {
152
+ const jiti = createJiti(import.meta.url);
153
+ return await jiti.import(resolve(configPath), { default: true });
154
+ };
144
155
  const resolveConfigEnvironments = (config) => {
145
156
  const errors = [];
146
157
  const { ids: allowedEnvironments, idsSet: allowedEnvironmentIds, errors: allowedEnvironmentErrors, } = validateAllowedEnvironmentIds(config.allowedEnvironments, "config.allowedEnvironments");
@@ -192,10 +203,13 @@ export const resolveConfig = async (config, override = {}) => {
192
203
  const historyPath = override.historyPath ?? config.historyPath;
193
204
  const historyLimit = override.historyLimit ?? config.historyLimit;
194
205
  const appendHistory = config.appendHistory ?? true;
195
- const knownIssuesPath = resolve(override.knownIssuesPath ?? config.knownIssuesPath ?? "./allure/known.json");
206
+ const configuredKnownIssuesPath = override.knownIssuesPath ?? config.knownIssuesPath;
207
+ const knownIssuesPathInput = configuredKnownIssuesPath ?? (hasKnownIssueRules(config.knownIssues) ? DEFAULT_KNOWN_ISSUES_PATH : undefined);
208
+ const knownIssuesPath = await resolveExactIssuesFilePath(knownIssuesPathInput, "known issues");
196
209
  const output = resolve(override.output ?? config.output ?? "./allure-report");
197
- const known = await readKnownIssues(knownIssuesPath);
210
+ const known = knownIssuesPath ? await readKnownIssues(knownIssuesPath) : undefined;
198
211
  const variables = config.variables ?? {};
212
+ const resultsDir = normalizeResultsDir(config.resultsDir);
199
213
  let pluginInstances = [];
200
214
  const hasPluginsOverride = override.plugins !== undefined;
201
215
  if (!hasPluginsOverride || Object.keys(override.plugins ?? {}).length > 0) {
@@ -225,6 +239,7 @@ export const resolveConfig = async (config, override = {}) => {
225
239
  hideLabels,
226
240
  knownIssuesPath,
227
241
  known,
242
+ knownIssues: config.knownIssues,
228
243
  environment,
229
244
  allowedEnvironments,
230
245
  variables,
@@ -247,6 +262,7 @@ export const resolveConfig = async (config, override = {}) => {
247
262
  : undefined,
248
263
  categories: config.categories,
249
264
  globalAttachments: config.globalAttachments,
265
+ ...(resultsDir.length ? { resultsDir } : {}),
250
266
  };
251
267
  };
252
268
  export const readConfig = async (cwd = process.cwd(), configPath, override) => {
@@ -265,6 +281,11 @@ export const readConfig = async (cwd = process.cwd(), configPath, override) => {
265
281
  case ".mjs":
266
282
  config = await loadJsConfig(cfg);
267
283
  break;
284
+ case ".ts":
285
+ case ".cts":
286
+ case ".mts":
287
+ config = await loadTsConfig(cfg);
288
+ break;
268
289
  default:
269
290
  config = DEFAULT_CONFIG;
270
291
  }
@@ -283,6 +304,10 @@ export const readRawConfig = async (cwd = process.cwd(), configPath) => {
283
304
  case ".cjs":
284
305
  case ".mjs":
285
306
  return loadJsConfig(cfg);
307
+ case ".ts":
308
+ case ".cts":
309
+ case ".mts":
310
+ return loadTsConfig(cfg);
286
311
  default:
287
312
  return DEFAULT_CONFIG;
288
313
  }
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./utils/path.js";
5
5
  export * from "./utils/new.js";
6
6
  export * from "./utils/flaky.js";
7
7
  export * from "./utils/environment.js";
8
+ export * from "./utils/resultsDir.js";
8
9
  export * from "./history.js";
9
10
  export * from "./known.js";
10
11
  export { resolveConfig, readConfig, readRawConfig, getPluginInstance } from "./config.js";
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export * from "./utils/path.js";
4
4
  export * from "./utils/new.js";
5
5
  export * from "./utils/flaky.js";
6
6
  export * from "./utils/environment.js";
7
+ export * from "./utils/resultsDir.js";
7
8
  export * from "./history.js";
8
9
  export * from "./known.js";
9
10
  export { resolveConfig, readConfig, readRawConfig, getPluginInstance } from "./config.js";
package/dist/known.d.ts CHANGED
@@ -1,4 +1,8 @@
1
- import type { KnownTestFailure } from "@allurereport/core-api";
1
+ import type { KnownIssuesConfig, KnownTestFailure, TestResult } from "@allurereport/core-api";
2
2
  import type { AllureStore } from "@allurereport/plugin-api";
3
+ export declare const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
4
+ export declare const hasKnownIssueRules: (knownIssues?: KnownIssuesConfig) => boolean;
5
+ export declare const getKnownIssueByRules: (testResult: TestResult, knownIssues: KnownIssuesConfig | undefined, environmentId?: string) => KnownTestFailure | undefined;
6
+ export declare const resolveExactIssuesFilePath: (pathOrDir: string | undefined, label: string) => Promise<string | undefined>;
3
7
  export declare const readKnownIssues: (knownIssuePath: string) => Promise<KnownTestFailure[]>;
4
8
  export declare const writeKnownIssues: (store: AllureStore, knownIssuesPath?: string) => Promise<void>;
package/dist/known.js CHANGED
@@ -1,9 +1,66 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import { resolve } from "node:path";
1
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, extname, resolve } from "node:path";
3
3
  import { isFileNotFoundError } from "./utils/misc.js";
4
- const failedStatuses = new Set(["failed", "broken"]);
4
+ export const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
5
+ export const hasKnownIssueRules = (knownIssues) => (knownIssues?.rules?.length ?? 0) > 0;
6
+ const messageMatches = (rule, testResult) => {
7
+ if (!rule.messageRegexp) {
8
+ return true;
9
+ }
10
+ const message = testResult.error?.message;
11
+ return typeof message === "string" && new RegExp(rule.messageRegexp).test(message);
12
+ };
13
+ const testCaseIdMatches = (rule, testResult) => {
14
+ const { id, allureId, externalId } = testResult.testCase ?? {};
15
+ return !rule.testCaseId || [id, allureId, externalId].includes(rule.testCaseId);
16
+ };
17
+ const retryHashMatches = (rule, testResult) => {
18
+ return !rule.retryHash || rule.retryHash === testResult.retryHash;
19
+ };
20
+ const environmentMatches = (rule, testResult) => {
21
+ return !rule.environmentId || rule.environmentId === testResult.environment;
22
+ };
23
+ export const getKnownIssueByRules = (testResult, knownIssues, environmentId) => {
24
+ if (!testResult.historyId || !hasKnownIssueRules(knownIssues)) {
25
+ return undefined;
26
+ }
27
+ const matchedRule = knownIssues.rules.find((rule) => [messageMatches, testCaseIdMatches, environmentMatches, retryHashMatches].every((matcher) => matcher(rule, testResult)));
28
+ if (!matchedRule) {
29
+ return undefined;
30
+ }
31
+ return {
32
+ historyId: testResult.historyId,
33
+ reason: matchedRule.decision.reason,
34
+ links: matchedRule.decision.links,
35
+ error: testResult.error,
36
+ };
37
+ };
38
+ export const resolveExactIssuesFilePath = async (pathOrDir, label) => {
39
+ if (!pathOrDir) {
40
+ return undefined;
41
+ }
42
+ const path = resolve(pathOrDir);
43
+ if (extname(path).toLowerCase() !== ".json") {
44
+ throw new Error(`Invalid ${label} path ${JSON.stringify(pathOrDir)}: expected exact .json file path`);
45
+ }
46
+ try {
47
+ const pathStats = await stat(path);
48
+ if (pathStats.isDirectory()) {
49
+ throw new Error(`Invalid ${label} path ${JSON.stringify(pathOrDir)}: expected file, got directory`);
50
+ }
51
+ }
52
+ catch (e) {
53
+ if (!isFileNotFoundError(e)) {
54
+ throw e;
55
+ }
56
+ }
57
+ return path;
58
+ };
5
59
  export const readKnownIssues = async (knownIssuePath) => {
6
- const path = resolve(knownIssuePath);
60
+ const path = await resolveExactIssuesFilePath(knownIssuePath, "known issues");
61
+ if (!path) {
62
+ return [];
63
+ }
7
64
  try {
8
65
  const content = await readFile(path, { encoding: "utf-8" });
9
66
  return JSON.parse(content);
@@ -15,18 +72,15 @@ export const readKnownIssues = async (knownIssuePath) => {
15
72
  throw e;
16
73
  }
17
74
  };
18
- export const writeKnownIssues = async (store, knownIssuesPath) => {
19
- if (!knownIssuesPath) {
75
+ const writeKnownIssueRecords = async (knownIssues, knownIssuesPath) => {
76
+ const path = await resolveExactIssuesFilePath(knownIssuesPath, "known issues");
77
+ if (!path) {
20
78
  return;
21
79
  }
22
- const testResults = await store.allTestResults();
23
- const knownIssues = testResults
24
- .filter((tr) => failedStatuses.has(tr.status))
25
- .filter((tr) => tr.historyId)
26
- .map(({ historyId, links }) => ({
27
- historyId: historyId,
28
- issues: links.filter((l) => l.type === "issue"),
29
- comment: "automatically generated from failure by allure known-issue command",
30
- }));
31
- await writeFile(knownIssuesPath, JSON.stringify(knownIssues));
80
+ await mkdir(dirname(path), { recursive: true });
81
+ await writeFile(path, `${JSON.stringify(knownIssues)}\n`, "utf-8");
82
+ };
83
+ export const writeKnownIssues = async (store, knownIssuesPath) => {
84
+ const knownIssues = await store.allKnownIssues();
85
+ await writeKnownIssueRecords(knownIssues, knownIssuesPath);
32
86
  };
@@ -4,8 +4,11 @@ export declare const stringifyQualityGateResults: (results: QualityGateValidatio
4
4
  export declare const convertQualityGateResultsToTestErrors: (results: QualityGateValidationResult[]) => TestError[];
5
5
  export declare class QualityGateState {
6
6
  #private;
7
- setResult(rule: string, value: any): void;
7
+ setResult(rule: string, value: any, testResults?: string[]): void;
8
8
  getResult(rule: string): any;
9
+ getTestResults(rule: string): string[];
10
+ markTestResultsProcessed(testResults: TestResult[]): void;
11
+ isTestResultProcessed(trId: string): boolean;
9
12
  }
10
13
  export declare class QualityGate {
11
14
  private readonly config;
@@ -3,7 +3,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
3
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
4
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
5
  };
6
- var _QualityGateState_state;
6
+ var _QualityGateState_state, _QualityGateState_processedTestResultIds;
7
7
  import { gray, red } from "yoctocolors";
8
8
  import { qualityGateDefaultRules } from "./rules.js";
9
9
  export const stringifyQualityGateResults = (results) => {
@@ -30,21 +30,43 @@ export const convertQualityGateResultsToTestErrors = (results) => {
30
30
  export class QualityGateState {
31
31
  constructor() {
32
32
  _QualityGateState_state.set(this, {});
33
+ _QualityGateState_processedTestResultIds.set(this, new Set());
33
34
  }
34
- setResult(rule, value) {
35
- __classPrivateFieldGet(this, _QualityGateState_state, "f")[rule] = value;
35
+ setResult(rule, value, testResults = []) {
36
+ const previousTestResults = __classPrivateFieldGet(this, _QualityGateState_state, "f")[rule]?.testResults ?? [];
37
+ __classPrivateFieldGet(this, _QualityGateState_state, "f")[rule] = {
38
+ result: value,
39
+ testResults: [...new Set([...previousTestResults, ...testResults])],
40
+ };
36
41
  }
37
42
  getResult(rule) {
38
- return __classPrivateFieldGet(this, _QualityGateState_state, "f")[rule];
43
+ return __classPrivateFieldGet(this, _QualityGateState_state, "f")[rule]?.result;
44
+ }
45
+ getTestResults(rule) {
46
+ return [...(__classPrivateFieldGet(this, _QualityGateState_state, "f")[rule]?.testResults ?? [])];
47
+ }
48
+ markTestResultsProcessed(testResults) {
49
+ testResults.forEach(({ id }) => __classPrivateFieldGet(this, _QualityGateState_processedTestResultIds, "f").add(id));
50
+ }
51
+ isTestResultProcessed(trId) {
52
+ return __classPrivateFieldGet(this, _QualityGateState_processedTestResultIds, "f").has(trId);
39
53
  }
40
54
  }
41
- _QualityGateState_state = new WeakMap();
55
+ _QualityGateState_state = new WeakMap(), _QualityGateState_processedTestResultIds = new WeakMap();
42
56
  export class QualityGate {
43
57
  constructor(config) {
44
58
  this.config = config;
45
59
  }
46
60
  async validate(payload) {
47
61
  const { state, trs, knownIssues, environment } = payload;
62
+ const trsToValidateById = new Map();
63
+ for (const tr of trs) {
64
+ if (tr.isRetry || state?.isTestResultProcessed(tr.id)) {
65
+ continue;
66
+ }
67
+ trsToValidateById.set(tr.id, tr);
68
+ }
69
+ const trsToValidate = trsToValidateById.values().toArray();
48
70
  const { rules, use = [...qualityGateDefaultRules] } = this.config;
49
71
  const results = [];
50
72
  let fastFailed = false;
@@ -58,6 +80,7 @@ export class QualityGate {
58
80
  if (fastFailed) {
59
81
  break;
60
82
  }
83
+ const filteredTrs = ruleset.filter ? trsToValidate.filter(ruleset.filter) : trsToValidate;
61
84
  for (const [key, expected] of Object.entries(ruleset)) {
62
85
  if (key === "filter" || key === "id" || key === "fastFail") {
63
86
  continue;
@@ -66,13 +89,12 @@ export class QualityGate {
66
89
  if (!rule) {
67
90
  throw new Error(`Rule ${key} is not provided. Make sure you have provided it in the "use" field of the quality gate config!`);
68
91
  }
69
- const trsToValidate = ruleset.filter ? trs.filter(ruleset.filter) : trs;
70
92
  const ruleId = ruleset.id ? [ruleset.id, rule.rule].join("/") : rule.rule;
71
93
  const result = await rule.validate({
72
- trs: trsToValidate,
94
+ trs: filteredTrs,
73
95
  state: {
74
96
  getResult: () => state?.getResult?.(ruleId),
75
- setResult: (value) => state?.setResult?.(ruleId, value),
97
+ setResult: (value, testResults) => state?.setResult?.(ruleId, value, testResults),
76
98
  },
77
99
  expected,
78
100
  knownIssues,
@@ -84,12 +106,13 @@ export class QualityGate {
84
106
  results.push({
85
107
  ...result,
86
108
  expected,
87
- rule: ruleset.id ? [ruleset.id, rule.rule].join("/") : rule.rule,
109
+ rule: ruleId,
88
110
  message: rule.message({
89
111
  actual: result.actual,
90
112
  expected,
91
113
  }),
92
114
  environment,
115
+ testResults: [...new Set([...(state?.getTestResults(ruleId) ?? []), ...result.testResults])],
93
116
  });
94
117
  if (ruleset.fastFail) {
95
118
  fastFailed = true;
@@ -97,6 +120,7 @@ export class QualityGate {
97
120
  }
98
121
  }
99
122
  }
123
+ state?.markTestResultsProcessed(trsToValidate);
100
124
  return {
101
125
  fastFailed,
102
126
  results,
@@ -1,8 +1,16 @@
1
1
  import { type QualityGateRule } from "@allurereport/plugin-api";
2
2
  export declare const maxFailuresRule: QualityGateRule<number>;
3
3
  export declare const minTestsCountRule: QualityGateRule<number>;
4
- export declare const successRateRule: QualityGateRule<number>;
4
+ export declare const successRateRule: QualityGateRule<number, {
5
+ totalCount: number;
6
+ unknownCount: number;
7
+ passedCount: number;
8
+ }>;
5
9
  export declare const maxDurationRule: QualityGateRule<number>;
6
- export declare const allTestsContainEnvRule: QualityGateRule<string>;
10
+ export declare const allTestsContainEnvRule: QualityGateRule<string, number>;
7
11
  export declare const environmentsTestedRule: QualityGateRule<string[]>;
8
- export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<string> | QualityGateRule<string[]>)[];
12
+ export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<number, {
13
+ totalCount: number;
14
+ unknownCount: number;
15
+ passedCount: number;
16
+ }> | QualityGateRule<string, number> | QualityGateRule<string[]>)[];
@@ -1,17 +1,19 @@
1
- import { filterSuccessful, filterUnknownByKnownIssues, filterUnsuccessful } from "@allurereport/core-api";
1
+ import { filterSuccessful, filterUnsuccessful } from "@allurereport/core-api";
2
2
  import { bold } from "yoctocolors";
3
3
  export const maxFailuresRule = {
4
4
  rule: "maxFailures",
5
5
  message: ({ actual, expected }) => `The number of failed tests ${bold(String(actual))} exceeds the allowed threshold value ${bold(String(expected))}`,
6
- validate: async ({ trs, knownIssues, expected, state }) => {
7
- const knownIssuesHistoryIds = new Set(knownIssues.map(({ historyId }) => historyId));
8
- const unknown = filterUnknownByKnownIssues(trs, knownIssuesHistoryIds);
6
+ validate: async ({ trs, expected, state }) => {
7
+ const previous = state.getResult() ?? 0;
8
+ const unknown = trs.filter((tr) => !tr.known);
9
9
  const failedTrs = unknown.filter(filterUnsuccessful);
10
- const actual = failedTrs.length + (state.getResult() ?? 0);
11
- state.setResult(actual);
10
+ const testResults = failedTrs.map((tr) => tr.id);
11
+ const actual = previous + failedTrs.length;
12
+ state.setResult(actual, testResults);
12
13
  return {
13
14
  success: actual <= expected,
14
15
  actual,
16
+ testResults,
15
17
  };
16
18
  },
17
19
  };
@@ -20,47 +22,64 @@ export const minTestsCountRule = {
20
22
  message: ({ actual, expected }) => `The total number of tests ${bold(String(actual))} is less than the expected threshold value ${bold(String(expected))}`,
21
23
  validate: async ({ trs, expected, state }) => {
22
24
  const actual = trs.length + (state.getResult() ?? 0);
23
- state.setResult(actual);
25
+ state.setResult(actual, []);
24
26
  return {
25
27
  success: actual >= expected,
26
28
  actual,
29
+ testResults: [],
27
30
  };
28
31
  },
29
32
  };
30
33
  export const successRateRule = {
31
34
  rule: "successRate",
32
35
  message: ({ actual, expected }) => `Success rate ${bold(String(actual))} is less, than expected ${bold(String(expected))}`,
33
- validate: async ({ trs, knownIssues, expected }) => {
34
- const knownIssuesHistoryIds = new Set(knownIssues.map(({ historyId }) => historyId));
35
- const unknown = filterUnknownByKnownIssues(trs, knownIssuesHistoryIds);
36
+ validate: async ({ trs, expected, state }) => {
37
+ const previous = state.getResult() ?? { totalCount: 0, unknownCount: 0, passedCount: 0 };
38
+ const unknown = trs.filter((tr) => !tr.known);
36
39
  const passedTrs = unknown.filter(filterSuccessful);
37
- const rate = passedTrs.length === 0 ? 0 : passedTrs.length / unknown.length;
40
+ const notPassedTrs = unknown.filter((tr) => !filterSuccessful(tr));
41
+ const totalCount = previous.totalCount + trs.length;
42
+ const unknownCount = previous.unknownCount + unknown.length;
43
+ const passedCount = previous.passedCount + passedTrs.length;
44
+ const testResults = notPassedTrs.map((tr) => tr.id);
45
+ const rate = totalCount === 0 ? 0 : unknownCount === 0 ? 1 : passedCount / unknownCount;
46
+ state.setResult({ totalCount, unknownCount, passedCount }, testResults);
38
47
  return {
39
48
  success: rate >= expected,
40
49
  actual: rate,
50
+ testResults,
41
51
  };
42
52
  },
43
53
  };
44
54
  export const maxDurationRule = {
45
55
  rule: "maxDuration",
46
56
  message: ({ actual, expected }) => `Maximum duration of some tests exceed the defined limit; actual ${bold(String(actual))}, expected ${bold(String(expected))}`,
47
- validate: async ({ trs, expected }) => {
48
- const actual = Math.max(...trs.map((tr) => tr.duration ?? 0));
57
+ validate: async ({ trs, expected, state }) => {
58
+ const previous = state.getResult() ?? 0;
59
+ const actual = Math.max(previous, ...trs.map((tr) => tr.duration ?? 0));
60
+ const tooLongTrs = trs.filter((tr) => (tr.duration ?? 0) > expected);
61
+ const testResults = tooLongTrs.map((tr) => tr.id);
62
+ state.setResult(actual, testResults);
49
63
  return {
50
64
  success: actual <= expected,
51
65
  actual,
66
+ testResults,
52
67
  };
53
68
  },
54
69
  };
55
70
  export const allTestsContainEnvRule = {
56
71
  rule: "allTestsContainEnv",
57
- message: ({ actual, expected }) => `Not all tests contain the required environment, ${bold(`"${expected}"`)}; ${bold(actual.length === 1 ? "one" : String(actual))} ${actual.length === 1 ? "test has" : "tests have"} different or missing environment`,
58
- validate: async ({ trs, expected }) => {
72
+ message: ({ actual, expected }) => `Not all tests contain the required "${bold(expected)}" environment, ${bold(actual)} tests have different or missing environment`,
73
+ validate: async ({ trs, expected, state }) => {
74
+ const previous = state.getResult() ?? 0;
59
75
  const testsWithoutEnv = trs.filter((tr) => (tr.environment ?? "") !== expected);
60
- const actual = testsWithoutEnv.length;
76
+ const testResults = testsWithoutEnv.map((tr) => tr.id);
77
+ const actual = testsWithoutEnv.length + previous;
78
+ state.setResult(actual, testResults);
61
79
  return {
62
80
  success: actual === 0,
63
81
  actual,
82
+ testResults,
64
83
  };
65
84
  },
66
85
  };
@@ -71,11 +90,12 @@ export const environmentsTestedRule = {
71
90
  const previouslyTested = new Set(state.getResult() ?? []);
72
91
  const batchTested = trs.map((tr) => tr.environment).filter((env) => env != null && env !== "");
73
92
  const testedEnvs = new Set([...previouslyTested, ...batchTested]);
74
- state.setResult([...testedEnvs]);
93
+ state.setResult([...testedEnvs], []);
75
94
  const missing = expected.filter((env) => !testedEnvs.has(env));
76
95
  return {
77
96
  success: missing.length === 0,
78
97
  actual: missing,
98
+ testResults: [],
79
99
  };
80
100
  },
81
101
  };
package/dist/report.js CHANGED
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  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");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _AllureReport_instances, _AllureReport_ci, _AllureReport_store, _AllureReport_readers, _AllureReport_plugins, _AllureReport_reportFiles, _AllureReport_realtimeChannel, _AllureReport_realtimeUpdateScheduler, _AllureReport_realTime, _AllureReport_hideLabels, _AllureReport_output, _AllureReport_history, _AllureReport_allureServiceClient, _AllureReport_qualityGate, _AllureReport_dump, _AllureReport_categories, _AllureReport_environments, _AllureReport_globalAttachments, _AllureReport_dumpTempDirs, _AllureReport_state, _AllureReport_executionStage, _AllureReport_historyDataPoint, _AllureReport_summaryPath, _AllureReport_summariesByPluginId, _AllureReport_publishedRemoteHrefs, _AllureReport_published, _AllureReport_endGeneratePerfSpan, _AllureReport_publish, _AllureReport_runRealtimeUpdate, _AllureReport_getReportsToPublish, _AllureReport_cleanupFailedRemoteReport, _AllureReport_logPublishError, _AllureReport_applyPublishLinksToSummaries, _AllureReport_cloneSummariesByPluginId, _AllureReport_writeSummaryFiles, _AllureReport_generateRootSummary, _AllureReport_finishGeneratePerfSpan, _AllureReport_finishPerfMetrics, _AllureReport_eachPlugin, _AllureReport_getPluginState;
12
+ var _AllureReport_instances, _AllureReport_ci, _AllureReport_store, _AllureReport_readers, _AllureReport_plugins, _AllureReport_reportFiles, _AllureReport_realtimeChannel, _AllureReport_realtimeUpdateScheduler, _AllureReport_realTime, _AllureReport_hideLabels, _AllureReport_output, _AllureReport_history, _AllureReport_appendHistory, _AllureReport_allureServiceClient, _AllureReport_qualityGate, _AllureReport_dump, _AllureReport_categories, _AllureReport_environments, _AllureReport_globalAttachments, _AllureReport_knownIssuesPath, _AllureReport_dumpTempDirs, _AllureReport_state, _AllureReport_executionStage, _AllureReport_historyDataPoint, _AllureReport_summaryPath, _AllureReport_testResultsRegistryPath, _AllureReport_summariesByPluginId, _AllureReport_publishedRemoteHrefs, _AllureReport_published, _AllureReport_endGeneratePerfSpan, _AllureReport_publish, _AllureReport_runRealtimeUpdate, _AllureReport_restoreStateDumps, _AllureReport_getReportsToPublish, _AllureReport_cleanupFailedRemoteReport, _AllureReport_logPublishError, _AllureReport_applyPublishLinksToSummaries, _AllureReport_cloneSummariesByPluginId, _AllureReport_writeSummaryFiles, _AllureReport_writeTestResultRegistry, _AllureReport_generateRootSummary, _AllureReport_finishGeneratePerfSpan, _AllureReport_finishPerfMetrics, _AllureReport_eachPlugin, _AllureReport_getPluginState;
13
13
  import console from "node:console";
14
14
  import { randomUUID } from "node:crypto";
15
15
  import { once } from "node:events";
@@ -19,8 +19,9 @@ import { tmpdir } from "node:os";
19
19
  import { basename, dirname, join, resolve, sep } from "node:path";
20
20
  import { promisify } from "node:util";
21
21
  import { detect } from "@allurereport/ci";
22
+ import { createProgressLogger } from "@allurereport/cli-commons";
22
23
  import { normalizeCategoriesConfig } from "@allurereport/core-api";
23
- import { AllureStoreDumpFiles, } from "@allurereport/plugin-api";
24
+ import { AllureStoreDumpFiles, createTestResultRegistry, } from "@allurereport/plugin-api";
24
25
  import { allure1, allure2, attachments, cucumberjson, junitXml, readXcResultBundle } from "@allurereport/reader";
25
26
  import { PathResultFile } from "@allurereport/reader-api";
26
27
  import { AllureRemoteHistory, AllureServiceClient, AllureTestOpsClient, KnownError, UnknownError, } from "@allurereport/service";
@@ -30,6 +31,7 @@ import ZipReadStream from "node-stream-zip";
30
31
  import pLimit from "p-limit";
31
32
  import ZipWriteStream from "zip-stream";
32
33
  import { AllureLocalHistory, createHistory } from "./history.js";
34
+ import { writeKnownIssues } from "./known.js";
33
35
  import { DefaultPluginState, PluginFiles } from "./plugin.js";
34
36
  import { QualityGate } from "./qualityGate/index.js";
35
37
  import { DefaultAllureStore } from "./store/store.js";
@@ -42,6 +44,7 @@ const { version } = JSON.parse(readFileSync(new URL("../package.json", import.me
42
44
  const INIT_REQUIRED_ERROR_MESSAGE = "report is not initialised. Call the start() method first.";
43
45
  const DEFAULT_READ_CONCURRENCY = 64;
44
46
  const MAX_READ_CONCURRENCY = 256;
47
+ const TEST_RESULTS_REGISTRY_FILENAME = "test-results.json";
45
48
  const readConcurrency = () => {
46
49
  const parsed = Number.parseInt(process.env.ALLURE_READ_CONCURRENCY ?? "", 10);
47
50
  if (!Number.isFinite(parsed)) {
@@ -80,17 +83,20 @@ export class AllureReport {
80
83
  _AllureReport_hideLabels.set(this, void 0);
81
84
  _AllureReport_output.set(this, void 0);
82
85
  _AllureReport_history.set(this, void 0);
86
+ _AllureReport_appendHistory.set(this, void 0);
83
87
  _AllureReport_allureServiceClient.set(this, void 0);
84
88
  _AllureReport_qualityGate.set(this, void 0);
85
89
  _AllureReport_dump.set(this, void 0);
86
90
  _AllureReport_categories.set(this, void 0);
87
91
  _AllureReport_environments.set(this, void 0);
88
92
  _AllureReport_globalAttachments.set(this, void 0);
93
+ _AllureReport_knownIssuesPath.set(this, void 0);
89
94
  _AllureReport_dumpTempDirs.set(this, []);
90
95
  _AllureReport_state.set(this, void 0);
91
96
  _AllureReport_executionStage.set(this, "init");
92
97
  _AllureReport_historyDataPoint.set(this, void 0);
93
98
  _AllureReport_summaryPath.set(this, void 0);
99
+ _AllureReport_testResultsRegistryPath.set(this, void 0);
94
100
  _AllureReport_summariesByPluginId.set(this, new Map());
95
101
  _AllureReport_publishedRemoteHrefs.set(this, new Set());
96
102
  _AllureReport_published.set(this, false);
@@ -109,6 +115,7 @@ export class AllureReport {
109
115
  historyPoint = createHistory(this.reportUuid, this.reportName, allTcs, allTrs, this.reportUrl);
110
116
  __classPrivateFieldSet(this, _AllureReport_historyDataPoint, historyPoint, "f");
111
117
  }
118
+ await __classPrivateFieldGet(this, _AllureReport_writeTestResultRegistry, "f").call(this);
112
119
  await __classPrivateFieldGet(this, _AllureReport_writeSummaryFiles, "f").call(this);
113
120
  await __classPrivateFieldGet(this, _AllureReport_generateRootSummary, "f").call(this);
114
121
  if (__classPrivateFieldGet(this, _AllureReport_realTime, "f") || !__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")) {
@@ -124,33 +131,21 @@ export class AllureReport {
124
131
  const linksByPluginId = {};
125
132
  const summariesSnapshot = __classPrivateFieldGet(this, _AllureReport_cloneSummariesByPluginId, "f").call(this);
126
133
  const uploadProgressMessage = reportsToPublish.length === 1 ? `Publishing "${reportsToPublish[0].pluginId}" report` : "Publishing reports";
127
- const totalFilesToUpload = reportsToPublish.reduce((acc, report) => acc + Object.keys(report.files).length, 0);
128
- const progressStep = Math.max(1, Math.ceil(totalFilesToUpload / 20));
134
+ const totalFilesToUpload = reportsToPublish.reduce((acc, report) => acc + Object.keys(report.files).length, 0) +
135
+ (__classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") ? 1 : 0);
129
136
  let summariesMutated = false;
130
137
  let reportCreated = false;
131
138
  let publishErrorMessage = "Report upload has failed, the report won't be published";
132
- let uploadedFiles = 0;
133
- let nextProgressLogAt = 0;
134
- let lastProgressLog = -1;
139
+ const progressLogger = createProgressLogger({
140
+ total: totalFilesToUpload,
141
+ message: uploadProgressMessage,
142
+ unitLabel: "files uploaded",
143
+ prefix: "[AllureReport]",
144
+ silent: !!__classPrivateFieldGet(this, _AllureReport_realTime, "f"),
145
+ });
135
146
  const endPublishPerfSpan = startPerfSpan(PERF_METRIC_NAMES.publishUploadTotal);
136
- const logUploadProgress = (force = false) => {
137
- if (force && uploadedFiles === lastProgressLog) {
138
- return;
139
- }
140
- if (!force && uploadedFiles < nextProgressLogAt && uploadedFiles !== totalFilesToUpload) {
141
- return;
142
- }
143
- if (!force && uploadedFiles === lastProgressLog) {
144
- return;
145
- }
146
- console.info(`[AllureReport]: ${uploadProgressMessage}: ${uploadedFiles}/${totalFilesToUpload} files uploaded`);
147
- lastProgressLog = uploadedFiles;
148
- nextProgressLogAt = Math.min(totalFilesToUpload, uploadedFiles + progressStep);
149
- };
150
- const incrementUploadProgress = (delta = 1) => {
151
- uploadedFiles = Math.min(totalFilesToUpload, uploadedFiles + delta);
152
- logUploadProgress();
153
- };
147
+ const logUploadProgress = progressLogger.log;
148
+ const incrementUploadProgress = progressLogger.increment;
154
149
  try {
155
150
  logUploadProgress(true);
156
151
  await client.createReport({
@@ -172,6 +167,14 @@ export class AllureReport {
172
167
  linksByPluginId[report.pluginId] = uploadResult.indexHref;
173
168
  }
174
169
  }
170
+ if (__classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f")) {
171
+ publishErrorMessage = "Test results registry upload has failed, the report won't be published";
172
+ await client.uploadReport({
173
+ reportUuid: this.reportUuid,
174
+ files: { [TEST_RESULTS_REGISTRY_FILENAME]: __classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") },
175
+ onProgress: incrementUploadProgress,
176
+ });
177
+ }
175
178
  const changedPluginIds = __classPrivateFieldGet(this, _AllureReport_applyPublishLinksToSummaries, "f").call(this, linksByPluginId);
176
179
  summariesMutated = changedPluginIds.size > 0;
177
180
  if (changedPluginIds.size > 0) {
@@ -225,6 +228,7 @@ export class AllureReport {
225
228
  __classPrivateFieldGet(this, _AllureReport_logPublishError, "f").call(this, publishErrorMessage, err);
226
229
  }
227
230
  finally {
231
+ progressLogger.cancel?.();
228
232
  endPublishPerfSpan();
229
233
  }
230
234
  });
@@ -238,7 +242,7 @@ export class AllureReport {
238
242
  }
239
243
  try {
240
244
  const entries = (await readdir(resultsDirPath, { withFileTypes: true }))
241
- .filter((dirent) => dirent.isFile())
245
+ .filter((dirent) => dirent.isFile() && !dirent.name.endsWith(".tmp"))
242
246
  .sort((a, b) => a.name.localeCompare(b.name));
243
247
  const limit = pLimit(readConcurrency());
244
248
  await Promise.all(entries.map((dirent) => limit(async () => {
@@ -280,6 +284,11 @@ export class AllureReport {
280
284
  };
281
285
  this.validate = async (params) => {
282
286
  const { trs, knownIssues, state, environment } = params;
287
+ const currentKnownIssues = await __classPrivateFieldGet(this, _AllureReport_store, "f").allKnownIssues();
288
+ const effectiveKnownIssues = new Map();
289
+ [...knownIssues, ...currentKnownIssues].forEach((issue) => {
290
+ effectiveKnownIssues.set(issue.historyId, issue);
291
+ });
283
292
  const qualityGateEnvironment = environment === undefined
284
293
  ? undefined
285
294
  : (environmentIdentityById(__classPrivateFieldGet(this, _AllureReport_environments, "f"), environment)?.name ??
@@ -287,7 +296,7 @@ export class AllureReport {
287
296
  environment);
288
297
  return __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").validate({
289
298
  trs: trs.filter(Boolean),
290
- knownIssues,
299
+ knownIssues: [...effectiveKnownIssues.values()],
291
300
  state,
292
301
  environment: qualityGateEnvironment,
293
302
  });
@@ -384,7 +393,7 @@ export class AllureReport {
384
393
  const addJsonDumpEntry = async (entryName, value) => {
385
394
  await addRequiredDumpEntry(Buffer.from(JSON.stringify(value)), entryName);
386
395
  };
387
- const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, indexKnownByHistoryId = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
396
+ const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, knownIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
388
397
  [AllureStoreDumpFiles.TestResults, testResults],
389
398
  [AllureStoreDumpFiles.TestCases, testCases],
390
399
  [AllureStoreDumpFiles.Fixtures, fixtures],
@@ -392,6 +401,7 @@ export class AllureReport {
392
401
  [AllureStoreDumpFiles.CheckResults, checkResults],
393
402
  [AllureStoreDumpFiles.Environments, environments],
394
403
  [AllureStoreDumpFiles.ReportVariables, reportVariables],
404
+ [AllureStoreDumpFiles.KnownIssues, knownIssues],
395
405
  [AllureStoreDumpFiles.GlobalAttachments, globalAttachmentIds],
396
406
  [AllureStoreDumpFiles.GlobalErrors, globalErrors],
397
407
  [AllureStoreDumpFiles.IndexAttachmentsByTestResults, indexAttachmentByTestResult],
@@ -399,7 +409,6 @@ export class AllureReport {
399
409
  [AllureStoreDumpFiles.IndexTestResultsByTestCase, indexTestResultByTestCase],
400
410
  [AllureStoreDumpFiles.IndexAttachmentsByFixture, indexAttachmentByFixture],
401
411
  [AllureStoreDumpFiles.IndexFixturesByTestResult, indexFixturesByTestResult],
402
- [AllureStoreDumpFiles.IndexKnownByHistoryId, indexKnownByHistoryId],
403
412
  [AllureStoreDumpFiles.QualityGateResults, qualityGateResults],
404
413
  [AllureStoreDumpFiles.TestResultIngestOrder, testResultIdsIngestOrder],
405
414
  ];
@@ -467,7 +476,11 @@ export class AllureReport {
467
476
  }
468
477
  await rename(dumpTempPath, dumpPath);
469
478
  };
470
- this.restoreState = async (dumps) => measurePerf(PERF_METRIC_NAMES.restoreStateTotal, async () => {
479
+ this.restoreState = async (dumps) => {
480
+ __classPrivateFieldGet(this, _AllureReport_store, "f").resetIngestOrder();
481
+ await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, dumps);
482
+ };
483
+ _AllureReport_restoreStateDumps.set(this, async (dumps) => measurePerf(PERF_METRIC_NAMES.restoreStateTotal, async () => {
471
484
  for (const dump of dumps) {
472
485
  await measurePerf(PERF_METRIC_NAMES.restoreStateDump, async () => {
473
486
  if (!existsSync(dump)) {
@@ -504,7 +517,7 @@ export class AllureReport {
504
517
  await writeFile(nestedDumpPath, await dumpArchive.entryData(entryName));
505
518
  nestedDumpPaths.push(nestedDumpPath);
506
519
  }
507
- await this.restoreState(nestedDumpPaths);
520
+ await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, nestedDumpPaths);
508
521
  return;
509
522
  }
510
523
  }
@@ -522,7 +535,7 @@ export class AllureReport {
522
535
  const indexTestResultsByTestCaseEntry = await requiredEntryData(AllureStoreDumpFiles.IndexTestResultsByTestCase);
523
536
  const indexAttachmentsByFixtureEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByFixture);
524
537
  const indexFixturesByTestResultEntry = await requiredEntryData(AllureStoreDumpFiles.IndexFixturesByTestResult);
525
- const indexKnownByHistoryIdEntry = await requiredEntryData(AllureStoreDumpFiles.IndexKnownByHistoryId);
538
+ const knownIssuesEntry = await requiredEntryData(AllureStoreDumpFiles.KnownIssues);
526
539
  const qualityGateResultsEntry = await requiredEntryData(AllureStoreDumpFiles.QualityGateResults);
527
540
  const testResultIngestOrderEntry = await optionalEntryData(AllureStoreDumpFiles.TestResultIngestOrder);
528
541
  const attachmentsLinks = JSON.parse(attachmentsEntry.toString("utf8"));
@@ -535,6 +548,7 @@ export class AllureReport {
535
548
  case AllureStoreDumpFiles.Fixtures:
536
549
  case AllureStoreDumpFiles.Environments:
537
550
  case AllureStoreDumpFiles.ReportVariables:
551
+ case AllureStoreDumpFiles.KnownIssues:
538
552
  case AllureStoreDumpFiles.GlobalAttachments:
539
553
  case AllureStoreDumpFiles.GlobalErrors:
540
554
  case AllureStoreDumpFiles.IndexAttachmentsByTestResults:
@@ -542,7 +556,6 @@ export class AllureReport {
542
556
  case AllureStoreDumpFiles.IndexTestResultsByTestCase:
543
557
  case AllureStoreDumpFiles.IndexAttachmentsByFixture:
544
558
  case AllureStoreDumpFiles.IndexFixturesByTestResult:
545
- case AllureStoreDumpFiles.IndexKnownByHistoryId:
546
559
  case AllureStoreDumpFiles.QualityGateResults:
547
560
  case AllureStoreDumpFiles.TestResultIngestOrder:
548
561
  return acc;
@@ -570,7 +583,7 @@ export class AllureReport {
570
583
  indexTestResultByTestCase: JSON.parse(indexTestResultsByTestCaseEntry.toString("utf8")),
571
584
  indexAttachmentByFixture: JSON.parse(indexAttachmentsByFixtureEntry.toString("utf8")),
572
585
  indexFixturesByTestResult: JSON.parse(indexFixturesByTestResultEntry.toString("utf8")),
573
- indexKnownByHistoryId: JSON.parse(indexKnownByHistoryIdEntry.toString("utf8")),
586
+ knownIssues: JSON.parse(knownIssuesEntry.toString("utf8")),
574
587
  qualityGateResults: JSON.parse(qualityGateResultsEntry.toString("utf8")),
575
588
  testResultIdsIngestOrder: testResultIngestOrderEntry
576
589
  ? JSON.parse(testResultIngestOrderEntry.toString("utf8"))
@@ -624,7 +637,7 @@ export class AllureReport {
624
637
  }
625
638
  });
626
639
  }
627
- });
640
+ }));
628
641
  _AllureReport_getReportsToPublish.set(this, async () => {
629
642
  const reports = [];
630
643
  for (const { enabled, id, options } of __classPrivateFieldGet(this, _AllureReport_plugins, "f")) {
@@ -691,6 +704,11 @@ export class AllureReport {
691
704
  await context.reportFiles.addFile("summary.json", Buffer.from(JSON.stringify(summary)));
692
705
  });
693
706
  });
707
+ _AllureReport_writeTestResultRegistry.set(this, async () => {
708
+ const testResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults({ includeRetries: false });
709
+ const registry = createTestResultRegistry(testResults);
710
+ __classPrivateFieldSet(this, _AllureReport_testResultsRegistryPath, await __classPrivateFieldGet(this, _AllureReport_reportFiles, "f").addFile(TEST_RESULTS_REGISTRY_FILENAME, Buffer.from(JSON.stringify(registry))), "f");
711
+ });
694
712
  _AllureReport_generateRootSummary.set(this, async () => {
695
713
  const summaries = [...__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").values()].map(clonePluginSummary);
696
714
  if (summaries.length > 1) {
@@ -751,16 +769,21 @@ export class AllureReport {
751
769
  outputDirFiles = await readdir(__classPrivateFieldGet(this, _AllureReport_output, "f"));
752
770
  }
753
771
  catch { }
772
+ if (__classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f")) {
773
+ await writeKnownIssues(__classPrivateFieldGet(this, _AllureReport_store, "f"), __classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f"));
774
+ }
754
775
  if (outputDirFiles.length === 0) {
755
776
  return;
756
777
  }
757
- const reportPath = join(__classPrivateFieldGet(this, _AllureReport_output, "f"), outputDirFiles[0]);
758
- const reportStats = await lstat(reportPath);
759
- const outputEntriesStats = await Promise.all(outputDirFiles.map((file) => lstat(join(__classPrivateFieldGet(this, _AllureReport_output, "f"), file))));
760
- const outputDirectoryEntries = outputEntriesStats.filter((entry) => entry.isDirectory());
761
- if (reportStats.isDirectory() && outputDirectoryEntries.length === 1) {
778
+ const outputEntries = await Promise.all(outputDirFiles.map(async (file) => ({ file, stats: await lstat(join(__classPrivateFieldGet(this, _AllureReport_output, "f"), file)) })));
779
+ const outputDirectoryEntries = outputEntries.filter(({ stats }) => stats.isDirectory());
780
+ if (outputDirectoryEntries.length === 1) {
781
+ const reportPath = join(__classPrivateFieldGet(this, _AllureReport_output, "f"), outputDirectoryEntries[0].file);
762
782
  const reportContent = await readdir(reportPath);
763
783
  for (const entry of reportContent) {
784
+ if (entry === TEST_RESULTS_REGISTRY_FILENAME) {
785
+ continue;
786
+ }
764
787
  const currentFilePath = join(reportPath, entry);
765
788
  const newFilePath = resolve(dirname(currentFilePath), "..", entry);
766
789
  await rename(currentFilePath, newFilePath);
@@ -773,7 +796,7 @@ export class AllureReport {
773
796
  }
774
797
  catch { }
775
798
  }
776
- if (__classPrivateFieldGet(this, _AllureReport_history, "f")) {
799
+ if (__classPrivateFieldGet(this, _AllureReport_history, "f") && __classPrivateFieldGet(this, _AllureReport_appendHistory, "f")) {
777
800
  try {
778
801
  await __classPrivateFieldGet(this, _AllureReport_store, "f").appendHistory(__classPrivateFieldGet(this, _AllureReport_historyDataPoint, "f"));
779
802
  }
@@ -870,7 +893,7 @@ export class AllureReport {
870
893
  }
871
894
  }
872
895
  });
873
- const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], known, reportFiles, realTime, historyPath, historyLimit, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, dump, categories, allureService, globalAttachments, } = opts;
896
+ const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], known, knownIssues, knownIssuesPath, reportFiles, realTime, historyPath, historyLimit, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, dump, categories, allureService, globalAttachments, } = opts;
874
897
  const allureServiceAccessToken = allureService?.accessToken;
875
898
  if (allureServiceAccessToken) {
876
899
  const allureServiceClientConfig = {
@@ -894,6 +917,8 @@ export class AllureReport {
894
917
  __classPrivateFieldSet(this, _AllureReport_hideLabels, hideLabels, "f");
895
918
  __classPrivateFieldSet(this, _AllureReport_environments, environments ?? {}, "f");
896
919
  __classPrivateFieldSet(this, _AllureReport_globalAttachments, globalAttachments, "f");
920
+ __classPrivateFieldSet(this, _AllureReport_appendHistory, appendHistory ?? true, "f");
921
+ __classPrivateFieldSet(this, _AllureReport_knownIssuesPath, knownIssuesPath, "f");
897
922
  if (qualityGate) {
898
923
  __classPrivateFieldSet(this, _AllureReport_qualityGate, new QualityGate(qualityGate), "f");
899
924
  }
@@ -918,6 +943,7 @@ export class AllureReport {
918
943
  environmentsConfig: environments,
919
944
  history: __classPrivateFieldGet(this, _AllureReport_history, "f"),
920
945
  known,
946
+ knownIssuesConfig: knownIssues,
921
947
  defaultLabels,
922
948
  environment,
923
949
  allowedEnvironments,
@@ -940,6 +966,6 @@ export class AllureReport {
940
966
  return __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").dispatcher;
941
967
  }
942
968
  }
943
- _AllureReport_ci = new WeakMap(), _AllureReport_store = new WeakMap(), _AllureReport_readers = new WeakMap(), _AllureReport_plugins = new WeakMap(), _AllureReport_reportFiles = new WeakMap(), _AllureReport_realtimeChannel = new WeakMap(), _AllureReport_realtimeUpdateScheduler = new WeakMap(), _AllureReport_realTime = new WeakMap(), _AllureReport_hideLabels = new WeakMap(), _AllureReport_output = new WeakMap(), _AllureReport_history = new WeakMap(), _AllureReport_allureServiceClient = new WeakMap(), _AllureReport_qualityGate = new WeakMap(), _AllureReport_dump = new WeakMap(), _AllureReport_categories = new WeakMap(), _AllureReport_environments = new WeakMap(), _AllureReport_globalAttachments = new WeakMap(), _AllureReport_dumpTempDirs = new WeakMap(), _AllureReport_state = new WeakMap(), _AllureReport_executionStage = new WeakMap(), _AllureReport_historyDataPoint = new WeakMap(), _AllureReport_summaryPath = new WeakMap(), _AllureReport_summariesByPluginId = new WeakMap(), _AllureReport_publishedRemoteHrefs = new WeakMap(), _AllureReport_published = new WeakMap(), _AllureReport_endGeneratePerfSpan = new WeakMap(), _AllureReport_publish = new WeakMap(), _AllureReport_runRealtimeUpdate = new WeakMap(), _AllureReport_getReportsToPublish = new WeakMap(), _AllureReport_cleanupFailedRemoteReport = new WeakMap(), _AllureReport_logPublishError = new WeakMap(), _AllureReport_applyPublishLinksToSummaries = new WeakMap(), _AllureReport_cloneSummariesByPluginId = new WeakMap(), _AllureReport_writeSummaryFiles = new WeakMap(), _AllureReport_generateRootSummary = new WeakMap(), _AllureReport_finishGeneratePerfSpan = new WeakMap(), _AllureReport_finishPerfMetrics = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_getPluginState = function _AllureReport_getPluginState(init, id) {
969
+ _AllureReport_ci = new WeakMap(), _AllureReport_store = new WeakMap(), _AllureReport_readers = new WeakMap(), _AllureReport_plugins = new WeakMap(), _AllureReport_reportFiles = new WeakMap(), _AllureReport_realtimeChannel = new WeakMap(), _AllureReport_realtimeUpdateScheduler = new WeakMap(), _AllureReport_realTime = new WeakMap(), _AllureReport_hideLabels = new WeakMap(), _AllureReport_output = new WeakMap(), _AllureReport_history = new WeakMap(), _AllureReport_appendHistory = new WeakMap(), _AllureReport_allureServiceClient = new WeakMap(), _AllureReport_qualityGate = new WeakMap(), _AllureReport_dump = new WeakMap(), _AllureReport_categories = new WeakMap(), _AllureReport_environments = new WeakMap(), _AllureReport_globalAttachments = new WeakMap(), _AllureReport_knownIssuesPath = new WeakMap(), _AllureReport_dumpTempDirs = new WeakMap(), _AllureReport_state = new WeakMap(), _AllureReport_executionStage = new WeakMap(), _AllureReport_historyDataPoint = new WeakMap(), _AllureReport_summaryPath = new WeakMap(), _AllureReport_testResultsRegistryPath = new WeakMap(), _AllureReport_summariesByPluginId = new WeakMap(), _AllureReport_publishedRemoteHrefs = new WeakMap(), _AllureReport_published = new WeakMap(), _AllureReport_endGeneratePerfSpan = new WeakMap(), _AllureReport_publish = new WeakMap(), _AllureReport_runRealtimeUpdate = new WeakMap(), _AllureReport_restoreStateDumps = new WeakMap(), _AllureReport_getReportsToPublish = new WeakMap(), _AllureReport_cleanupFailedRemoteReport = new WeakMap(), _AllureReport_logPublishError = new WeakMap(), _AllureReport_applyPublishLinksToSummaries = new WeakMap(), _AllureReport_cloneSummariesByPluginId = new WeakMap(), _AllureReport_writeSummaryFiles = new WeakMap(), _AllureReport_writeTestResultRegistry = new WeakMap(), _AllureReport_generateRootSummary = new WeakMap(), _AllureReport_finishGeneratePerfSpan = new WeakMap(), _AllureReport_finishPerfMetrics = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_getPluginState = function _AllureReport_getPluginState(init, id) {
944
970
  return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
945
971
  };
@@ -236,7 +236,7 @@ const convertLink = (link) => ({
236
236
  type: link.type,
237
237
  });
238
238
  const calculateTestId = (raw) => {
239
- const maybeAllureId = raw.labels?.find((label) => label.name === "ALLURE_ID" || label.name === "AS_ID")?.value;
239
+ const maybeAllureId = raw.labels?.find((label) => (label.name === "ALLURE_ID" || label.name === "AS_ID") && label.value !== "-1")?.value;
240
240
  if (maybeAllureId) {
241
241
  return [md5(`ALLURE_ID=${maybeAllureId}`), maybeAllureId];
242
242
  }
@@ -4,6 +4,7 @@ export declare const calculateRetryHash: (testCaseId: string | undefined, parame
4
4
  export declare class RetrySubstore {
5
5
  #private;
6
6
  recordIngestOrder(testResultId: string): void;
7
+ resetIngestOrder(): void;
7
8
  restoreIngestOrder(restoredIds: string[] | undefined, hasTestResult: (testResultId: string) => boolean): void;
8
9
  ingestOrderIdsForDump(): string[];
9
10
  upsert(testResult: TestResult): void;
@@ -31,8 +31,10 @@ export class RetrySubstore {
31
31
  }
32
32
  __classPrivateFieldGet(this, _RetrySubstore_testResultIngestIndexById, "f").set(testResultId, __classPrivateFieldGet(this, _RetrySubstore_testResultIngestIndexById, "f").size);
33
33
  }
34
- restoreIngestOrder(restoredIds, hasTestResult) {
34
+ resetIngestOrder() {
35
35
  __classPrivateFieldGet(this, _RetrySubstore_testResultIngestIndexById, "f").clear();
36
+ }
37
+ restoreIngestOrder(restoredIds, hasTestResult) {
36
38
  for (const id of restoredIds ?? []) {
37
39
  if (!hasTestResult(id) || __classPrivateFieldGet(this, _RetrySubstore_testResultIngestIndexById, "f").has(id)) {
38
40
  continue;
@@ -1,20 +1,21 @@
1
- import { type AllureCheckResult, type AllureHistory, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type KnownTestFailure, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
1
+ import { type AllureCheckResult, type AllureHistory, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type KnownIssuesConfig, type KnownTestFailure, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
2
2
  import { type AllureStore, type AllureStoreDump, type ExitCode, type PluginGlobalError, type QualityGateValidationResult, type RealtimeEventsDispatcher, type RealtimeSubscriber, type ResultFile, type TestResultFilter } from "@allurereport/plugin-api";
3
3
  import type { RawFixtureResult, RawGlobals, RawMetadata, RawTestResult, ReaderContext, ResultsVisitor } from "@allurereport/reader-api";
4
4
  export declare const mapToObject: <K extends string | number | symbol, T = any>(map: Map<K, T>) => Record<K, T>;
5
5
  export declare const updateMapWithRecord: <K extends string | number | symbol, T = any>(map: Map<K, T>, record: Record<K, T>) => Map<K, T>;
6
6
  export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
7
7
  #private;
8
+ readonly known: Map<string, KnownTestFailure>;
8
9
  readonly indexTestResultByTestCase: Map<string, TestResult[]>;
9
10
  readonly indexTestResultByEnvironmentId: Map<string, TestResult[]>;
10
11
  readonly indexTestResultByHistoryId: Map<string, TestResult[]>;
11
12
  readonly indexAttachmentByTestResult: Map<string, AttachmentLink[]>;
12
13
  readonly indexAttachmentByFixture: Map<string, AttachmentLink[]>;
13
14
  readonly indexFixturesByTestResult: Map<string, TestFixtureResult[]>;
14
- readonly indexKnownByHistoryId: Map<string, KnownTestFailure[]>;
15
15
  constructor(params?: {
16
16
  history?: AllureHistory;
17
17
  known?: KnownTestFailure[];
18
+ knownIssuesConfig?: KnownIssuesConfig;
18
19
  realtimeDispatcher?: RealtimeEventsDispatcher;
19
20
  realtimeSubscriber?: RealtimeSubscriber;
20
21
  defaultLabels?: DefaultLabelsConfig;
@@ -68,6 +69,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
68
69
  attachmentsByTrId(trId: string): Promise<AttachmentLink[]>;
69
70
  retriesByTr(tr?: TestResult): Promise<TestResult[]>;
70
71
  retriesByTrId(trId: string): Promise<TestResult[]>;
72
+ resetIngestOrder(): void;
71
73
  historyByTr(tr: TestResult): Promise<HistoryTestResult[] | undefined>;
72
74
  historyByTrId(trId: string): Promise<HistoryTestResult[] | undefined>;
73
75
  fixturesByTrId(trId: string): Promise<TestFixtureResult[]>;
@@ -78,6 +80,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
78
80
  retriesByTrId: Map<string, TestResult[]>;
79
81
  }>;
80
82
  failedTestResults(): Promise<TestResult[]>;
83
+ blockingFailedTestResults(): Promise<TestResult[]>;
81
84
  unknownFailedTestResults(): Promise<TestResult[]>;
82
85
  testResultsByLabel(labelName: string): Promise<{
83
86
  _: TestResult[];
@@ -9,10 +9,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  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");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _DefaultAllureStore_instances, _DefaultAllureStore_testResults, _DefaultAllureStore_environmentDisplayNames, _DefaultAllureStore_environmentNameToId, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_known, _DefaultAllureStore_fixtures, _DefaultAllureStore_defaultLabels, _DefaultAllureStore_environment, _DefaultAllureStore_environmentsConfig, _DefaultAllureStore_reportVariables, _DefaultAllureStore_realtimeDispatcher, _DefaultAllureStore_realtimeSubscriber, _DefaultAllureStore_allowedEnvironmentIds, _DefaultAllureStore_retrySubstore, _DefaultAllureStore_testResultIdsByEnvironmentId, _DefaultAllureStore_cachedEnvironmentEntries, _DefaultAllureStore_globalAttachmentIds, _DefaultAllureStore_globalAttachmentIdsByEnv, _DefaultAllureStore_globalErrors, _DefaultAllureStore_globalErrorsByEnv, _DefaultAllureStore_globalExitCode, _DefaultAllureStore_checkResultsById, _DefaultAllureStore_qualityGateResults, _DefaultAllureStore_historyPoints, _DefaultAllureStore_environments, _DefaultAllureStore_mergeEnvironmentIdentity, _DefaultAllureStore_environmentIdForLookup, _DefaultAllureStore_addEnvironments, _DefaultAllureStore_environmentIdByName, _DefaultAllureStore_setTestResultEnvironmentId, _DefaultAllureStore_assertAllowedEnvironmentId, _DefaultAllureStore_assertAllowedStoredEnvironment, _DefaultAllureStore_environmentIdByTestResult, _DefaultAllureStore_assignRetryHash, _DefaultAllureStore_rebuildRetrySubstore, _DefaultAllureStore_resolveGlobalEnvironmentIdentity, _DefaultAllureStore_globalAttachmentId, _DefaultAllureStore_indexGlobalError, _DefaultAllureStore_addGlobalError, _DefaultAllureStore_indexGlobalAttachment, _DefaultAllureStore_addGlobalAttachment, _DefaultAllureStore_restoreAttachmentContent, _DefaultAllureStore_relinkRestoredAttachmentSteps, _DefaultAllureStore_retriesByTr, _DefaultAllureStore_historyByTr;
12
+ var _DefaultAllureStore_instances, _DefaultAllureStore_testResults, _DefaultAllureStore_environmentDisplayNames, _DefaultAllureStore_environmentNameToId, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_knownIssuesConfig, _DefaultAllureStore_fixtures, _DefaultAllureStore_defaultLabels, _DefaultAllureStore_environment, _DefaultAllureStore_environmentsConfig, _DefaultAllureStore_reportVariables, _DefaultAllureStore_realtimeDispatcher, _DefaultAllureStore_realtimeSubscriber, _DefaultAllureStore_allowedEnvironmentIds, _DefaultAllureStore_retrySubstore, _DefaultAllureStore_testResultIdsByEnvironmentId, _DefaultAllureStore_cachedEnvironmentEntries, _DefaultAllureStore_globalAttachmentIds, _DefaultAllureStore_globalAttachmentIdsByEnv, _DefaultAllureStore_globalErrors, _DefaultAllureStore_globalErrorsByEnv, _DefaultAllureStore_globalExitCode, _DefaultAllureStore_checkResultsById, _DefaultAllureStore_qualityGateResults, _DefaultAllureStore_historyPoints, _DefaultAllureStore_environments, _DefaultAllureStore_historyIdMatches, _DefaultAllureStore_mergeEnvironmentIdentity, _DefaultAllureStore_environmentIdForLookup, _DefaultAllureStore_addEnvironments, _DefaultAllureStore_environmentIdByName, _DefaultAllureStore_setTestResultEnvironmentId, _DefaultAllureStore_assertAllowedEnvironmentId, _DefaultAllureStore_assertAllowedStoredEnvironment, _DefaultAllureStore_environmentIdByTestResult, _DefaultAllureStore_assignRetryHash, _DefaultAllureStore_setKnownIssue, _DefaultAllureStore_classifyKnownIssue, _DefaultAllureStore_rebuildRetrySubstore, _DefaultAllureStore_resolveGlobalEnvironmentIdentity, _DefaultAllureStore_globalAttachmentId, _DefaultAllureStore_indexGlobalError, _DefaultAllureStore_addGlobalError, _DefaultAllureStore_indexGlobalAttachment, _DefaultAllureStore_addGlobalAttachment, _DefaultAllureStore_restoreAttachmentContent, _DefaultAllureStore_relinkRestoredAttachmentSteps, _DefaultAllureStore_retriesByTr, _DefaultAllureStore_historyByTr;
13
13
  import { extname } from "node:path";
14
14
  import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, compareBy, createDictionary, getHistoryIdCandidates, getWorstStatus, normalizeHistoryDataPoint, ordinal, reverse, selectHistoryTestResults, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
15
15
  import { md5, } from "@allurereport/plugin-api";
16
+ import { getKnownIssueByRules } from "../known.js";
16
17
  import { environmentIdentityById, normalizeEnvironmentDescriptorMap, resolveEnvironmentIdentity, resolveStoredEnvironmentIdentity, validateAllowedEnvironmentId, } from "../utils/environment.js";
17
18
  import { isFlaky } from "../utils/flaky.js";
18
19
  import { getStatusTransition } from "../utils/new.js";
@@ -63,7 +64,8 @@ export class DefaultAllureStore {
63
64
  _DefaultAllureStore_testCases.set(this, void 0);
64
65
  _DefaultAllureStore_metadata.set(this, void 0);
65
66
  _DefaultAllureStore_history.set(this, void 0);
66
- _DefaultAllureStore_known.set(this, void 0);
67
+ _DefaultAllureStore_knownIssuesConfig.set(this, void 0);
68
+ this.known = new Map();
67
69
  _DefaultAllureStore_fixtures.set(this, void 0);
68
70
  _DefaultAllureStore_defaultLabels.set(this, {});
69
71
  _DefaultAllureStore_environment.set(this, void 0);
@@ -81,7 +83,6 @@ export class DefaultAllureStore {
81
83
  this.indexAttachmentByTestResult = new Map();
82
84
  this.indexAttachmentByFixture = new Map();
83
85
  this.indexFixturesByTestResult = new Map();
84
- this.indexKnownByHistoryId = new Map();
85
86
  _DefaultAllureStore_globalAttachmentIds.set(this, []);
86
87
  _DefaultAllureStore_globalAttachmentIdsByEnv.set(this, new Map());
87
88
  _DefaultAllureStore_globalErrors.set(this, []);
@@ -91,7 +92,7 @@ export class DefaultAllureStore {
91
92
  _DefaultAllureStore_qualityGateResults.set(this, []);
92
93
  _DefaultAllureStore_historyPoints.set(this, []);
93
94
  _DefaultAllureStore_environments.set(this, []);
94
- const { history, known = [], realtimeDispatcher, realtimeSubscriber, defaultLabels = {}, environment, allowedEnvironments, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
95
+ const { history, known = [], knownIssuesConfig, realtimeDispatcher, realtimeSubscriber, defaultLabels = {}, environment, allowedEnvironments, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
95
96
  const errors = [];
96
97
  const { normalized: normalizedEnvironmentsConfig, identities, errors: configErrors, } = normalizeEnvironmentDescriptorMap(environmentsConfig, "environmentsConfig");
97
98
  errors.push(...configErrors);
@@ -116,8 +117,8 @@ export class DefaultAllureStore {
116
117
  __classPrivateFieldSet(this, _DefaultAllureStore_metadata, new Map(), "f");
117
118
  __classPrivateFieldSet(this, _DefaultAllureStore_fixtures, new Map(), "f");
118
119
  __classPrivateFieldSet(this, _DefaultAllureStore_history, history, "f");
119
- __classPrivateFieldSet(this, _DefaultAllureStore_known, [...known], "f");
120
- __classPrivateFieldGet(this, _DefaultAllureStore_known, "f").forEach((ktf) => index(this.indexKnownByHistoryId, ktf.historyId, ktf));
120
+ __classPrivateFieldSet(this, _DefaultAllureStore_knownIssuesConfig, knownIssuesConfig, "f");
121
+ known.forEach((ktf) => this.known.set(ktf.historyId, ktf));
121
122
  __classPrivateFieldSet(this, _DefaultAllureStore_realtimeDispatcher, realtimeDispatcher, "f");
122
123
  __classPrivateFieldSet(this, _DefaultAllureStore_realtimeSubscriber, realtimeSubscriber, "f");
123
124
  __classPrivateFieldSet(this, _DefaultAllureStore_defaultLabels, defaultLabels, "f");
@@ -329,6 +330,7 @@ export class DefaultAllureStore {
329
330
  testResult.transition = getStatusTransition(testResult, trHistory);
330
331
  testResult.flaky = isFlaky(testResult, trHistory);
331
332
  }
333
+ testResult.known = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyKnownIssue).call(this, testResult, environmentIdentity.id);
332
334
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
333
335
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_setTestResultEnvironmentId).call(this, testResult, environmentIdentity.id);
334
336
  __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").recordIngestOrder(testResult.id);
@@ -511,7 +513,7 @@ export class DefaultAllureStore {
511
513
  }, []);
512
514
  }
513
515
  async allKnownIssues() {
514
- return __classPrivateFieldGet(this, _DefaultAllureStore_known, "f");
516
+ return [...this.known.values()].flat();
515
517
  }
516
518
  async allNewTestResults(filter, history) {
517
519
  if (!__classPrivateFieldGet(this, _DefaultAllureStore_history, "f") && !history) {
@@ -580,6 +582,9 @@ export class DefaultAllureStore {
580
582
  const tr = __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").get(trId);
581
583
  return __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_retriesByTr).call(this, tr);
582
584
  }
585
+ resetIngestOrder() {
586
+ __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").resetIngestOrder();
587
+ }
583
588
  async historyByTr(tr) {
584
589
  return __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_historyByTr).call(this, tr);
585
590
  }
@@ -624,16 +629,12 @@ export class DefaultAllureStore {
624
629
  }
625
630
  return failedTrs;
626
631
  }
627
- async unknownFailedTestResults() {
632
+ async blockingFailedTestResults() {
628
633
  const failedTestResults = await this.failedTestResults();
629
- if (!__classPrivateFieldGet(this, _DefaultAllureStore_known, "f")?.length) {
630
- return failedTestResults;
631
- }
632
- const knownHistoryIds = new Set(__classPrivateFieldGet(this, _DefaultAllureStore_known, "f").map((ktf) => ktf.historyId));
633
- return failedTestResults.filter((tr) => {
634
- const historyIdCandidates = getHistoryIdCandidates(tr);
635
- return (historyIdCandidates.length > 0 && historyIdCandidates.every((historyId) => !knownHistoryIds.has(historyId)));
636
- });
634
+ return failedTestResults.filter((tr) => !tr.known);
635
+ }
636
+ async unknownFailedTestResults() {
637
+ return this.blockingFailedTestResults();
637
638
  }
638
639
  async testResultsByLabel(labelName) {
639
640
  const results = createDictionary();
@@ -793,7 +794,7 @@ export class DefaultAllureStore {
793
794
  indexTestResultByTestCase: {},
794
795
  indexAttachmentByFixture: {},
795
796
  indexFixturesByTestResult: {},
796
- indexKnownByHistoryId: {},
797
+ knownIssues: {},
797
798
  qualityGateResults: __classPrivateFieldGet(this, _DefaultAllureStore_qualityGateResults, "f"),
798
799
  testResultIdsIngestOrder: __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").ingestOrderIdsForDump(),
799
800
  };
@@ -812,13 +813,13 @@ export class DefaultAllureStore {
812
813
  this.indexFixturesByTestResult.forEach((fixtures, trId) => {
813
814
  storeDump.indexFixturesByTestResult[trId] = fixtures.map((f) => f.id);
814
815
  });
815
- this.indexKnownByHistoryId.forEach((known, historyId) => {
816
- storeDump.indexKnownByHistoryId[historyId] = known;
816
+ this.known.forEach((known, historyId) => {
817
+ storeDump.knownIssues[historyId] = known;
817
818
  });
818
819
  return storeDump;
819
820
  }
820
821
  async restoreState(stateDump, attachmentsContents = {}) {
821
- const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, indexKnownByHistoryId = {}, qualityGateResults = [], testResultIdsIngestOrder = [], } = stateDump;
822
+ const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, knownIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], } = stateDump;
822
823
  const storedEnvironmentAliases = environments.flatMap((environmentValue) => {
823
824
  if (typeof environmentValue === "string") {
824
825
  return [{ id: environmentValue, name: environmentValue }];
@@ -868,6 +869,7 @@ export class DefaultAllureStore {
868
869
  envNameToId.set(id, id);
869
870
  }
870
871
  Object.values(testResults).forEach((testResult) => {
872
+ testResult.known ?? (testResult.known = false);
871
873
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
872
874
  const storedEnvKey = typeof testResult.environment === "string" ? testResult.environment : undefined;
873
875
  const envId = (storedEnvKey ? envNameToId.get(storedEnvKey) : undefined) ?? __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
@@ -883,6 +885,7 @@ export class DefaultAllureStore {
883
885
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f"), attachments);
884
886
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_testCases, "f"), testCases);
885
887
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_fixtures, "f"), fixtures);
888
+ updateMapWithRecord(this.known, knownIssues);
886
889
  Object.entries(attachmentsContents).forEach(([id, content]) => {
887
890
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_restoreAttachmentContent).call(this, id, content);
888
891
  });
@@ -971,15 +974,10 @@ export class DefaultAllureStore {
971
974
  }
972
975
  existingFixtures.push(...fxs);
973
976
  });
974
- Object.entries(indexKnownByHistoryId).forEach(([historyId, knownFailures]) => {
975
- const existingKnown = this.indexKnownByHistoryId.get(historyId);
976
- if (!existingKnown) {
977
- this.indexKnownByHistoryId.set(historyId, knownFailures);
978
- return;
979
- }
980
- existingKnown.push(...knownFailures);
981
- });
982
977
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_rebuildRetrySubstore).call(this);
978
+ for (const testResult of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()) {
979
+ testResult.known = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyKnownIssue).call(this, testResult, __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult));
980
+ }
983
981
  qualityGateResults.forEach((result, index) => {
984
982
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_assertAllowedStoredEnvironment).call(this, {
985
983
  environment: result.environment,
@@ -991,7 +989,9 @@ export class DefaultAllureStore {
991
989
  });
992
990
  }
993
991
  }
994
- _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environmentDisplayNames = new WeakMap(), _DefaultAllureStore_environmentNameToId = new WeakMap(), _DefaultAllureStore_attachments = new WeakMap(), _DefaultAllureStore_attachmentContents = new WeakMap(), _DefaultAllureStore_testCases = new WeakMap(), _DefaultAllureStore_metadata = new WeakMap(), _DefaultAllureStore_history = new WeakMap(), _DefaultAllureStore_known = new WeakMap(), _DefaultAllureStore_fixtures = new WeakMap(), _DefaultAllureStore_defaultLabels = new WeakMap(), _DefaultAllureStore_environment = new WeakMap(), _DefaultAllureStore_environmentsConfig = new WeakMap(), _DefaultAllureStore_reportVariables = new WeakMap(), _DefaultAllureStore_realtimeDispatcher = new WeakMap(), _DefaultAllureStore_realtimeSubscriber = new WeakMap(), _DefaultAllureStore_allowedEnvironmentIds = new WeakMap(), _DefaultAllureStore_retrySubstore = new WeakMap(), _DefaultAllureStore_testResultIdsByEnvironmentId = new WeakMap(), _DefaultAllureStore_cachedEnvironmentEntries = new WeakMap(), _DefaultAllureStore_globalAttachmentIds = new WeakMap(), _DefaultAllureStore_globalAttachmentIdsByEnv = new WeakMap(), _DefaultAllureStore_globalErrors = new WeakMap(), _DefaultAllureStore_globalErrorsByEnv = new WeakMap(), _DefaultAllureStore_globalExitCode = new WeakMap(), _DefaultAllureStore_checkResultsById = new WeakMap(), _DefaultAllureStore_qualityGateResults = new WeakMap(), _DefaultAllureStore_historyPoints = new WeakMap(), _DefaultAllureStore_environments = new WeakMap(), _DefaultAllureStore_instances = new WeakSet(), _DefaultAllureStore_mergeEnvironmentIdentity = function _DefaultAllureStore_mergeEnvironmentIdentity(existingEnvironment, incomingEnvironment) {
992
+ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environmentDisplayNames = new WeakMap(), _DefaultAllureStore_environmentNameToId = new WeakMap(), _DefaultAllureStore_attachments = new WeakMap(), _DefaultAllureStore_attachmentContents = new WeakMap(), _DefaultAllureStore_testCases = new WeakMap(), _DefaultAllureStore_metadata = new WeakMap(), _DefaultAllureStore_history = new WeakMap(), _DefaultAllureStore_knownIssuesConfig = new WeakMap(), _DefaultAllureStore_fixtures = new WeakMap(), _DefaultAllureStore_defaultLabels = new WeakMap(), _DefaultAllureStore_environment = new WeakMap(), _DefaultAllureStore_environmentsConfig = new WeakMap(), _DefaultAllureStore_reportVariables = new WeakMap(), _DefaultAllureStore_realtimeDispatcher = new WeakMap(), _DefaultAllureStore_realtimeSubscriber = new WeakMap(), _DefaultAllureStore_allowedEnvironmentIds = new WeakMap(), _DefaultAllureStore_retrySubstore = new WeakMap(), _DefaultAllureStore_testResultIdsByEnvironmentId = new WeakMap(), _DefaultAllureStore_cachedEnvironmentEntries = new WeakMap(), _DefaultAllureStore_globalAttachmentIds = new WeakMap(), _DefaultAllureStore_globalAttachmentIdsByEnv = new WeakMap(), _DefaultAllureStore_globalErrors = new WeakMap(), _DefaultAllureStore_globalErrorsByEnv = new WeakMap(), _DefaultAllureStore_globalExitCode = new WeakMap(), _DefaultAllureStore_checkResultsById = new WeakMap(), _DefaultAllureStore_qualityGateResults = new WeakMap(), _DefaultAllureStore_historyPoints = new WeakMap(), _DefaultAllureStore_environments = new WeakMap(), _DefaultAllureStore_instances = new WeakSet(), _DefaultAllureStore_historyIdMatches = function _DefaultAllureStore_historyIdMatches(historyIdCandidates, indexByHistoryId) {
993
+ return historyIdCandidates.find((historyId) => indexByHistoryId.has(historyId));
994
+ }, _DefaultAllureStore_mergeEnvironmentIdentity = function _DefaultAllureStore_mergeEnvironmentIdentity(existingEnvironment, incomingEnvironment) {
995
995
  const configuredEnvironment = environmentIdentityById(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f"), incomingEnvironment.id);
996
996
  if (configuredEnvironment) {
997
997
  return configuredEnvironment;
@@ -1093,6 +1093,23 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environment
1093
1093
  const environmentId = options?.environmentId ?? __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
1094
1094
  const parametersHash = options?.parametersHash ?? calculateParametersHash(testResult.parameters);
1095
1095
  testResult.retryHash = calculateRetryHash(testResult.testCase?.id, parametersHash, environmentId);
1096
+ }, _DefaultAllureStore_setKnownIssue = function _DefaultAllureStore_setKnownIssue(knownIssue) {
1097
+ this.known.set(knownIssue.historyId, knownIssue);
1098
+ }, _DefaultAllureStore_classifyKnownIssue = function _DefaultAllureStore_classifyKnownIssue(testResult, environmentId) {
1099
+ if (testResult.status !== "failed" && testResult.status !== "broken") {
1100
+ return false;
1101
+ }
1102
+ const historyIdCandidates = getHistoryIdCandidates(testResult);
1103
+ if (historyIdCandidates.length === 0) {
1104
+ return false;
1105
+ }
1106
+ const ruleKnownIssue = getKnownIssueByRules(testResult, __classPrivateFieldGet(this, _DefaultAllureStore_knownIssuesConfig, "f"), environmentId);
1107
+ if (ruleKnownIssue) {
1108
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_setKnownIssue).call(this, ruleKnownIssue);
1109
+ return true;
1110
+ }
1111
+ const knownHistoryId = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_historyIdMatches).call(this, historyIdCandidates, this.known);
1112
+ return !!knownHistoryId;
1096
1113
  }, _DefaultAllureStore_rebuildRetrySubstore = function _DefaultAllureStore_rebuildRetrySubstore() {
1097
1114
  __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").reset();
1098
1115
  for (const [, testResult] of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f")) {
@@ -0,0 +1 @@
1
+ export declare const normalizeResultsDir: (value?: string | string[]) => string[];
@@ -0,0 +1,7 @@
1
+ export const normalizeResultsDir = (value) => {
2
+ if (value === undefined) {
3
+ return [];
4
+ }
5
+ const values = Array.isArray(value) ? value : [value];
6
+ return values.filter((entry) => entry.length > 0);
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.14.3",
3
+ "version": "3.16.0",
4
4
  "description": "Collection of generic Allure utilities used across the entire project",
5
5
  "keywords": [
6
6
  "allure"
@@ -25,27 +25,29 @@
25
25
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
26
26
  },
27
27
  "dependencies": {
28
- "@allurereport/ci": "3.14.3",
29
- "@allurereport/core-api": "3.14.3",
30
- "@allurereport/plugin-agent": "3.14.3",
31
- "@allurereport/plugin-allure2": "3.14.3",
32
- "@allurereport/plugin-api": "3.14.3",
33
- "@allurereport/plugin-awesome": "3.14.3",
34
- "@allurereport/plugin-classic": "3.14.3",
35
- "@allurereport/plugin-csv": "3.14.3",
36
- "@allurereport/plugin-dashboard": "3.14.3",
37
- "@allurereport/plugin-jira": "3.14.3",
38
- "@allurereport/plugin-log": "3.14.3",
39
- "@allurereport/plugin-progress": "3.14.3",
40
- "@allurereport/plugin-slack": "3.14.3",
41
- "@allurereport/plugin-testops": "3.14.3",
42
- "@allurereport/plugin-testplan": "3.14.3",
43
- "@allurereport/reader": "3.14.3",
44
- "@allurereport/reader-api": "3.14.3",
45
- "@allurereport/service": "3.14.3",
46
- "@allurereport/summary": "3.14.3",
28
+ "@allurereport/ci": "3.16.0",
29
+ "@allurereport/cli-commons": "3.16.0",
30
+ "@allurereport/core-api": "3.16.0",
31
+ "@allurereport/plugin-agent": "3.16.0",
32
+ "@allurereport/plugin-allure2": "3.16.0",
33
+ "@allurereport/plugin-api": "3.16.0",
34
+ "@allurereport/plugin-awesome": "3.16.0",
35
+ "@allurereport/plugin-classic": "3.16.0",
36
+ "@allurereport/plugin-csv": "3.16.0",
37
+ "@allurereport/plugin-dashboard": "3.16.0",
38
+ "@allurereport/plugin-jira": "3.16.0",
39
+ "@allurereport/plugin-log": "3.16.0",
40
+ "@allurereport/plugin-progress": "3.16.0",
41
+ "@allurereport/plugin-slack": "3.16.0",
42
+ "@allurereport/plugin-testops": "3.16.0",
43
+ "@allurereport/plugin-testplan": "3.16.0",
44
+ "@allurereport/reader": "3.16.0",
45
+ "@allurereport/reader-api": "3.16.0",
46
+ "@allurereport/service": "3.16.0",
47
+ "@allurereport/summary": "3.16.0",
47
48
  "glob": "^13.0.6",
48
49
  "handlebars": "^4.7.9",
50
+ "jiti": "^2.5.1",
49
51
  "node-stream-zip": "^1.15.0",
50
52
  "p-limit": "^7.3.0",
51
53
  "yaml": "^2.8.3",