@allurereport/core 3.14.3 → 3.15.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,7 +7,7 @@ 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">>;
10
+ type FullConfigRequiredFromConfig = Required<Pick<Config, "name" | "output" | "open">>;
11
11
  export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "allureService" | "knownIssuesPath" | "plugins" | "port">, FullConfigRequiredFromConfig {
12
12
  port: Config["port"] | undefined;
13
13
  allowedEnvironments?: Config["allowedEnvironments"];
@@ -15,6 +15,7 @@ export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "a
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;
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,8 +2,9 @@ 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";
@@ -13,6 +14,9 @@ const CONFIG_FILENAMES = [
13
14
  "allurerc.js",
14
15
  "allurerc.mjs",
15
16
  "allurerc.cjs",
17
+ "allurerc.ts",
18
+ "allurerc.mts",
19
+ "allurerc.cts",
16
20
  "allurerc.json",
17
21
  "allurerc.yaml",
18
22
  "allurerc.yml",
@@ -80,7 +84,7 @@ export const findConfig = async (cwd, configPath) => {
80
84
  return resolved;
81
85
  }
82
86
  }
83
- catch (ignored) {
87
+ catch {
84
88
  }
85
89
  }
86
90
  };
@@ -94,6 +98,7 @@ export const validateConfig = (config) => {
94
98
  "historyPath",
95
99
  "historyLimit",
96
100
  "knownIssuesPath",
101
+ "knownIssues",
97
102
  "plugins",
98
103
  "defaultLabels",
99
104
  "variables",
@@ -141,6 +146,10 @@ export const loadJsonConfig = async (configPath) => {
141
146
  export const loadJsConfig = async (configPath) => {
142
147
  return (await import(normalizeImportPath(configPath))).default;
143
148
  };
149
+ export const loadTsConfig = async (configPath) => {
150
+ const jiti = createJiti(import.meta.url);
151
+ return await jiti.import(resolve(configPath), { default: true });
152
+ };
144
153
  const resolveConfigEnvironments = (config) => {
145
154
  const errors = [];
146
155
  const { ids: allowedEnvironments, idsSet: allowedEnvironmentIds, errors: allowedEnvironmentErrors, } = validateAllowedEnvironmentIds(config.allowedEnvironments, "config.allowedEnvironments");
@@ -192,9 +201,11 @@ export const resolveConfig = async (config, override = {}) => {
192
201
  const historyPath = override.historyPath ?? config.historyPath;
193
202
  const historyLimit = override.historyLimit ?? config.historyLimit;
194
203
  const appendHistory = config.appendHistory ?? true;
195
- const knownIssuesPath = resolve(override.knownIssuesPath ?? config.knownIssuesPath ?? "./allure/known.json");
204
+ const configuredKnownIssuesPath = override.knownIssuesPath ?? config.knownIssuesPath;
205
+ const knownIssuesPathInput = configuredKnownIssuesPath ?? (hasKnownIssueRules(config.knownIssues) ? DEFAULT_KNOWN_ISSUES_PATH : undefined);
206
+ const knownIssuesPath = await resolveExactIssuesFilePath(knownIssuesPathInput, "known issues");
196
207
  const output = resolve(override.output ?? config.output ?? "./allure-report");
197
- const known = await readKnownIssues(knownIssuesPath);
208
+ const known = knownIssuesPath ? await readKnownIssues(knownIssuesPath) : undefined;
198
209
  const variables = config.variables ?? {};
199
210
  let pluginInstances = [];
200
211
  const hasPluginsOverride = override.plugins !== undefined;
@@ -225,6 +236,7 @@ export const resolveConfig = async (config, override = {}) => {
225
236
  hideLabels,
226
237
  knownIssuesPath,
227
238
  known,
239
+ knownIssues: config.knownIssues,
228
240
  environment,
229
241
  allowedEnvironments,
230
242
  variables,
@@ -265,6 +277,11 @@ export const readConfig = async (cwd = process.cwd(), configPath, override) => {
265
277
  case ".mjs":
266
278
  config = await loadJsConfig(cfg);
267
279
  break;
280
+ case ".ts":
281
+ case ".cts":
282
+ case ".mts":
283
+ config = await loadTsConfig(cfg);
284
+ break;
268
285
  default:
269
286
  config = DEFAULT_CONFIG;
270
287
  }
@@ -283,6 +300,10 @@ export const readRawConfig = async (cwd = process.cwd(), configPath) => {
283
300
  case ".cjs":
284
301
  case ".mjs":
285
302
  return loadJsConfig(cfg);
303
+ case ".ts":
304
+ case ".cts":
305
+ case ".mts":
306
+ return loadTsConfig(cfg);
286
307
  default:
287
308
  return DEFAULT_CONFIG;
288
309
  }
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,20 @@ 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
+ });
135
145
  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
- };
146
+ const logUploadProgress = progressLogger.log;
147
+ const incrementUploadProgress = progressLogger.increment;
154
148
  try {
155
149
  logUploadProgress(true);
156
150
  await client.createReport({
@@ -172,6 +166,14 @@ export class AllureReport {
172
166
  linksByPluginId[report.pluginId] = uploadResult.indexHref;
173
167
  }
174
168
  }
169
+ if (__classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f")) {
170
+ publishErrorMessage = "Test results registry upload has failed, the report won't be published";
171
+ await client.uploadReport({
172
+ reportUuid: this.reportUuid,
173
+ files: { [TEST_RESULTS_REGISTRY_FILENAME]: __classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") },
174
+ onProgress: incrementUploadProgress,
175
+ });
176
+ }
175
177
  const changedPluginIds = __classPrivateFieldGet(this, _AllureReport_applyPublishLinksToSummaries, "f").call(this, linksByPluginId);
176
178
  summariesMutated = changedPluginIds.size > 0;
177
179
  if (changedPluginIds.size > 0) {
@@ -225,6 +227,7 @@ export class AllureReport {
225
227
  __classPrivateFieldGet(this, _AllureReport_logPublishError, "f").call(this, publishErrorMessage, err);
226
228
  }
227
229
  finally {
230
+ progressLogger.cancel?.();
228
231
  endPublishPerfSpan();
229
232
  }
230
233
  });
@@ -238,7 +241,7 @@ export class AllureReport {
238
241
  }
239
242
  try {
240
243
  const entries = (await readdir(resultsDirPath, { withFileTypes: true }))
241
- .filter((dirent) => dirent.isFile())
244
+ .filter((dirent) => dirent.isFile() && !dirent.name.endsWith(".tmp"))
242
245
  .sort((a, b) => a.name.localeCompare(b.name));
243
246
  const limit = pLimit(readConcurrency());
244
247
  await Promise.all(entries.map((dirent) => limit(async () => {
@@ -280,6 +283,11 @@ export class AllureReport {
280
283
  };
281
284
  this.validate = async (params) => {
282
285
  const { trs, knownIssues, state, environment } = params;
286
+ const currentKnownIssues = await __classPrivateFieldGet(this, _AllureReport_store, "f").allKnownIssues();
287
+ const effectiveKnownIssues = new Map();
288
+ [...knownIssues, ...currentKnownIssues].forEach((issue) => {
289
+ effectiveKnownIssues.set(issue.historyId, issue);
290
+ });
283
291
  const qualityGateEnvironment = environment === undefined
284
292
  ? undefined
285
293
  : (environmentIdentityById(__classPrivateFieldGet(this, _AllureReport_environments, "f"), environment)?.name ??
@@ -287,7 +295,7 @@ export class AllureReport {
287
295
  environment);
288
296
  return __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").validate({
289
297
  trs: trs.filter(Boolean),
290
- knownIssues,
298
+ knownIssues: [...effectiveKnownIssues.values()],
291
299
  state,
292
300
  environment: qualityGateEnvironment,
293
301
  });
@@ -384,7 +392,7 @@ export class AllureReport {
384
392
  const addJsonDumpEntry = async (entryName, value) => {
385
393
  await addRequiredDumpEntry(Buffer.from(JSON.stringify(value)), entryName);
386
394
  };
387
- const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, indexKnownByHistoryId = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
395
+ const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, knownIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
388
396
  [AllureStoreDumpFiles.TestResults, testResults],
389
397
  [AllureStoreDumpFiles.TestCases, testCases],
390
398
  [AllureStoreDumpFiles.Fixtures, fixtures],
@@ -392,6 +400,7 @@ export class AllureReport {
392
400
  [AllureStoreDumpFiles.CheckResults, checkResults],
393
401
  [AllureStoreDumpFiles.Environments, environments],
394
402
  [AllureStoreDumpFiles.ReportVariables, reportVariables],
403
+ [AllureStoreDumpFiles.KnownIssues, knownIssues],
395
404
  [AllureStoreDumpFiles.GlobalAttachments, globalAttachmentIds],
396
405
  [AllureStoreDumpFiles.GlobalErrors, globalErrors],
397
406
  [AllureStoreDumpFiles.IndexAttachmentsByTestResults, indexAttachmentByTestResult],
@@ -399,7 +408,6 @@ export class AllureReport {
399
408
  [AllureStoreDumpFiles.IndexTestResultsByTestCase, indexTestResultByTestCase],
400
409
  [AllureStoreDumpFiles.IndexAttachmentsByFixture, indexAttachmentByFixture],
401
410
  [AllureStoreDumpFiles.IndexFixturesByTestResult, indexFixturesByTestResult],
402
- [AllureStoreDumpFiles.IndexKnownByHistoryId, indexKnownByHistoryId],
403
411
  [AllureStoreDumpFiles.QualityGateResults, qualityGateResults],
404
412
  [AllureStoreDumpFiles.TestResultIngestOrder, testResultIdsIngestOrder],
405
413
  ];
@@ -467,7 +475,11 @@ export class AllureReport {
467
475
  }
468
476
  await rename(dumpTempPath, dumpPath);
469
477
  };
470
- this.restoreState = async (dumps) => measurePerf(PERF_METRIC_NAMES.restoreStateTotal, async () => {
478
+ this.restoreState = async (dumps) => {
479
+ __classPrivateFieldGet(this, _AllureReport_store, "f").resetIngestOrder();
480
+ await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, dumps);
481
+ };
482
+ _AllureReport_restoreStateDumps.set(this, async (dumps) => measurePerf(PERF_METRIC_NAMES.restoreStateTotal, async () => {
471
483
  for (const dump of dumps) {
472
484
  await measurePerf(PERF_METRIC_NAMES.restoreStateDump, async () => {
473
485
  if (!existsSync(dump)) {
@@ -504,7 +516,7 @@ export class AllureReport {
504
516
  await writeFile(nestedDumpPath, await dumpArchive.entryData(entryName));
505
517
  nestedDumpPaths.push(nestedDumpPath);
506
518
  }
507
- await this.restoreState(nestedDumpPaths);
519
+ await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, nestedDumpPaths);
508
520
  return;
509
521
  }
510
522
  }
@@ -522,7 +534,7 @@ export class AllureReport {
522
534
  const indexTestResultsByTestCaseEntry = await requiredEntryData(AllureStoreDumpFiles.IndexTestResultsByTestCase);
523
535
  const indexAttachmentsByFixtureEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByFixture);
524
536
  const indexFixturesByTestResultEntry = await requiredEntryData(AllureStoreDumpFiles.IndexFixturesByTestResult);
525
- const indexKnownByHistoryIdEntry = await requiredEntryData(AllureStoreDumpFiles.IndexKnownByHistoryId);
537
+ const knownIssuesEntry = await requiredEntryData(AllureStoreDumpFiles.KnownIssues);
526
538
  const qualityGateResultsEntry = await requiredEntryData(AllureStoreDumpFiles.QualityGateResults);
527
539
  const testResultIngestOrderEntry = await optionalEntryData(AllureStoreDumpFiles.TestResultIngestOrder);
528
540
  const attachmentsLinks = JSON.parse(attachmentsEntry.toString("utf8"));
@@ -535,6 +547,7 @@ export class AllureReport {
535
547
  case AllureStoreDumpFiles.Fixtures:
536
548
  case AllureStoreDumpFiles.Environments:
537
549
  case AllureStoreDumpFiles.ReportVariables:
550
+ case AllureStoreDumpFiles.KnownIssues:
538
551
  case AllureStoreDumpFiles.GlobalAttachments:
539
552
  case AllureStoreDumpFiles.GlobalErrors:
540
553
  case AllureStoreDumpFiles.IndexAttachmentsByTestResults:
@@ -542,7 +555,6 @@ export class AllureReport {
542
555
  case AllureStoreDumpFiles.IndexTestResultsByTestCase:
543
556
  case AllureStoreDumpFiles.IndexAttachmentsByFixture:
544
557
  case AllureStoreDumpFiles.IndexFixturesByTestResult:
545
- case AllureStoreDumpFiles.IndexKnownByHistoryId:
546
558
  case AllureStoreDumpFiles.QualityGateResults:
547
559
  case AllureStoreDumpFiles.TestResultIngestOrder:
548
560
  return acc;
@@ -570,7 +582,7 @@ export class AllureReport {
570
582
  indexTestResultByTestCase: JSON.parse(indexTestResultsByTestCaseEntry.toString("utf8")),
571
583
  indexAttachmentByFixture: JSON.parse(indexAttachmentsByFixtureEntry.toString("utf8")),
572
584
  indexFixturesByTestResult: JSON.parse(indexFixturesByTestResultEntry.toString("utf8")),
573
- indexKnownByHistoryId: JSON.parse(indexKnownByHistoryIdEntry.toString("utf8")),
585
+ knownIssues: JSON.parse(knownIssuesEntry.toString("utf8")),
574
586
  qualityGateResults: JSON.parse(qualityGateResultsEntry.toString("utf8")),
575
587
  testResultIdsIngestOrder: testResultIngestOrderEntry
576
588
  ? JSON.parse(testResultIngestOrderEntry.toString("utf8"))
@@ -624,7 +636,7 @@ export class AllureReport {
624
636
  }
625
637
  });
626
638
  }
627
- });
639
+ }));
628
640
  _AllureReport_getReportsToPublish.set(this, async () => {
629
641
  const reports = [];
630
642
  for (const { enabled, id, options } of __classPrivateFieldGet(this, _AllureReport_plugins, "f")) {
@@ -691,6 +703,11 @@ export class AllureReport {
691
703
  await context.reportFiles.addFile("summary.json", Buffer.from(JSON.stringify(summary)));
692
704
  });
693
705
  });
706
+ _AllureReport_writeTestResultRegistry.set(this, async () => {
707
+ const testResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults({ includeRetries: false });
708
+ const registry = createTestResultRegistry(testResults);
709
+ __classPrivateFieldSet(this, _AllureReport_testResultsRegistryPath, await __classPrivateFieldGet(this, _AllureReport_reportFiles, "f").addFile(TEST_RESULTS_REGISTRY_FILENAME, Buffer.from(JSON.stringify(registry))), "f");
710
+ });
694
711
  _AllureReport_generateRootSummary.set(this, async () => {
695
712
  const summaries = [...__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").values()].map(clonePluginSummary);
696
713
  if (summaries.length > 1) {
@@ -751,16 +768,21 @@ export class AllureReport {
751
768
  outputDirFiles = await readdir(__classPrivateFieldGet(this, _AllureReport_output, "f"));
752
769
  }
753
770
  catch { }
771
+ if (__classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f")) {
772
+ await writeKnownIssues(__classPrivateFieldGet(this, _AllureReport_store, "f"), __classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f"));
773
+ }
754
774
  if (outputDirFiles.length === 0) {
755
775
  return;
756
776
  }
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) {
777
+ const outputEntries = await Promise.all(outputDirFiles.map(async (file) => ({ file, stats: await lstat(join(__classPrivateFieldGet(this, _AllureReport_output, "f"), file)) })));
778
+ const outputDirectoryEntries = outputEntries.filter(({ stats }) => stats.isDirectory());
779
+ if (outputDirectoryEntries.length === 1) {
780
+ const reportPath = join(__classPrivateFieldGet(this, _AllureReport_output, "f"), outputDirectoryEntries[0].file);
762
781
  const reportContent = await readdir(reportPath);
763
782
  for (const entry of reportContent) {
783
+ if (entry === TEST_RESULTS_REGISTRY_FILENAME) {
784
+ continue;
785
+ }
764
786
  const currentFilePath = join(reportPath, entry);
765
787
  const newFilePath = resolve(dirname(currentFilePath), "..", entry);
766
788
  await rename(currentFilePath, newFilePath);
@@ -773,7 +795,7 @@ export class AllureReport {
773
795
  }
774
796
  catch { }
775
797
  }
776
- if (__classPrivateFieldGet(this, _AllureReport_history, "f")) {
798
+ if (__classPrivateFieldGet(this, _AllureReport_history, "f") && __classPrivateFieldGet(this, _AllureReport_appendHistory, "f")) {
777
799
  try {
778
800
  await __classPrivateFieldGet(this, _AllureReport_store, "f").appendHistory(__classPrivateFieldGet(this, _AllureReport_historyDataPoint, "f"));
779
801
  }
@@ -870,7 +892,7 @@ export class AllureReport {
870
892
  }
871
893
  }
872
894
  });
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;
895
+ 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
896
  const allureServiceAccessToken = allureService?.accessToken;
875
897
  if (allureServiceAccessToken) {
876
898
  const allureServiceClientConfig = {
@@ -894,6 +916,8 @@ export class AllureReport {
894
916
  __classPrivateFieldSet(this, _AllureReport_hideLabels, hideLabels, "f");
895
917
  __classPrivateFieldSet(this, _AllureReport_environments, environments ?? {}, "f");
896
918
  __classPrivateFieldSet(this, _AllureReport_globalAttachments, globalAttachments, "f");
919
+ __classPrivateFieldSet(this, _AllureReport_appendHistory, appendHistory ?? true, "f");
920
+ __classPrivateFieldSet(this, _AllureReport_knownIssuesPath, knownIssuesPath, "f");
897
921
  if (qualityGate) {
898
922
  __classPrivateFieldSet(this, _AllureReport_qualityGate, new QualityGate(qualityGate), "f");
899
923
  }
@@ -918,6 +942,7 @@ export class AllureReport {
918
942
  environmentsConfig: environments,
919
943
  history: __classPrivateFieldGet(this, _AllureReport_history, "f"),
920
944
  known,
945
+ knownIssuesConfig: knownIssues,
921
946
  defaultLabels,
922
947
  environment,
923
948
  allowedEnvironments,
@@ -940,6 +965,6 @@ export class AllureReport {
940
965
  return __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").dispatcher;
941
966
  }
942
967
  }
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) {
968
+ _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
969
  return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
945
970
  };
@@ -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")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.14.3",
3
+ "version": "3.15.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.15.0",
29
+ "@allurereport/cli-commons": "3.15.0",
30
+ "@allurereport/core-api": "3.15.0",
31
+ "@allurereport/plugin-agent": "3.15.0",
32
+ "@allurereport/plugin-allure2": "3.15.0",
33
+ "@allurereport/plugin-api": "3.15.0",
34
+ "@allurereport/plugin-awesome": "3.15.0",
35
+ "@allurereport/plugin-classic": "3.15.0",
36
+ "@allurereport/plugin-csv": "3.15.0",
37
+ "@allurereport/plugin-dashboard": "3.15.0",
38
+ "@allurereport/plugin-jira": "3.15.0",
39
+ "@allurereport/plugin-log": "3.15.0",
40
+ "@allurereport/plugin-progress": "3.15.0",
41
+ "@allurereport/plugin-slack": "3.15.0",
42
+ "@allurereport/plugin-testops": "3.15.0",
43
+ "@allurereport/plugin-testplan": "3.15.0",
44
+ "@allurereport/reader": "3.15.0",
45
+ "@allurereport/reader-api": "3.15.0",
46
+ "@allurereport/service": "3.15.0",
47
+ "@allurereport/summary": "3.15.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",