@allurereport/core 3.14.2 → 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 HistoryDataPoint, type HistoryTestResult, type KnownTestFailure, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
2
- import { type AllureStore, type AllureStoreDump, type ExitCode, type PluginGlobalAttachment, type PluginGlobalError, type QualityGateValidationResult, type RealtimeEventsDispatcher, type RealtimeSubscriber, type ResultFile, type TestResultFilter } from "@allurereport/plugin-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
+ 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;
@@ -33,8 +34,8 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
33
34
  globalExitCode(): Promise<ExitCode | undefined>;
34
35
  allGlobalErrors(): Promise<TestError[]>;
35
36
  allGlobalErrorsByEnv(): Promise<Record<string, PluginGlobalError[]>>;
36
- allGlobalAttachments(): Promise<AttachmentLink[]>;
37
- allGlobalAttachmentsByEnv(): Promise<Record<string, PluginGlobalAttachment[]>>;
37
+ allGlobalAttachments(): Promise<GlobalAttachmentLink[]>;
38
+ allGlobalAttachmentsByEnv(): Promise<Record<string, GlobalAttachmentLink[]>>;
38
39
  visitCheckResult(result: AllureCheckResult): Promise<void>;
39
40
  visitTestResult(raw: RawTestResult, context: ReaderContext): Promise<void>;
40
41
  visitTestFixtureResult(result: RawFixtureResult, context: ReaderContext): Promise<void>;
@@ -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");
@@ -153,13 +154,17 @@ export class DefaultAllureStore {
153
154
  __classPrivateFieldSet(this, _DefaultAllureStore_globalExitCode, exitCode, "f");
154
155
  });
155
156
  __classPrivateFieldGet(this, _DefaultAllureStore_realtimeSubscriber, "f")?.onGlobalError((error) => {
156
- __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addGlobalError).call(this, error);
157
+ const resolvedEnvironment = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_resolveGlobalEnvironmentIdentity).call(this, error.environment);
158
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addGlobalError).call(this, {
159
+ ...error,
160
+ environment: resolvedEnvironment.id,
161
+ });
157
162
  });
158
163
  __classPrivateFieldGet(this, _DefaultAllureStore_realtimeSubscriber, "f")?.onGlobalAttachment(({ attachment, fileName, environment }) => {
159
164
  const originalFileName = attachment.getOriginalFileName();
160
165
  const resolvedEnvironment = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_resolveGlobalEnvironmentIdentity).call(this, environment);
161
166
  const attachmentLink = {
162
- id: __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_globalAttachmentId).call(this, originalFileName, resolvedEnvironment?.id),
167
+ id: __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_globalAttachmentId).call(this, originalFileName, resolvedEnvironment.id),
163
168
  name: fileName || originalFileName,
164
169
  missed: false,
165
170
  used: true,
@@ -167,7 +172,7 @@ export class DefaultAllureStore {
167
172
  contentType: attachment.getContentType(),
168
173
  contentLength: attachment.getContentLength(),
169
174
  originalFileName,
170
- environment: resolvedEnvironment?.name ?? environment,
175
+ environment: resolvedEnvironment.id,
171
176
  };
172
177
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addGlobalAttachment).call(this, attachmentLink, attachment);
173
178
  });
@@ -325,6 +330,7 @@ export class DefaultAllureStore {
325
330
  testResult.transition = getStatusTransition(testResult, trHistory);
326
331
  testResult.flaky = isFlaky(testResult, trHistory);
327
332
  }
333
+ testResult.known = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyKnownIssue).call(this, testResult, environmentIdentity.id);
328
334
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
329
335
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_setTestResultEnvironmentId).call(this, testResult, environmentIdentity.id);
330
336
  __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").recordIngestOrder(testResult.id);
@@ -404,7 +410,7 @@ export class DefaultAllureStore {
404
410
  const linkedAttachment = __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").get(md5(originalFileName));
405
411
  const attachmentContent = __classPrivateFieldGet(this, _DefaultAllureStore_attachmentContents, "f").get(md5(originalFileName));
406
412
  const attachmentLink = {
407
- id: __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_globalAttachmentId).call(this, originalFileName, resolvedEnvironment?.id),
413
+ id: __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_globalAttachmentId).call(this, originalFileName, resolvedEnvironment.id),
408
414
  name: attachment?.name || originalFileName,
409
415
  originalFileName,
410
416
  ext: extname(originalFileName),
@@ -412,7 +418,7 @@ export class DefaultAllureStore {
412
418
  missed: !attachmentContent,
413
419
  contentType: attachment?.contentType ?? linkedAttachment?.contentType,
414
420
  contentLength: linkedAttachment?.contentLength,
415
- environment: resolvedEnvironment?.name ?? attachment.environment,
421
+ environment: resolvedEnvironment.name,
416
422
  };
417
423
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addGlobalAttachment).call(this, attachmentLink, attachmentContent);
418
424
  });
@@ -507,7 +513,7 @@ export class DefaultAllureStore {
507
513
  }, []);
508
514
  }
509
515
  async allKnownIssues() {
510
- return __classPrivateFieldGet(this, _DefaultAllureStore_known, "f");
516
+ return [...this.known.values()].flat();
511
517
  }
512
518
  async allNewTestResults(filter, history) {
513
519
  if (!__classPrivateFieldGet(this, _DefaultAllureStore_history, "f") && !history) {
@@ -576,6 +582,9 @@ export class DefaultAllureStore {
576
582
  const tr = __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").get(trId);
577
583
  return __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_retriesByTr).call(this, tr);
578
584
  }
585
+ resetIngestOrder() {
586
+ __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").resetIngestOrder();
587
+ }
579
588
  async historyByTr(tr) {
580
589
  return __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_historyByTr).call(this, tr);
581
590
  }
@@ -620,16 +629,12 @@ export class DefaultAllureStore {
620
629
  }
621
630
  return failedTrs;
622
631
  }
623
- async unknownFailedTestResults() {
632
+ async blockingFailedTestResults() {
624
633
  const failedTestResults = await this.failedTestResults();
625
- if (!__classPrivateFieldGet(this, _DefaultAllureStore_known, "f")?.length) {
626
- return failedTestResults;
627
- }
628
- const knownHistoryIds = new Set(__classPrivateFieldGet(this, _DefaultAllureStore_known, "f").map((ktf) => ktf.historyId));
629
- return failedTestResults.filter((tr) => {
630
- const historyIdCandidates = getHistoryIdCandidates(tr);
631
- return (historyIdCandidates.length > 0 && historyIdCandidates.every((historyId) => !knownHistoryIds.has(historyId)));
632
- });
634
+ return failedTestResults.filter((tr) => !tr.known);
635
+ }
636
+ async unknownFailedTestResults() {
637
+ return this.blockingFailedTestResults();
633
638
  }
634
639
  async testResultsByLabel(labelName) {
635
640
  const results = createDictionary();
@@ -789,7 +794,7 @@ export class DefaultAllureStore {
789
794
  indexTestResultByTestCase: {},
790
795
  indexAttachmentByFixture: {},
791
796
  indexFixturesByTestResult: {},
792
- indexKnownByHistoryId: {},
797
+ knownIssues: {},
793
798
  qualityGateResults: __classPrivateFieldGet(this, _DefaultAllureStore_qualityGateResults, "f"),
794
799
  testResultIdsIngestOrder: __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").ingestOrderIdsForDump(),
795
800
  };
@@ -808,13 +813,13 @@ export class DefaultAllureStore {
808
813
  this.indexFixturesByTestResult.forEach((fixtures, trId) => {
809
814
  storeDump.indexFixturesByTestResult[trId] = fixtures.map((f) => f.id);
810
815
  });
811
- this.indexKnownByHistoryId.forEach((known, historyId) => {
812
- storeDump.indexKnownByHistoryId[historyId] = known;
816
+ this.known.forEach((known, historyId) => {
817
+ storeDump.knownIssues[historyId] = known;
813
818
  });
814
819
  return storeDump;
815
820
  }
816
821
  async restoreState(stateDump, attachmentsContents = {}) {
817
- 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;
818
823
  const storedEnvironmentAliases = environments.flatMap((environmentValue) => {
819
824
  if (typeof environmentValue === "string") {
820
825
  return [{ id: environmentValue, name: environmentValue }];
@@ -864,6 +869,7 @@ export class DefaultAllureStore {
864
869
  envNameToId.set(id, id);
865
870
  }
866
871
  Object.values(testResults).forEach((testResult) => {
872
+ testResult.known ?? (testResult.known = false);
867
873
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
868
874
  const storedEnvKey = typeof testResult.environment === "string" ? testResult.environment : undefined;
869
875
  const envId = (storedEnvKey ? envNameToId.get(storedEnvKey) : undefined) ?? __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
@@ -879,6 +885,7 @@ export class DefaultAllureStore {
879
885
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f"), attachments);
880
886
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_testCases, "f"), testCases);
881
887
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_fixtures, "f"), fixtures);
888
+ updateMapWithRecord(this.known, knownIssues);
882
889
  Object.entries(attachmentsContents).forEach(([id, content]) => {
883
890
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_restoreAttachmentContent).call(this, id, content);
884
891
  });
@@ -967,15 +974,10 @@ export class DefaultAllureStore {
967
974
  }
968
975
  existingFixtures.push(...fxs);
969
976
  });
970
- Object.entries(indexKnownByHistoryId).forEach(([historyId, knownFailures]) => {
971
- const existingKnown = this.indexKnownByHistoryId.get(historyId);
972
- if (!existingKnown) {
973
- this.indexKnownByHistoryId.set(historyId, knownFailures);
974
- return;
975
- }
976
- existingKnown.push(...knownFailures);
977
- });
978
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
+ }
979
981
  qualityGateResults.forEach((result, index) => {
980
982
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_assertAllowedStoredEnvironment).call(this, {
981
983
  environment: result.environment,
@@ -987,7 +989,9 @@ export class DefaultAllureStore {
987
989
  });
988
990
  }
989
991
  }
990
- _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) {
991
995
  const configuredEnvironment = environmentIdentityById(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f"), incomingEnvironment.id);
992
996
  if (configuredEnvironment) {
993
997
  return configuredEnvironment;
@@ -1089,6 +1093,23 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environment
1089
1093
  const environmentId = options?.environmentId ?? __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
1090
1094
  const parametersHash = options?.parametersHash ?? calculateParametersHash(testResult.parameters);
1091
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;
1092
1113
  }, _DefaultAllureStore_rebuildRetrySubstore = function _DefaultAllureStore_rebuildRetrySubstore() {
1093
1114
  __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").reset();
1094
1115
  for (const [, testResult] of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f")) {
@@ -1128,9 +1149,6 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environment
1128
1149
  __classPrivateFieldGet(this, _DefaultAllureStore_globalErrors, "f").push(__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_indexGlobalError).call(this, error));
1129
1150
  }, _DefaultAllureStore_indexGlobalAttachment = function _DefaultAllureStore_indexGlobalAttachment(attachmentLink) {
1130
1151
  const resolvedEnvironment = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_resolveGlobalEnvironmentIdentity).call(this, attachmentLink.environment);
1131
- if (!resolvedEnvironment) {
1132
- return attachmentLink;
1133
- }
1134
1152
  attachmentLink.environment = resolvedEnvironment.name;
1135
1153
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addEnvironments).call(this, [resolvedEnvironment]);
1136
1154
  index(__classPrivateFieldGet(this, _DefaultAllureStore_globalAttachmentIdsByEnv, "f"), resolvedEnvironment.id, attachmentLink.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.14.2",
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,31 +25,33 @@
25
25
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
26
26
  },
27
27
  "dependencies": {
28
- "@allurereport/ci": "3.14.2",
29
- "@allurereport/core-api": "3.14.2",
30
- "@allurereport/plugin-agent": "3.14.2",
31
- "@allurereport/plugin-allure2": "3.14.2",
32
- "@allurereport/plugin-api": "3.14.2",
33
- "@allurereport/plugin-awesome": "3.14.2",
34
- "@allurereport/plugin-classic": "3.14.2",
35
- "@allurereport/plugin-csv": "3.14.2",
36
- "@allurereport/plugin-dashboard": "3.14.2",
37
- "@allurereport/plugin-jira": "3.14.2",
38
- "@allurereport/plugin-log": "3.14.2",
39
- "@allurereport/plugin-progress": "3.14.2",
40
- "@allurereport/plugin-slack": "3.14.2",
41
- "@allurereport/plugin-testops": "3.14.2",
42
- "@allurereport/plugin-testplan": "3.14.2",
43
- "@allurereport/reader": "3.14.2",
44
- "@allurereport/reader-api": "3.14.2",
45
- "@allurereport/service": "3.14.2",
46
- "@allurereport/summary": "3.14.2",
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",
52
- "yoctocolors": "^2.1.1",
54
+ "yoctocolors": "^2.1.2",
53
55
  "zip-stream": "^7.0.5"
54
56
  },
55
57
  "devDependencies": {