@allurereport/core 3.16.1 → 3.17.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
@@ -1,4 +1,4 @@
1
- import type { CategoriesConfig, ResolvedAllureServiceConfig } from "@allurereport/core-api";
1
+ import type { CategoriesConfig, PerformanceConfig, ResolvedAllureServiceConfig } from "@allurereport/core-api";
2
2
  import type { Plugin, ReportFiles, Config } from "@allurereport/plugin-api";
3
3
  import type { ResultsReader } from "@allurereport/reader-api";
4
4
  export interface PluginInstance {
@@ -16,9 +16,11 @@ export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "a
16
16
  plugins?: PluginInstance[];
17
17
  realTime?: any;
18
18
  qualityGate?: Config["qualityGate"];
19
+ performance?: PerformanceConfig;
19
20
  allureService?: ResolvedAllureServiceConfig;
20
21
  categories?: CategoriesConfig;
21
22
  globalAttachments?: string[];
23
+ cwd?: string;
22
24
  resultsDir?: string[];
23
25
  }
24
26
  export {};
package/dist/config.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { Config, Plugin, PluginConstructorContext, PluginDescriptor } from
2
2
  import type { FullConfig, PluginInstance } from "./api.js";
3
3
  type PluginConstructor = new (options?: Record<string, any>, context?: PluginConstructorContext) => Plugin;
4
4
  export interface ConfigOverride {
5
+ cwd?: string;
5
6
  name?: Config["name"];
6
7
  output?: Config["output"];
7
8
  open?: Config["open"];
package/dist/config.js CHANGED
@@ -107,6 +107,7 @@ export const validateConfig = (config) => {
107
107
  "environments",
108
108
  "appendHistory",
109
109
  "qualityGate",
110
+ "performance",
110
111
  "allureService",
111
112
  "categories",
112
113
  "globalAttachments",
@@ -210,6 +211,7 @@ export const resolveConfig = async (config, override = {}) => {
210
211
  ? { ...(config.resolutions ?? { rules: [] }), knownIssuesPath }
211
212
  : undefined;
212
213
  const output = resolve(override.output ?? config.output ?? "./allure-report");
214
+ const cwd = resolve(override.cwd ?? process.cwd());
213
215
  const variables = config.variables ?? {};
214
216
  const resultsDir = normalizeResultsDir(config.resultsDir);
215
217
  let pluginInstances = [];
@@ -235,6 +237,7 @@ export const resolveConfig = async (config, override = {}) => {
235
237
  }
236
238
  return {
237
239
  name,
240
+ cwd,
238
241
  output,
239
242
  open,
240
243
  port,
@@ -251,6 +254,7 @@ export const resolveConfig = async (config, override = {}) => {
251
254
  plugins: pluginInstances,
252
255
  defaultLabels: config.defaultLabels ?? {},
253
256
  qualityGate: config.qualityGate,
257
+ performance: config.performance,
254
258
  allureService: config.allureService
255
259
  ? {
256
260
  accessToken: config.allureService.accessToken,
@@ -289,7 +293,7 @@ export const readConfig = async (cwd = process.cwd(), configPath, override) => {
289
293
  default:
290
294
  config = DEFAULT_CONFIG;
291
295
  }
292
- const fullConfig = await resolveConfig(config, override);
296
+ const fullConfig = await resolveConfig(config, { ...override, cwd });
293
297
  return fullConfig;
294
298
  };
295
299
  export const readRawConfig = async (cwd = process.cwd(), configPath) => {
package/dist/history.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type AllureHistory, type HistoryDataPoint, type TestCase, type TestResult } from "@allurereport/core-api";
2
- export declare const createHistory: (reportUuid: string, reportName: string | undefined, testCases: TestCase[], testResults: TestResult[], remoteUrl?: string) => HistoryDataPoint;
1
+ import { type AllureHistory, type HistoryDataPoint, type MetricSample, type TestCase, type TestResult } from "@allurereport/core-api";
2
+ export declare const createHistory: (reportUuid: string, reportName: string | undefined, testCases: TestCase[], testResults: TestResult[], remoteUrl?: string, metrics?: MetricSample[]) => HistoryDataPoint;
3
3
  export declare class AllureLocalHistory implements AllureHistory {
4
4
  #private;
5
5
  private readonly params;
package/dist/history.js CHANGED
@@ -43,7 +43,17 @@ const createHistoryItems = (testResults, remoteUrl) => {
43
43
  return acc;
44
44
  }, {});
45
45
  };
46
- export const createHistory = (reportUuid, reportName = "Allure Report", testCases, testResults, remoteUrl = "") => {
46
+ const metricsToHistoryValues = (metrics) => {
47
+ const grouped = new Map();
48
+ for (const metric of metrics) {
49
+ if (!metric.key || !Number.isFinite(metric.value)) {
50
+ continue;
51
+ }
52
+ grouped.set(metric.key, [...(grouped.get(metric.key) ?? []), metric.value]);
53
+ }
54
+ return Object.fromEntries([...grouped.entries()].map(([key, values]) => [key, values.reduce((acc, value) => acc + value, 0) / values.length]));
55
+ };
56
+ export const createHistory = (reportUuid, reportName = "Allure Report", testCases, testResults, remoteUrl = "", metrics = []) => {
47
57
  const knownTestCaseIds = testCases.map((tc) => tc.id);
48
58
  return {
49
59
  uuid: reportUuid,
@@ -51,7 +61,7 @@ export const createHistory = (reportUuid, reportName = "Allure Report", testCase
51
61
  timestamp: new Date().getTime(),
52
62
  knownTestCaseIds,
53
63
  testResults: createHistoryItems(testResults, remoteUrl),
54
- metrics: {},
64
+ metrics: metricsToHistoryValues(metrics),
55
65
  url: remoteUrl,
56
66
  };
57
67
  };
package/dist/index.d.ts CHANGED
@@ -11,4 +11,4 @@ export * from "./resolutions.js";
11
11
  export { resolveConfig, readConfig, readRawConfig, getPluginInstance } from "./config.js";
12
12
  export * from "./report.js";
13
13
  export * from "./plugin.js";
14
- export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
14
+ export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, metricMaxRule, metricMinRule, metricMaxDeltaRule, metricMaxDeltaPercentRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
package/dist/index.js CHANGED
@@ -10,4 +10,4 @@ export * from "./resolutions.js";
10
10
  export { resolveConfig, readConfig, readRawConfig, getPluginInstance } from "./config.js";
11
11
  export * from "./report.js";
12
12
  export * from "./plugin.js";
13
- export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
13
+ export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, metricMaxRule, metricMinRule, metricMaxDeltaRule, metricMaxDeltaPercentRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
@@ -1,11 +1,11 @@
1
- import { type TestError, type TestResult } from "@allurereport/core-api";
2
- import type { QualityGateConfig, QualityGateValidationResult } from "@allurereport/plugin-api";
1
+ import { type MetricSample, type TestError, type TestResult } from "@allurereport/core-api";
2
+ import type { QualityGateConfig, QualityGateMetricHistoryPoint, QualityGateValidationResult } from "@allurereport/plugin-api";
3
3
  export declare const stringifyQualityGateResults: (results: QualityGateValidationResult[]) => string;
4
4
  export declare const convertQualityGateResultsToTestErrors: (results: QualityGateValidationResult[]) => TestError[];
5
5
  export declare class QualityGateState {
6
6
  #private;
7
- setResult(rule: string, value: any, testResults?: string[]): void;
8
- getResult(rule: string): any;
7
+ setResult(rule: string, value: unknown, testResults?: string[]): void;
8
+ getResult(rule: string): unknown;
9
9
  getTestResults(rule: string): string[];
10
10
  markTestResultsProcessed(testResults: TestResult[]): void;
11
11
  isTestResultProcessed(trId: string): boolean;
@@ -16,6 +16,8 @@ export declare class QualityGate {
16
16
  validate(payload: {
17
17
  state?: QualityGateState;
18
18
  trs: TestResult[];
19
+ metrics?: MetricSample[];
20
+ previousHistory?: QualityGateMetricHistoryPoint[];
19
21
  environment?: string;
20
22
  }): Promise<{
21
23
  fastFailed: boolean;
@@ -59,7 +59,7 @@ export class QualityGate {
59
59
  this.config = config;
60
60
  }
61
61
  async validate(payload) {
62
- const { state, trs, environment } = payload;
62
+ const { state, trs, metrics = [], previousHistory = [], environment } = payload;
63
63
  const trsToValidateById = new Map();
64
64
  for (const tr of trs) {
65
65
  if (tr.isRetry || isIgnoredFailure(tr) || state?.isTestResultProcessed(tr.id)) {
@@ -98,6 +98,8 @@ export class QualityGate {
98
98
  setResult: (value, testResults) => state?.setResult?.(ruleId, value, testResults),
99
99
  },
100
100
  expected,
101
+ metrics,
102
+ previousHistory,
101
103
  environment,
102
104
  });
103
105
  if (result.success) {
@@ -1,14 +1,19 @@
1
1
  import { type QualityGateRule } from "@allurereport/plugin-api";
2
+ type MetricRuleConfig = {
3
+ key: string;
4
+ value: number;
5
+ title?: string;
6
+ unit?: string;
7
+ };
2
8
  export declare const maxFailuresRule: QualityGateRule<number>;
3
9
  export declare const minTestsCountRule: QualityGateRule<number>;
4
- export declare const successRateRule: QualityGateRule<number, {
5
- totalCount: number;
6
- passedCount: number;
7
- }>;
10
+ export declare const successRateRule: QualityGateRule<number>;
8
11
  export declare const maxDurationRule: QualityGateRule<number>;
9
12
  export declare const allTestsContainEnvRule: QualityGateRule<string, number>;
10
13
  export declare const environmentsTestedRule: QualityGateRule<string[]>;
11
- export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<number, {
12
- totalCount: number;
13
- passedCount: number;
14
- }> | QualityGateRule<string, number> | QualityGateRule<string[]>)[];
14
+ export declare const metricMaxRule: QualityGateRule<MetricRuleConfig>;
15
+ export declare const metricMinRule: QualityGateRule<MetricRuleConfig>;
16
+ export declare const metricMaxDeltaRule: QualityGateRule<MetricRuleConfig>;
17
+ export declare const metricMaxDeltaPercentRule: QualityGateRule<MetricRuleConfig>;
18
+ export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<string, number> | QualityGateRule<string[]> | QualityGateRule<MetricRuleConfig>)[];
19
+ export {};
@@ -1,10 +1,80 @@
1
1
  import { filterSuccessful, filterUnsuccessful } from "@allurereport/core-api";
2
2
  import { bold } from "yoctocolors";
3
+ const numberStateValue = (value) => (Number.isFinite(value) ? Number(value) : 0);
4
+ const stringArrayStateValue = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
5
+ const successRateStateValue = (value) => {
6
+ if (typeof value !== "object" || value === null) {
7
+ return { totalCount: 0, passedCount: 0 };
8
+ }
9
+ const state = value;
10
+ return {
11
+ totalCount: numberStateValue(state.totalCount),
12
+ passedCount: numberStateValue(state.passedCount),
13
+ };
14
+ };
15
+ const metricValues = (metrics, key) => metrics
16
+ .map((metric) => (metric.key === key ? metric.value : undefined))
17
+ .filter((value) => Number.isFinite(value));
18
+ const metricAverage = (metrics, key) => {
19
+ const values = metricValues(metrics, key);
20
+ if (values.length === 0) {
21
+ return undefined;
22
+ }
23
+ return values.reduce((acc, value) => acc + value, 0) / values.length;
24
+ };
25
+ const previousMetricValue = (history, key) => {
26
+ const previousHistory = [...history].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
27
+ for (const historyPoint of previousHistory) {
28
+ const value = historyPoint.metrics?.[key];
29
+ if (Number.isFinite(value)) {
30
+ return value;
31
+ }
32
+ }
33
+ return undefined;
34
+ };
35
+ const metricActual = (metrics, key, value) => {
36
+ const sample = metrics.find((metric) => metric.key === key);
37
+ return {
38
+ key,
39
+ value,
40
+ ...(sample?.title ? { title: sample.title } : {}),
41
+ ...(sample?.unit ? { unit: sample.unit } : {}),
42
+ };
43
+ };
44
+ const formatMetricValue = (value, unit) => {
45
+ if (!Number.isFinite(value)) {
46
+ return "n/a";
47
+ }
48
+ const finiteValue = value;
49
+ const formatted = Number.isInteger(finiteValue) ? String(finiteValue) : String(Number(finiteValue.toFixed(3)));
50
+ return unit ? `${formatted} ${unit}` : formatted;
51
+ };
52
+ const metricRuleTitle = (actual) => actual.title ?? actual.key;
53
+ const metricRuleUnit = (actual, expected) => actual.unit ?? expected.unit;
54
+ const missingMetricResult = (actual) => ({
55
+ success: false,
56
+ actual,
57
+ testResults: [],
58
+ });
59
+ const metricRuleResult = (success, actual) => ({
60
+ success,
61
+ actual,
62
+ testResults: [],
63
+ });
64
+ const metricDeltaWithinThreshold = (delta, threshold, better) => {
65
+ if (better === "lower") {
66
+ return delta <= threshold;
67
+ }
68
+ if (better === "higher") {
69
+ return -delta <= threshold;
70
+ }
71
+ return Math.abs(delta) <= threshold;
72
+ };
3
73
  export const maxFailuresRule = {
4
74
  rule: "maxFailures",
5
75
  message: ({ actual, expected }) => `The number of failed tests ${bold(String(actual))} exceeds the allowed threshold value ${bold(String(expected))}`,
6
76
  validate: async ({ trs, expected, state }) => {
7
- const previous = state.getResult() ?? 0;
77
+ const previous = numberStateValue(state.getResult());
8
78
  const failedTrs = trs.filter(filterUnsuccessful);
9
79
  const testResults = failedTrs.map((tr) => tr.id);
10
80
  const actual = previous + failedTrs.length;
@@ -20,7 +90,7 @@ export const minTestsCountRule = {
20
90
  rule: "minTestsCount",
21
91
  message: ({ actual, expected }) => `The total number of tests ${bold(String(actual))} is less than the expected threshold value ${bold(String(expected))}`,
22
92
  validate: async ({ trs, expected, state }) => {
23
- const actual = trs.length + (state.getResult() ?? 0);
93
+ const actual = trs.length + numberStateValue(state.getResult());
24
94
  state.setResult(actual, []);
25
95
  return {
26
96
  success: actual >= expected,
@@ -33,7 +103,7 @@ export const successRateRule = {
33
103
  rule: "successRate",
34
104
  message: ({ actual, expected }) => `Success rate ${bold(String(actual))} is less, than expected ${bold(String(expected))}`,
35
105
  validate: async ({ trs, expected, state }) => {
36
- const previous = state.getResult() ?? { totalCount: 0, passedCount: 0 };
106
+ const previous = successRateStateValue(state.getResult());
37
107
  const passedTrs = trs.filter(filterSuccessful);
38
108
  const notPassedTrs = trs.filter((tr) => !filterSuccessful(tr));
39
109
  const totalCount = previous.totalCount + trs.length;
@@ -52,7 +122,7 @@ export const maxDurationRule = {
52
122
  rule: "maxDuration",
53
123
  message: ({ actual, expected }) => `Maximum duration of some tests exceed the defined limit; actual ${bold(String(actual))}, expected ${bold(String(expected))}`,
54
124
  validate: async ({ trs, expected, state }) => {
55
- const previous = state.getResult() ?? 0;
125
+ const previous = numberStateValue(state.getResult());
56
126
  const actual = Math.max(previous, ...trs.map((tr) => tr.duration ?? 0));
57
127
  const tooLongTrs = trs.filter((tr) => (tr.duration ?? 0) > expected);
58
128
  const testResults = tooLongTrs.map((tr) => tr.id);
@@ -66,9 +136,9 @@ export const maxDurationRule = {
66
136
  };
67
137
  export const allTestsContainEnvRule = {
68
138
  rule: "allTestsContainEnv",
69
- message: ({ actual, expected }) => `Not all tests contain the required "${bold(expected)}" environment, ${bold(actual)} tests have different or missing environment`,
139
+ message: ({ actual, expected }) => `Not all tests contain the required "${bold(expected)}" environment, ${bold(String(actual))} tests have different or missing environment`,
70
140
  validate: async ({ trs, expected, state }) => {
71
- const previous = state.getResult() ?? 0;
141
+ const previous = numberStateValue(state.getResult());
72
142
  const testsWithoutEnv = trs.filter((tr) => (tr.environment ?? "") !== expected);
73
143
  const testResults = testsWithoutEnv.map((tr) => tr.id);
74
144
  const actual = testsWithoutEnv.length + previous;
@@ -84,7 +154,7 @@ export const environmentsTestedRule = {
84
154
  rule: "environmentsTested",
85
155
  message: ({ actual, expected }) => `The following environments were not tested: "${actual.join('", "')}"; expected all of: "${expected.join('", "')}"`,
86
156
  validate: async ({ trs, expected, state }) => {
87
- const previouslyTested = new Set(state.getResult() ?? []);
157
+ const previouslyTested = new Set(stringArrayStateValue(state.getResult()));
88
158
  const batchTested = trs.map((tr) => tr.environment).filter((env) => env != null && env !== "");
89
159
  const testedEnvs = new Set([...previouslyTested, ...batchTested]);
90
160
  state.setResult([...testedEnvs], []);
@@ -96,6 +166,62 @@ export const environmentsTestedRule = {
96
166
  };
97
167
  },
98
168
  };
169
+ export const metricMaxRule = {
170
+ rule: "metricMax",
171
+ message: ({ actual, expected }) => `${bold(metricRuleTitle(actual))} ${bold(formatMetricValue(actual.value, metricRuleUnit(actual, expected)))} exceeds the allowed maximum ${bold(formatMetricValue(expected.value, metricRuleUnit(actual, expected)))}`,
172
+ validate: async ({ metrics = [], expected }) => {
173
+ const value = metricAverage(metrics, expected.key);
174
+ const actual = metricActual(metrics, expected.key, value ?? Number.NaN);
175
+ return value === undefined ? missingMetricResult(actual) : metricRuleResult(value <= expected.value, actual);
176
+ },
177
+ };
178
+ export const metricMinRule = {
179
+ rule: "metricMin",
180
+ message: ({ actual, expected }) => `${bold(metricRuleTitle(actual))} ${bold(formatMetricValue(actual.value, metricRuleUnit(actual, expected)))} is below the required minimum ${bold(formatMetricValue(expected.value, metricRuleUnit(actual, expected)))}`,
181
+ validate: async ({ metrics = [], expected }) => {
182
+ const value = metricAverage(metrics, expected.key);
183
+ const actual = metricActual(metrics, expected.key, value ?? Number.NaN);
184
+ return value === undefined ? missingMetricResult(actual) : metricRuleResult(value >= expected.value, actual);
185
+ },
186
+ };
187
+ export const metricMaxDeltaRule = {
188
+ rule: "metricMaxDelta",
189
+ message: ({ actual, expected }) => `${bold(metricRuleTitle(actual))} changed by ${bold(formatMetricValue(actual.value, metricRuleUnit(actual, expected)))}, which exceeds ${bold(formatMetricValue(expected.value, metricRuleUnit(actual, expected)))}`,
190
+ validate: async ({ metrics = [], previousHistory = [], expected }) => {
191
+ const current = metricAverage(metrics, expected.key);
192
+ if (current === undefined) {
193
+ return missingMetricResult(metricActual(metrics, expected.key, Number.NaN));
194
+ }
195
+ const previous = previousMetricValue(previousHistory, expected.key);
196
+ if (previous === undefined) {
197
+ return metricRuleResult(true, metricActual(metrics, expected.key, 0));
198
+ }
199
+ const value = current - previous;
200
+ const actual = metricActual(metrics, expected.key, value);
201
+ const better = metrics.find((metric) => metric.key === expected.key)?.better;
202
+ return metricRuleResult(metricDeltaWithinThreshold(value, expected.value, better), actual);
203
+ },
204
+ };
205
+ export const metricMaxDeltaPercentRule = {
206
+ rule: "metricMaxDeltaPercent",
207
+ message: ({ actual, expected }) => `${bold(metricRuleTitle(actual))} changed by ${bold(formatMetricValue(actual.value, "%"))}, which exceeds ${bold(formatMetricValue(expected.value, "%"))}`,
208
+ validate: async ({ metrics = [], previousHistory = [], expected }) => {
209
+ const current = metricAverage(metrics, expected.key);
210
+ if (current === undefined) {
211
+ return missingMetricResult(metricActual(metrics, expected.key, Number.NaN));
212
+ }
213
+ const previous = previousMetricValue(previousHistory, expected.key);
214
+ if (previous === undefined) {
215
+ return metricRuleResult(true, metricActual(metrics, expected.key, 0));
216
+ }
217
+ const value = previous !== 0 ? ((current - previous) / Math.abs(previous)) * 100 : Number.NaN;
218
+ const actual = metricActual(metrics, expected.key, value);
219
+ const better = metrics.find((metric) => metric.key === expected.key)?.better;
220
+ return Number.isFinite(value)
221
+ ? metricRuleResult(metricDeltaWithinThreshold(value, expected.value, better), actual)
222
+ : missingMetricResult(actual);
223
+ },
224
+ };
99
225
  export const qualityGateDefaultRules = [
100
226
  maxFailuresRule,
101
227
  minTestsCountRule,
@@ -103,4 +229,8 @@ export const qualityGateDefaultRules = [
103
229
  maxDurationRule,
104
230
  allTestsContainEnvRule,
105
231
  environmentsTestedRule,
232
+ metricMaxRule,
233
+ metricMinRule,
234
+ metricMaxDeltaRule,
235
+ metricMaxDeltaPercentRule,
106
236
  ];
package/dist/report.js CHANGED
@@ -9,14 +9,14 @@ 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_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;
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_cwd, _AllureReport_state, _AllureReport_executionStage, _AllureReport_historyDataPoint, _AllureReport_summaryPath, _AllureReport_testResultsRegistryPath, _AllureReport_summariesByPluginId, _AllureReport_publishedRemoteHrefs, _AllureReport_artifactFilesByPath, _AllureReport_published, _AllureReport_endGeneratePerfSpan, _AllureReport_createHistoryDataPoint, _AllureReport_ingestSelfPerfMetrics, _AllureReport_publish, _AllureReport_getCwd, _AllureReport_runRealtimeUpdate, _AllureReport_recordArtifact, _AllureReport_restoreStateDumps, _AllureReport_getReportsToPublish, _AllureReport_cleanupFailedRemoteReport, _AllureReport_logPublishError, _AllureReport_applyPublishLinksToSummaries, _AllureReport_cloneSummariesByPluginId, _AllureReport_writeSummaryFiles, _AllureReport_writeTestResultRegistry, _AllureReport_getRootRemoteReportFiles, _AllureReport_writeQualityGateFiles, _AllureReport_finishArtifactsManifest, _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";
16
16
  import { createReadStream, createWriteStream, existsSync, readFileSync } from "node:fs";
17
17
  import { lstat, mkdtemp, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
18
18
  import { tmpdir } from "node:os";
19
- import { basename, dirname, join, resolve, sep } from "node:path";
19
+ import { basename, dirname, join, resolve } from "node:path";
20
20
  import { promisify } from "node:util";
21
21
  import { detect } from "@allurereport/ci";
22
22
  import { createProgressLogger } from "@allurereport/cli-commons";
@@ -35,8 +35,9 @@ import { DefaultPluginState, PluginFiles } from "./plugin.js";
35
35
  import { QualityGate } from "./qualityGate/index.js";
36
36
  import { writeKnownIssues } from "./resolutions.js";
37
37
  import { DefaultAllureStore } from "./store/store.js";
38
+ import { ARTIFACTS_MANIFEST_FILENAME, createReportArtifact, deduplicateDumpInputs, } from "./utils/artifacts.js";
38
39
  import { environmentIdentityById, environmentIdentityByName } from "./utils/environment.js";
39
- import { measurePerf, PERF_METRIC_NAMES, PERF_METRIC_PREFIXES, startPerfSpan, writePerfMetrics } from "./utils/perf.js";
40
+ import { getPerfMetricsResults, isPerfMetricsEnabled, measurePerf, PERF_METRIC_NAMES, PERF_METRIC_PREFIXES, perfMetricsFileName, startPerfSpan, writePerfMetrics, } from "./utils/perf.js";
40
41
  import { RealtimeChannel } from "./utils/realtimeChannel.js";
41
42
  import { RealtimeUpdateScheduler } from "./utils/realtimeUpdateScheduler.js";
42
43
  import { resolveDumpAttachmentPath, UnsafeDumpPathError } from "./utils/safeDumpPath.js";
@@ -45,6 +46,12 @@ const INIT_REQUIRED_ERROR_MESSAGE = "report is not initialised. Call the start()
45
46
  const DEFAULT_READ_CONCURRENCY = 64;
46
47
  const MAX_READ_CONCURRENCY = 256;
47
48
  const TEST_RESULTS_REGISTRY_FILENAME = "test-results.json";
49
+ const QUALITY_GATE_RESULTS_FILENAME = "quality-gate.json";
50
+ const ROOT_INTEGRATION_FILENAMES = new Set([
51
+ TEST_RESULTS_REGISTRY_FILENAME,
52
+ QUALITY_GATE_RESULTS_FILENAME,
53
+ ARTIFACTS_MANIFEST_FILENAME,
54
+ ]);
48
55
  const readConcurrency = () => {
49
56
  const parsed = Number.parseInt(process.env.ALLURE_READ_CONCURRENCY ?? "", 10);
50
57
  if (!Number.isFinite(parsed)) {
@@ -92,6 +99,7 @@ export class AllureReport {
92
99
  _AllureReport_globalAttachments.set(this, void 0);
93
100
  _AllureReport_knownIssuesPath.set(this, void 0);
94
101
  _AllureReport_dumpTempDirs.set(this, []);
102
+ _AllureReport_cwd.set(this, void 0);
95
103
  _AllureReport_state.set(this, void 0);
96
104
  _AllureReport_executionStage.set(this, "init");
97
105
  _AllureReport_historyDataPoint.set(this, void 0);
@@ -99,8 +107,28 @@ export class AllureReport {
99
107
  _AllureReport_testResultsRegistryPath.set(this, void 0);
100
108
  _AllureReport_summariesByPluginId.set(this, new Map());
101
109
  _AllureReport_publishedRemoteHrefs.set(this, new Set());
110
+ _AllureReport_artifactFilesByPath.set(this, new Map());
102
111
  _AllureReport_published.set(this, false);
103
112
  _AllureReport_endGeneratePerfSpan.set(this, void 0);
113
+ _AllureReport_createHistoryDataPoint.set(this, async () => {
114
+ const allTrs = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults();
115
+ const allTcs = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestCases();
116
+ return createHistory(this.reportUuid, this.reportName, allTcs, allTrs, this.reportUrl, await __classPrivateFieldGet(this, _AllureReport_store, "f").allMetrics());
117
+ });
118
+ _AllureReport_ingestSelfPerfMetrics.set(this, async () => {
119
+ if (!isPerfMetricsEnabled()) {
120
+ return;
121
+ }
122
+ const results = getPerfMetricsResults();
123
+ if (results.length === 0) {
124
+ return;
125
+ }
126
+ const originalFileName = perfMetricsFileName(this.reportUuid);
127
+ await __classPrivateFieldGet(this, _AllureReport_store, "f").visitMetrics(results, {
128
+ readerId: "api",
129
+ metadata: { originalFileName },
130
+ });
131
+ });
104
132
  _AllureReport_publish.set(this, async () => {
105
133
  if (__classPrivateFieldGet(this, _AllureReport_published, "f")) {
106
134
  return;
@@ -110,12 +138,13 @@ export class AllureReport {
110
138
  }
111
139
  let historyPoint = __classPrivateFieldGet(this, _AllureReport_historyDataPoint, "f");
112
140
  if (!historyPoint) {
113
- const allTrs = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults();
114
- const allTcs = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestCases();
115
- historyPoint = createHistory(this.reportUuid, this.reportName, allTcs, allTrs, this.reportUrl);
141
+ historyPoint = await __classPrivateFieldGet(this, _AllureReport_createHistoryDataPoint, "f").call(this);
116
142
  __classPrivateFieldSet(this, _AllureReport_historyDataPoint, historyPoint, "f");
117
143
  }
118
144
  await __classPrivateFieldGet(this, _AllureReport_writeTestResultRegistry, "f").call(this);
145
+ if (__classPrivateFieldGet(this, _AllureReport_qualityGate, "f")) {
146
+ await __classPrivateFieldGet(this, _AllureReport_writeQualityGateFiles, "f").call(this);
147
+ }
119
148
  await __classPrivateFieldGet(this, _AllureReport_writeSummaryFiles, "f").call(this);
120
149
  await __classPrivateFieldGet(this, _AllureReport_generateRootSummary, "f").call(this);
121
150
  if (__classPrivateFieldGet(this, _AllureReport_realTime, "f") || !__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")) {
@@ -131,8 +160,9 @@ export class AllureReport {
131
160
  const linksByPluginId = {};
132
161
  const summariesSnapshot = __classPrivateFieldGet(this, _AllureReport_cloneSummariesByPluginId, "f").call(this);
133
162
  const uploadProgressMessage = reportsToPublish.length === 1 ? `Publishing "${reportsToPublish[0].pluginId}" report` : "Publishing reports";
163
+ const rootRemoteReportFiles = __classPrivateFieldGet(this, _AllureReport_getRootRemoteReportFiles, "f").call(this);
134
164
  const totalFilesToUpload = reportsToPublish.reduce((acc, report) => acc + Object.keys(report.files).length, 0) +
135
- (__classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") ? 1 : 0);
165
+ Object.keys(rootRemoteReportFiles).length;
136
166
  let summariesMutated = false;
137
167
  let reportCreated = false;
138
168
  let publishErrorMessage = "Report upload has failed, the report won't be published";
@@ -167,11 +197,11 @@ export class AllureReport {
167
197
  linksByPluginId[report.pluginId] = uploadResult.indexHref;
168
198
  }
169
199
  }
170
- if (__classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f")) {
200
+ if (Object.keys(rootRemoteReportFiles).length > 0) {
171
201
  publishErrorMessage = "Test results registry upload has failed, the report won't be published";
172
202
  await client.uploadReport({
173
203
  reportUuid: this.reportUuid,
174
- files: { [TEST_RESULTS_REGISTRY_FILENAME]: __classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") },
204
+ files: rootRemoteReportFiles,
175
205
  onProgress: incrementUploadProgress,
176
206
  });
177
207
  }
@@ -292,9 +322,15 @@ export class AllureReport {
292
322
  return __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").validate({
293
323
  trs: trs.filter(Boolean),
294
324
  state,
325
+ metrics: await __classPrivateFieldGet(this, _AllureReport_store, "f").allMetrics(),
326
+ previousHistory: (await __classPrivateFieldGet(this, _AllureReport_store, "f").allHistoryDataPoints()).filter(({ uuid }) => uuid !== this.reportUuid),
295
327
  environment: qualityGateEnvironment,
296
328
  });
297
329
  };
330
+ _AllureReport_getCwd.set(this, () => {
331
+ __classPrivateFieldSet(this, _AllureReport_cwd, __classPrivateFieldGet(this, _AllureReport_cwd, "f") ?? resolve(process.cwd()), "f");
332
+ return __classPrivateFieldGet(this, _AllureReport_cwd, "f");
333
+ });
298
334
  this.start = async () => {
299
335
  await __classPrivateFieldGet(this, _AllureReport_store, "f").readHistory();
300
336
  if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") === "running") {
@@ -305,22 +341,17 @@ export class AllureReport {
305
341
  }
306
342
  __classPrivateFieldSet(this, _AllureReport_executionStage, "running", "f");
307
343
  __classPrivateFieldSet(this, _AllureReport_endGeneratePerfSpan, startPerfSpan(PERF_METRIC_NAMES.generateTotal), "f");
308
- const cwd = resolve(process.cwd());
309
- const cwdWithSep = cwd.endsWith(sep) ? cwd : `${cwd}${sep}`;
310
344
  if (__classPrivateFieldGet(this, _AllureReport_globalAttachments, "f")?.length) {
345
+ const cwd = __classPrivateFieldGet(this, _AllureReport_getCwd, "f").call(this);
311
346
  const matchedFiles = new Set();
312
347
  for (const pattern of __classPrivateFieldGet(this, _AllureReport_globalAttachments, "f")) {
313
348
  const files = await glob(pattern, { cwd, nodir: true, absolute: true });
314
- files.forEach((filePath) => matchedFiles.add(filePath));
349
+ files.forEach((filePath) => matchedFiles.add(resolve(cwd, filePath)));
315
350
  }
316
- for (const filePath of matchedFiles) {
317
- const absoluteFilePath = resolve(filePath);
318
- const isInsideCwd = absoluteFilePath === cwd || absoluteFilePath.startsWith(cwdWithSep);
319
- if (!isInsideCwd) {
320
- continue;
321
- }
351
+ for (const absoluteFilePath of matchedFiles) {
322
352
  const originalFileName = basename(absoluteFilePath);
323
353
  __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").dispatcher.sendGlobalAttachment(new PathResultFile(absoluteFilePath, originalFileName), originalFileName);
354
+ __classPrivateFieldGet(this, _AllureReport_recordArtifact, "f").call(this, absoluteFilePath, originalFileName);
324
355
  }
325
356
  }
326
357
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, true, async (plugin, context) => {
@@ -387,7 +418,7 @@ export class AllureReport {
387
418
  const addJsonDumpEntry = async (entryName, value) => {
388
419
  await addRequiredDumpEntry(Buffer.from(JSON.stringify(value)), entryName);
389
420
  };
390
- const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
421
+ const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], metrics = [], }) => [
391
422
  [AllureStoreDumpFiles.TestResults, testResults],
392
423
  [AllureStoreDumpFiles.TestCases, testCases],
393
424
  [AllureStoreDumpFiles.Fixtures, fixtures],
@@ -406,6 +437,7 @@ export class AllureReport {
406
437
  [AllureStoreDumpFiles.IndexFixturesByTestResult, indexFixturesByTestResult],
407
438
  [AllureStoreDumpFiles.QualityGateResults, qualityGateResults],
408
439
  [AllureStoreDumpFiles.TestResultIngestOrder, testResultIdsIngestOrder],
440
+ [AllureStoreDumpFiles.Metrics, metrics],
409
441
  ];
410
442
  let dumpError;
411
443
  dumpArchive.pipe(dumpArchiveWriteStream);
@@ -473,10 +505,18 @@ export class AllureReport {
473
505
  };
474
506
  this.restoreState = async (dumps) => {
475
507
  __classPrivateFieldGet(this, _AllureReport_store, "f").resetIngestOrder();
476
- await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, dumps);
508
+ await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, dumps.map((path) => ({ artifactPath: path, path })));
477
509
  };
510
+ _AllureReport_recordArtifact.set(this, (filePath, name = basename(filePath)) => {
511
+ const artifact = createReportArtifact(filePath, __classPrivateFieldGet(this, _AllureReport_getCwd, "f").call(this), name);
512
+ if (!__classPrivateFieldGet(this, _AllureReport_artifactFilesByPath, "f").has(artifact.path)) {
513
+ __classPrivateFieldGet(this, _AllureReport_artifactFilesByPath, "f").set(artifact.path, artifact);
514
+ }
515
+ });
478
516
  _AllureReport_restoreStateDumps.set(this, async (dumps) => measurePerf(PERF_METRIC_NAMES.restoreStateTotal, async () => {
479
- for (const dump of dumps) {
517
+ let restoredCount = 0;
518
+ for (const { artifactPath, path: dump, recordArtifact = true } of deduplicateDumpInputs(dumps, __classPrivateFieldGet(this, _AllureReport_getCwd, "f").call(this))) {
519
+ const artifactFilePath = artifactPath ?? dump;
480
520
  await measurePerf(PERF_METRIC_NAMES.restoreStateDump, async () => {
481
521
  if (!existsSync(dump)) {
482
522
  console.error(`Failed to restore state from "${dump}", continuing without it`);
@@ -505,14 +545,24 @@ export class AllureReport {
505
545
  entryName.toLowerCase().endsWith(".zip"));
506
546
  if (nestedDumpEntries.length > 0) {
507
547
  const nestedDumpsTempDir = await mkdtemp(join(tmpdir(), `${basename(dump, ".zip")}-nested-`));
508
- const nestedDumpPaths = [];
548
+ const nestedDumps = [];
509
549
  __classPrivateFieldGet(this, _AllureReport_dumpTempDirs, "f").push(nestedDumpsTempDir);
510
550
  for (const [entryName] of nestedDumpEntries) {
511
- const nestedDumpPath = join(nestedDumpsTempDir, `${nestedDumpPaths.length}-${basename(entryName)}`);
551
+ const nestedDumpPath = join(nestedDumpsTempDir, `${nestedDumps.length}-${basename(entryName)}`);
512
552
  await writeFile(nestedDumpPath, await dumpArchive.entryData(entryName));
513
- nestedDumpPaths.push(nestedDumpPath);
553
+ nestedDumps.push({
554
+ artifactPath: artifactFilePath,
555
+ path: nestedDumpPath,
556
+ recordArtifact: false,
557
+ });
558
+ }
559
+ const nestedRestoredCount = await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, nestedDumps);
560
+ if (nestedRestoredCount > 0) {
561
+ restoredCount += 1;
562
+ if (recordArtifact) {
563
+ __classPrivateFieldGet(this, _AllureReport_recordArtifact, "f").call(this, artifactFilePath);
564
+ }
514
565
  }
515
- await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, nestedDumpPaths);
516
566
  return;
517
567
  }
518
568
  }
@@ -534,6 +584,7 @@ export class AllureReport {
534
584
  const resolutionIssuesEntry = await optionalEntryData(AllureStoreDumpFiles.ResolutionIssues);
535
585
  const qualityGateResultsEntry = await requiredEntryData(AllureStoreDumpFiles.QualityGateResults);
536
586
  const testResultIngestOrderEntry = await optionalEntryData(AllureStoreDumpFiles.TestResultIngestOrder);
587
+ const metricsEntry = await optionalEntryData(AllureStoreDumpFiles.Metrics);
537
588
  const attachmentsLinks = JSON.parse(attachmentsEntry.toString("utf8"));
538
589
  const attachmentsEntries = dumpEntriesList.reduce((acc, [entryName, entry]) => {
539
590
  switch (entryName) {
@@ -555,6 +606,7 @@ export class AllureReport {
555
606
  case AllureStoreDumpFiles.IndexFixturesByTestResult:
556
607
  case AllureStoreDumpFiles.QualityGateResults:
557
608
  case AllureStoreDumpFiles.TestResultIngestOrder:
609
+ case AllureStoreDumpFiles.Metrics:
558
610
  return acc;
559
611
  default:
560
612
  if (entry.isDirectory || !attachmentsLinks[entryName] || attachmentsLinks[entryName].missed) {
@@ -588,6 +640,7 @@ export class AllureReport {
588
640
  testResultIdsIngestOrder: testResultIngestOrderEntry
589
641
  ? JSON.parse(testResultIngestOrderEntry.toString("utf8"))
590
642
  : [],
643
+ metrics: metricsEntry ? JSON.parse(metricsEntry.toString("utf8")) : [],
591
644
  };
592
645
  const dumpTempDir = await mkdtemp(join(tmpdir(), basename(dump, ".zip")));
593
646
  const resultsAttachments = {};
@@ -614,6 +667,10 @@ export class AllureReport {
614
667
  });
615
668
  await measurePerf(PERF_METRIC_NAMES.restoreStateStoreRestore, async () => __classPrivateFieldGet(this, _AllureReport_store, "f").restoreState(dumpState, resultsAttachments));
616
669
  console.info(`Successfully restored state from "${dump}"`);
670
+ restoredCount += 1;
671
+ if (recordArtifact) {
672
+ __classPrivateFieldGet(this, _AllureReport_recordArtifact, "f").call(this, artifactFilePath);
673
+ }
617
674
  }
618
675
  catch (err) {
619
676
  restoreError = err;
@@ -637,6 +694,7 @@ export class AllureReport {
637
694
  }
638
695
  });
639
696
  }
697
+ return restoredCount;
640
698
  }));
641
699
  _AllureReport_getReportsToPublish.set(this, async () => {
642
700
  const reports = [];
@@ -709,6 +767,25 @@ export class AllureReport {
709
767
  const registry = createTestResultRegistry(testResults);
710
768
  __classPrivateFieldSet(this, _AllureReport_testResultsRegistryPath, await __classPrivateFieldGet(this, _AllureReport_reportFiles, "f").addFile(TEST_RESULTS_REGISTRY_FILENAME, Buffer.from(JSON.stringify(registry))), "f");
711
769
  });
770
+ _AllureReport_getRootRemoteReportFiles.set(this, () => ({
771
+ ...(__classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") ? { [TEST_RESULTS_REGISTRY_FILENAME]: __classPrivateFieldGet(this, _AllureReport_testResultsRegistryPath, "f") } : {}),
772
+ }));
773
+ _AllureReport_writeQualityGateFiles.set(this, async () => {
774
+ const qualityGateResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").qualityGateResults();
775
+ await __classPrivateFieldGet(this, _AllureReport_reportFiles, "f").addFile(QUALITY_GATE_RESULTS_FILENAME, Buffer.from(JSON.stringify(qualityGateResults)));
776
+ });
777
+ _AllureReport_finishArtifactsManifest.set(this, async () => {
778
+ if (__classPrivateFieldGet(this, _AllureReport_artifactFilesByPath, "f").size === 0) {
779
+ return;
780
+ }
781
+ const manifest = [...__classPrivateFieldGet(this, _AllureReport_artifactFilesByPath, "f").values()];
782
+ try {
783
+ await writeFile(join(__classPrivateFieldGet(this, _AllureReport_output, "f"), ARTIFACTS_MANIFEST_FILENAME), JSON.stringify(manifest));
784
+ }
785
+ catch (err) {
786
+ console.error("Failed to write Allure artifacts manifest", err);
787
+ }
788
+ });
712
789
  _AllureReport_generateRootSummary.set(this, async () => {
713
790
  const summaries = [...__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").values()].map(clonePluginSummary);
714
791
  if (summaries.length > 1) {
@@ -724,9 +801,6 @@ export class AllureReport {
724
801
  throw new Error(INIT_REQUIRED_ERROR_MESSAGE);
725
802
  }
726
803
  try {
727
- const testResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults();
728
- const testCases = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestCases();
729
- __classPrivateFieldSet(this, _AllureReport_historyDataPoint, createHistory(this.reportUuid, this.reportName, testCases, testResults, this.reportUrl), "f");
730
804
  __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").close();
731
805
  try {
732
806
  await __classPrivateFieldGet(this, _AllureReport_realtimeUpdateScheduler, "f").close();
@@ -747,6 +821,8 @@ export class AllureReport {
747
821
  });
748
822
  });
749
823
  __classPrivateFieldGet(this, _AllureReport_finishGeneratePerfSpan, "f").call(this);
824
+ await __classPrivateFieldGet(this, _AllureReport_ingestSelfPerfMetrics, "f").call(this);
825
+ __classPrivateFieldSet(this, _AllureReport_historyDataPoint, await __classPrivateFieldGet(this, _AllureReport_createHistoryDataPoint, "f").call(this), "f");
750
826
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
751
827
  const summary = await plugin?.info?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
752
828
  if (!summary) {
@@ -781,7 +857,7 @@ export class AllureReport {
781
857
  const reportPath = join(__classPrivateFieldGet(this, _AllureReport_output, "f"), outputDirectoryEntries[0].file);
782
858
  const reportContent = await readdir(reportPath);
783
859
  for (const entry of reportContent) {
784
- if (entry === TEST_RESULTS_REGISTRY_FILENAME) {
860
+ if (ROOT_INTEGRATION_FILENAMES.has(entry)) {
785
861
  continue;
786
862
  }
787
863
  const currentFilePath = join(reportPath, entry);
@@ -790,6 +866,7 @@ export class AllureReport {
790
866
  }
791
867
  await rm(reportPath, { recursive: true });
792
868
  }
869
+ await __classPrivateFieldGet(this, _AllureReport_finishArtifactsManifest, "f").call(this);
793
870
  for (const dir of __classPrivateFieldGet(this, _AllureReport_dumpTempDirs, "f")) {
794
871
  try {
795
872
  await rm(dir, { recursive: true });
@@ -818,11 +895,6 @@ export class AllureReport {
818
895
  console.info(`- ${href}`);
819
896
  });
820
897
  }
821
- if (!__classPrivateFieldGet(this, _AllureReport_qualityGate, "f")) {
822
- return;
823
- }
824
- const qualityGateResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").qualityGateResultsByEnv();
825
- await writeFile(join(__classPrivateFieldGet(this, _AllureReport_output, "f"), "quality-gate.json"), JSON.stringify(qualityGateResults));
826
898
  }
827
899
  finally {
828
900
  await __classPrivateFieldGet(this, _AllureReport_finishPerfMetrics, "f").call(this);
@@ -835,7 +907,7 @@ export class AllureReport {
835
907
  _AllureReport_finishPerfMetrics.set(this, async () => {
836
908
  __classPrivateFieldGet(this, _AllureReport_finishGeneratePerfSpan, "f").call(this);
837
909
  try {
838
- await writePerfMetrics(__classPrivateFieldGet(this, _AllureReport_output, "f"));
910
+ await writePerfMetrics(__classPrivateFieldGet(this, _AllureReport_output, "f"), perfMetricsFileName(this.reportUuid));
839
911
  }
840
912
  catch (err) {
841
913
  console.error("Failed to write Allure performance metrics", err);
@@ -893,7 +965,7 @@ export class AllureReport {
893
965
  }
894
966
  }
895
967
  });
896
- const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyLimit, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, dump, categories, allureService, globalAttachments, } = opts;
968
+ const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyLimit, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, performance, dump, categories, allureService, globalAttachments, cwd, } = opts;
897
969
  const allureServiceAccessToken = allureService?.accessToken;
898
970
  if (allureServiceAccessToken) {
899
971
  const allureServiceClientConfig = {
@@ -907,6 +979,7 @@ export class AllureReport {
907
979
  : undefined, "f");
908
980
  }
909
981
  this.reportUuid = randomUUID();
982
+ __classPrivateFieldSet(this, _AllureReport_cwd, cwd ? resolve(cwd) : undefined, "f");
910
983
  __classPrivateFieldSet(this, _AllureReport_ci, detect(), "f");
911
984
  const reportTitleSuffix = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestName ?? __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobRunName;
912
985
  this.reportName = [name, reportTitleSuffix].filter(Boolean).join(" – ");
@@ -946,6 +1019,7 @@ export class AllureReport {
946
1019
  defaultLabels,
947
1020
  environment,
948
1021
  allowedEnvironments,
1022
+ performance,
949
1023
  }), "f");
950
1024
  __classPrivateFieldSet(this, _AllureReport_readers, [...readers], "f");
951
1025
  __classPrivateFieldSet(this, _AllureReport_plugins, [...plugins], "f");
@@ -965,6 +1039,6 @@ export class AllureReport {
965
1039
  return __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").dispatcher;
966
1040
  }
967
1041
  }
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) {
1042
+ _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_cwd = 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_artifactFilesByPath = new WeakMap(), _AllureReport_published = new WeakMap(), _AllureReport_endGeneratePerfSpan = new WeakMap(), _AllureReport_createHistoryDataPoint = new WeakMap(), _AllureReport_ingestSelfPerfMetrics = new WeakMap(), _AllureReport_publish = new WeakMap(), _AllureReport_getCwd = new WeakMap(), _AllureReport_runRealtimeUpdate = new WeakMap(), _AllureReport_recordArtifact = 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_getRootRemoteReportFiles = new WeakMap(), _AllureReport_writeQualityGateFiles = new WeakMap(), _AllureReport_finishArtifactsManifest = new WeakMap(), _AllureReport_generateRootSummary = new WeakMap(), _AllureReport_finishGeneratePerfSpan = new WeakMap(), _AllureReport_finishPerfMetrics = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_getPluginState = function _AllureReport_getPluginState(init, id) {
969
1043
  return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
970
1044
  };
@@ -1,4 +1,4 @@
1
- import { type AllureCheckResult, type AllureHistory, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type ResolutionIssue, type ResolutionsConfig, 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 AllurePerformanceResult, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type ResolutionIssue, type ResolutionsConfig, type MetricSample, type PerformanceConfig, 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>;
@@ -21,6 +21,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
21
21
  allowedEnvironments?: string[];
22
22
  environmentsConfig?: EnvironmentsConfig;
23
23
  reportVariables?: ReportVariables;
24
+ performance?: PerformanceConfig;
24
25
  });
25
26
  readHistory(): Promise<HistoryDataPoint[]>;
26
27
  appendHistory(history: HistoryDataPoint): Promise<void>;
@@ -35,6 +36,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
35
36
  allGlobalAttachments(): Promise<GlobalAttachmentLink[]>;
36
37
  allGlobalAttachmentsByEnv(): Promise<Record<string, GlobalAttachmentLink[]>>;
37
38
  visitCheckResult(result: AllureCheckResult): Promise<void>;
39
+ visitMetrics(metrics: AllurePerformanceResult[], context?: ReaderContext): Promise<void>;
38
40
  visitTestResult(raw: RawTestResult, context: ReaderContext): Promise<void>;
39
41
  visitTestFixtureResult(result: RawFixtureResult, context: ReaderContext): Promise<void>;
40
42
  visitAttachmentFile(resultFile: ResultFile): Promise<void>;
@@ -57,6 +59,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
57
59
  allResolutionIssues(): Promise<ResolutionIssue[]>;
58
60
  resolutionIssueByTestResultId(trId: string): Promise<ResolutionIssue | undefined>;
59
61
  testResultsByResolutionIssueId(resolutionIssueId: string): Promise<TestResult[]>;
62
+ allMetrics(): Promise<MetricSample[]>;
60
63
  allNewTestResults(filter?: TestResultFilter, history?: HistoryDataPoint[]): Promise<TestResult[]>;
61
64
  testCaseById(tcId: string): Promise<TestCase | undefined>;
62
65
  testResultById(trId: string): Promise<TestResult | undefined>;
@@ -9,9 +9,9 @@ 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_resolutionsConfig, _DefaultAllureStore_resolutionIssues, _DefaultAllureStore_testResultIdsByResolutionIssueId, _DefaultAllureStore_resolutionIssueIdByTestResultId, _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_classifyResolution, _DefaultAllureStore_associateResolutionIssue, _DefaultAllureStore_removeResolutionIssueAssociation, _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_resolutionsConfig, _DefaultAllureStore_resolutionIssues, _DefaultAllureStore_testResultIdsByResolutionIssueId, _DefaultAllureStore_resolutionIssueIdByTestResultId, _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_metrics, _DefaultAllureStore_performance, _DefaultAllureStore_historyPoints, _DefaultAllureStore_environments, _DefaultAllureStore_mergeEnvironmentIdentity, _DefaultAllureStore_environmentIdForLookup, _DefaultAllureStore_addEnvironments, _DefaultAllureStore_environmentIdByName, _DefaultAllureStore_setTestResultEnvironmentId, _DefaultAllureStore_assertAllowedEnvironmentId, _DefaultAllureStore_assertAllowedStoredEnvironment, _DefaultAllureStore_environmentIdByTestResult, _DefaultAllureStore_assignRetryHash, _DefaultAllureStore_classifyResolution, _DefaultAllureStore_associateResolutionIssue, _DefaultAllureStore_removeResolutionIssueAssociation, _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
- import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, compareBy, createDictionary, getHistoryIdCandidates, getWorstStatus, normalizeHistoryDataPoint, ordinal, reverse, selectHistoryTestResults, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
14
+ import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, compareBy, createDictionary, getHistoryIdCandidates, getWorstStatus, normalizeHistoryDataPoint, ordinal, reverse, resolveMetricSamples, selectHistoryTestResults, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
15
15
  import { md5, } from "@allurereport/plugin-api";
16
16
  import { getResolutionByRules, isIgnoredFailure } from "../resolutions.js";
17
17
  import { environmentIdentityById, normalizeEnvironmentDescriptorMap, resolveEnvironmentIdentity, resolveStoredEnvironmentIdentity, validateAllowedEnvironmentId, } from "../utils/environment.js";
@@ -92,9 +92,11 @@ export class DefaultAllureStore {
92
92
  _DefaultAllureStore_globalExitCode.set(this, void 0);
93
93
  _DefaultAllureStore_checkResultsById.set(this, new Map());
94
94
  _DefaultAllureStore_qualityGateResults.set(this, []);
95
+ _DefaultAllureStore_metrics.set(this, []);
96
+ _DefaultAllureStore_performance.set(this, {});
95
97
  _DefaultAllureStore_historyPoints.set(this, []);
96
98
  _DefaultAllureStore_environments.set(this, []);
97
- const { history, resolutionsConfig, realtimeDispatcher, realtimeSubscriber, defaultLabels = {}, environment, allowedEnvironments, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
99
+ const { history, resolutionsConfig, realtimeDispatcher, realtimeSubscriber, defaultLabels = {}, environment, allowedEnvironments, environmentsConfig = {}, reportVariables = {}, performance = {}, } = params ?? {};
98
100
  const errors = [];
99
101
  const { normalized: normalizedEnvironmentsConfig, identities, errors: configErrors, } = normalizeEnvironmentDescriptorMap(environmentsConfig, "environmentsConfig");
100
102
  errors.push(...configErrors);
@@ -120,6 +122,7 @@ export class DefaultAllureStore {
120
122
  __classPrivateFieldSet(this, _DefaultAllureStore_fixtures, new Map(), "f");
121
123
  __classPrivateFieldSet(this, _DefaultAllureStore_history, history, "f");
122
124
  __classPrivateFieldSet(this, _DefaultAllureStore_resolutionsConfig, resolutionsConfig, "f");
125
+ __classPrivateFieldSet(this, _DefaultAllureStore_performance, performance, "f");
123
126
  __classPrivateFieldSet(this, _DefaultAllureStore_realtimeDispatcher, realtimeDispatcher, "f");
124
127
  __classPrivateFieldSet(this, _DefaultAllureStore_realtimeSubscriber, realtimeSubscriber, "f");
125
128
  __classPrivateFieldSet(this, _DefaultAllureStore_defaultLabels, defaultLabels, "f");
@@ -290,6 +293,13 @@ export class DefaultAllureStore {
290
293
  async visitCheckResult(result) {
291
294
  await this.addCheckResult(result);
292
295
  }
296
+ async visitMetrics(metrics, context = { readerId: "api" }) {
297
+ const source = context.metadata?.originalFileName;
298
+ __classPrivateFieldGet(this, _DefaultAllureStore_metrics, "f").push(...resolveMetricSamples(metrics.map((metric) => ({
299
+ ...metric,
300
+ ...(source ? { source } : {}),
301
+ })), __classPrivateFieldGet(this, _DefaultAllureStore_performance, "f")));
302
+ }
293
303
  async visitTestResult(raw, context) {
294
304
  const attachmentLinks = [];
295
305
  const testResult = testResultRawToState({
@@ -529,6 +539,9 @@ export class DefaultAllureStore {
529
539
  .map((testResultId) => __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").get(testResultId))
530
540
  .filter(Boolean);
531
541
  }
542
+ async allMetrics() {
543
+ return [...__classPrivateFieldGet(this, _DefaultAllureStore_metrics, "f")];
544
+ }
532
545
  async allNewTestResults(filter, history) {
533
546
  if (!__classPrivateFieldGet(this, _DefaultAllureStore_history, "f") && !history) {
534
547
  return [];
@@ -673,6 +686,20 @@ export class DefaultAllureStore {
673
686
  }
674
687
  async testsStatistic(filter) {
675
688
  const statistic = { total: 0 };
689
+ const incrementResolution = (tr) => {
690
+ if (tr.resolution === "issue") {
691
+ statistic.resolutions ?? (statistic.resolutions = {});
692
+ statistic.resolutions.issues = (statistic.resolutions.issues ?? 0) + 1;
693
+ }
694
+ if (tr.resolution === "muted") {
695
+ statistic.resolutions ?? (statistic.resolutions = {});
696
+ statistic.resolutions.muted = (statistic.resolutions.muted ?? 0) + 1;
697
+ }
698
+ if (tr.resolution === "accepted") {
699
+ statistic.resolutions ?? (statistic.resolutions = {});
700
+ statistic.resolutions.accepted = (statistic.resolutions.accepted ?? 0) + 1;
701
+ }
702
+ };
676
703
  for (const [, tr] of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f")) {
677
704
  if (tr.isRetry) {
678
705
  continue;
@@ -691,6 +718,7 @@ export class DefaultAllureStore {
691
718
  if (tr.transition === "new") {
692
719
  statistic.new = (statistic.new ?? 0) + 1;
693
720
  }
721
+ incrementResolution(tr);
694
722
  if (!statistic[tr.status]) {
695
723
  statistic[tr.status] = 0;
696
724
  }
@@ -811,6 +839,7 @@ export class DefaultAllureStore {
811
839
  indexFixturesByTestResult: {},
812
840
  resolutionIssues: mapToObject(__classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f")),
813
841
  qualityGateResults: __classPrivateFieldGet(this, _DefaultAllureStore_qualityGateResults, "f"),
842
+ metrics: __classPrivateFieldGet(this, _DefaultAllureStore_metrics, "f"),
814
843
  testResultIdsIngestOrder: __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").ingestOrderIdsForDump(),
815
844
  };
816
845
  this.indexAttachmentByFixture.forEach((link, fxId) => {
@@ -834,7 +863,7 @@ export class DefaultAllureStore {
834
863
  return storeDump;
835
864
  }
836
865
  async restoreState(stateDump, attachmentsContents = {}) {
837
- const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], } = stateDump;
866
+ const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], metrics = [], testResultIdsIngestOrder = [], } = stateDump;
838
867
  const storedEnvironmentAliases = environments.flatMap((environmentValue) => {
839
868
  if (typeof environmentValue === "string") {
840
869
  return [{ id: environmentValue, name: environmentValue }];
@@ -1018,9 +1047,10 @@ export class DefaultAllureStore {
1018
1047
  });
1019
1048
  __classPrivateFieldGet(this, _DefaultAllureStore_qualityGateResults, "f").push(result);
1020
1049
  });
1050
+ __classPrivateFieldGet(this, _DefaultAllureStore_metrics, "f").push(...metrics);
1021
1051
  }
1022
1052
  }
1023
- _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_resolutionsConfig = new WeakMap(), _DefaultAllureStore_resolutionIssues = new WeakMap(), _DefaultAllureStore_testResultIdsByResolutionIssueId = new WeakMap(), _DefaultAllureStore_resolutionIssueIdByTestResultId = 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) {
1053
+ _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_resolutionsConfig = new WeakMap(), _DefaultAllureStore_resolutionIssues = new WeakMap(), _DefaultAllureStore_testResultIdsByResolutionIssueId = new WeakMap(), _DefaultAllureStore_resolutionIssueIdByTestResultId = 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_metrics = new WeakMap(), _DefaultAllureStore_performance = new WeakMap(), _DefaultAllureStore_historyPoints = new WeakMap(), _DefaultAllureStore_environments = new WeakMap(), _DefaultAllureStore_instances = new WeakSet(), _DefaultAllureStore_mergeEnvironmentIdentity = function _DefaultAllureStore_mergeEnvironmentIdentity(existingEnvironment, incomingEnvironment) {
1024
1054
  const configuredEnvironment = environmentIdentityById(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f"), incomingEnvironment.id);
1025
1055
  if (configuredEnvironment) {
1026
1056
  return configuredEnvironment;
@@ -1125,6 +1155,7 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environment
1125
1155
  }, _DefaultAllureStore_classifyResolution = function _DefaultAllureStore_classifyResolution(testResult) {
1126
1156
  const rule = getResolutionByRules(testResult, __classPrivateFieldGet(this, _DefaultAllureStore_resolutionsConfig, "f"));
1127
1157
  testResult.resolution = rule?.resolution;
1158
+ testResult.resolutionComment = rule?.comment;
1128
1159
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
1129
1160
  if (rule?.resolution !== "issue") {
1130
1161
  return;
@@ -0,0 +1,14 @@
1
+ export declare const ARTIFACTS_MANIFEST_FILENAME = "artifacts.json";
2
+ export type ReportArtifact = {
3
+ name: string;
4
+ path: string;
5
+ };
6
+ export type ReportArtifactsManifest = ReportArtifact[];
7
+ export type RestoreStateDumpInput = {
8
+ artifactPath?: string;
9
+ path: string;
10
+ recordArtifact?: boolean;
11
+ };
12
+ export declare const normalizeArtifactPath: (filePath: string, cwd: string) => string;
13
+ export declare const createReportArtifact: (filePath: string, cwd: string, name?: string) => ReportArtifact;
14
+ export declare const deduplicateDumpInputs: (dumps: RestoreStateDumpInput[], cwd: string) => RestoreStateDumpInput[];
@@ -0,0 +1,35 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { basename, relative, resolve, sep } from "node:path";
3
+ export const ARTIFACTS_MANIFEST_FILENAME = "artifacts.json";
4
+ const existingRealPath = (filePath) => {
5
+ try {
6
+ return realpathSync(filePath);
7
+ }
8
+ catch {
9
+ return filePath;
10
+ }
11
+ };
12
+ export const normalizeArtifactPath = (filePath, cwd) => {
13
+ const path = relative(resolve(cwd), resolve(cwd, filePath)).split(sep).join("/");
14
+ return path || ".";
15
+ };
16
+ export const createReportArtifact = (filePath, cwd, name = basename(filePath)) => ({
17
+ name,
18
+ path: normalizeArtifactPath(filePath, cwd),
19
+ });
20
+ export const deduplicateDumpInputs = (dumps, cwd) => {
21
+ const seen = new Set();
22
+ return dumps.reduce((acc, dump) => {
23
+ const absolutePath = resolve(cwd, dump.path);
24
+ const dedupeKey = existingRealPath(absolutePath);
25
+ if (seen.has(dedupeKey)) {
26
+ return acc;
27
+ }
28
+ seen.add(dedupeKey);
29
+ acc.push({
30
+ ...dump,
31
+ path: absolutePath,
32
+ });
33
+ return acc;
34
+ }, []);
35
+ };
@@ -1,4 +1,6 @@
1
- export declare const PERF_METRICS_FILE = "allure-perf-metrics.json";
1
+ import type { AllurePerformanceResult } from "@allurereport/core-api";
2
+ export declare const PERF_METRICS_FILE = "performance.json";
3
+ export declare const perfMetricsFileName: (reportUuid: string) => string;
2
4
  export type PerfMetricSpan = {
3
5
  name: string;
4
6
  startTimeMs: number;
@@ -12,14 +14,8 @@ export type PerfMetricSummary = {
12
14
  maxMs: number;
13
15
  avgMs: number;
14
16
  };
15
- export type PerfMetricsPayload = {
16
- version: 1;
17
- generatedAt: string;
18
- timeOriginMs: number;
19
- spans: PerfMetricSpan[];
20
- summary: PerfMetricSummary[];
21
- };
22
17
  export declare const PERF_METRIC_NAMES: {
18
+ readonly allureTotal: "allure.total";
23
19
  readonly restoreStateTotal: "restoreState.total";
24
20
  readonly restoreStateDump: "restoreState.dump";
25
21
  readonly restoreStateAttachments: "restoreState.attachments";
@@ -37,6 +33,6 @@ export declare const PERF_METRIC_PREFIXES: {
37
33
  export declare const isPerfMetricsEnabled: () => boolean;
38
34
  export declare const startPerfSpan: (name: string) => (() => void);
39
35
  export declare const measurePerf: <T>(name: string, fn: () => Promise<T>) => Promise<T>;
40
- export declare const getPerfMetricsPayload: () => PerfMetricsPayload;
41
- export declare const writePerfMetrics: (output: string) => Promise<boolean>;
36
+ export declare const getPerfMetricsResults: () => AllurePerformanceResult[];
37
+ export declare const writePerfMetrics: (output: string, fileName?: string) => Promise<boolean>;
42
38
  export declare const resetPerfMetrics: () => void;
@@ -1,7 +1,9 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { mkdir, writeFile } from "node:fs/promises";
2
3
  import { join } from "node:path";
3
4
  import { performance } from "node:perf_hooks";
4
- export const PERF_METRICS_FILE = "allure-perf-metrics.json";
5
+ export const PERF_METRICS_FILE = "performance.json";
6
+ export const perfMetricsFileName = (reportUuid) => `${reportUuid}-perf.json`;
5
7
  const MARK_PREFIX = "allure:perf:";
6
8
  const ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
7
9
  const SPANS = [];
@@ -10,6 +12,7 @@ const MEASURES = new Set();
10
12
  let sequence = 0;
11
13
  const round = (value) => Number(value.toFixed(3));
12
14
  export const PERF_METRIC_NAMES = {
15
+ allureTotal: "allure.total",
13
16
  restoreStateTotal: "restoreState.total",
14
17
  restoreStateDump: "restoreState.dump",
15
18
  restoreStateAttachments: "restoreState.attachments",
@@ -24,6 +27,22 @@ export const PERF_METRIC_PREFIXES = {
24
27
  generatePluginDone: "generate.plugin.done.",
25
28
  publishUploadPlugin: "publish.upload.plugin.",
26
29
  };
30
+ const getCoveredDurationSummary = () => {
31
+ if (SPANS.length === 0) {
32
+ return undefined;
33
+ }
34
+ const startTimeMs = Math.min(...SPANS.map(({ startTimeMs }) => startTimeMs));
35
+ const endTimeMs = Math.max(...SPANS.map(({ startTimeMs, durationMs }) => startTimeMs + durationMs));
36
+ const durationMs = round(endTimeMs - startTimeMs);
37
+ return {
38
+ name: PERF_METRIC_NAMES.allureTotal,
39
+ count: 1,
40
+ totalMs: durationMs,
41
+ minMs: durationMs,
42
+ maxMs: durationMs,
43
+ avgMs: durationMs,
44
+ };
45
+ };
27
46
  export const isPerfMetricsEnabled = () => ENABLED_VALUES.has((process.env.ALLURE_PERF_METRICS ?? "").toLowerCase());
28
47
  export const startPerfSpan = (name) => {
29
48
  if (!isPerfMetricsEnabled()) {
@@ -72,39 +91,33 @@ export const measurePerf = async (name, fn) => {
72
91
  end();
73
92
  }
74
93
  };
75
- export const getPerfMetricsPayload = () => {
76
- const byName = new Map();
77
- for (const span of SPANS) {
78
- const current = byName.get(span.name) ?? [];
79
- current.push(span);
80
- byName.set(span.name, current);
94
+ export const getPerfMetricsResults = () => {
95
+ const totalSummary = getCoveredDurationSummary();
96
+ const results = SPANS.map((span) => ({
97
+ id: randomUUID(),
98
+ key: span.name,
99
+ value: span.durationMs,
100
+ start: round(performance.timeOrigin + span.startTimeMs),
101
+ stop: round(performance.timeOrigin + span.startTimeMs + span.durationMs),
102
+ }));
103
+ if (totalSummary) {
104
+ results.unshift({
105
+ id: randomUUID(),
106
+ key: PERF_METRIC_NAMES.allureTotal,
107
+ value: totalSummary.avgMs,
108
+ start: round(performance.timeOrigin + Math.min(...SPANS.map(({ startTimeMs }) => startTimeMs))),
109
+ stop: round(performance.timeOrigin + Math.max(...SPANS.map(({ startTimeMs, durationMs }) => startTimeMs + durationMs))),
110
+ });
81
111
  }
82
- return {
83
- version: 1,
84
- generatedAt: new Date().toISOString(),
85
- timeOriginMs: round(performance.timeOrigin),
86
- spans: [...SPANS],
87
- summary: [...byName.entries()].map(([name, group]) => {
88
- const durations = group.map(({ durationMs }) => durationMs);
89
- const totalMs = durations.reduce((acc, duration) => acc + duration, 0);
90
- return {
91
- name,
92
- count: group.length,
93
- totalMs: round(totalMs),
94
- minMs: round(Math.min(...durations)),
95
- maxMs: round(Math.max(...durations)),
96
- avgMs: round(totalMs / group.length),
97
- };
98
- }),
99
- };
112
+ return results;
100
113
  };
101
- export const writePerfMetrics = async (output) => {
114
+ export const writePerfMetrics = async (output, fileName = PERF_METRICS_FILE) => {
102
115
  if (!isPerfMetricsEnabled()) {
103
116
  return false;
104
117
  }
105
- const payload = getPerfMetricsPayload();
118
+ const results = getPerfMetricsResults();
106
119
  await mkdir(output, { recursive: true });
107
- await writeFile(join(output, PERF_METRICS_FILE), `${JSON.stringify(payload, null, 2)}\n`, "utf8");
120
+ await writeFile(join(output, fileName), `${JSON.stringify(results, null, 2)}\n`, "utf8");
108
121
  resetPerfMetrics();
109
122
  return true;
110
123
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.16.1",
3
+ "version": "3.17.0",
4
4
  "description": "Collection of generic Allure utilities used across the entire project",
5
5
  "keywords": [
6
6
  "allure"
@@ -25,26 +25,26 @@
25
25
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
26
26
  },
27
27
  "dependencies": {
28
- "@allurereport/ci": "3.16.1",
29
- "@allurereport/cli-commons": "3.16.1",
30
- "@allurereport/core-api": "3.16.1",
31
- "@allurereport/plugin-agent": "3.16.1",
32
- "@allurereport/plugin-allure2": "3.16.1",
33
- "@allurereport/plugin-api": "3.16.1",
34
- "@allurereport/plugin-awesome": "3.16.1",
35
- "@allurereport/plugin-classic": "3.16.1",
36
- "@allurereport/plugin-csv": "3.16.1",
37
- "@allurereport/plugin-dashboard": "3.16.1",
38
- "@allurereport/plugin-jira": "3.16.1",
39
- "@allurereport/plugin-log": "3.16.1",
40
- "@allurereport/plugin-progress": "3.16.1",
41
- "@allurereport/plugin-slack": "3.16.1",
42
- "@allurereport/plugin-testops": "3.16.1",
43
- "@allurereport/plugin-testplan": "3.16.1",
44
- "@allurereport/reader": "3.16.1",
45
- "@allurereport/reader-api": "3.16.1",
46
- "@allurereport/service": "3.16.1",
47
- "@allurereport/summary": "3.16.1",
28
+ "@allurereport/ci": "3.17.0",
29
+ "@allurereport/cli-commons": "3.17.0",
30
+ "@allurereport/core-api": "3.17.0",
31
+ "@allurereport/plugin-agent": "3.17.0",
32
+ "@allurereport/plugin-allure2": "3.17.0",
33
+ "@allurereport/plugin-api": "3.17.0",
34
+ "@allurereport/plugin-awesome": "3.17.0",
35
+ "@allurereport/plugin-classic": "3.17.0",
36
+ "@allurereport/plugin-csv": "3.17.0",
37
+ "@allurereport/plugin-dashboard": "3.17.0",
38
+ "@allurereport/plugin-jira": "3.17.0",
39
+ "@allurereport/plugin-log": "3.17.0",
40
+ "@allurereport/plugin-progress": "3.17.0",
41
+ "@allurereport/plugin-slack": "3.17.0",
42
+ "@allurereport/plugin-testops": "3.17.0",
43
+ "@allurereport/plugin-testplan": "3.17.0",
44
+ "@allurereport/reader": "3.17.0",
45
+ "@allurereport/reader-api": "3.17.0",
46
+ "@allurereport/service": "3.17.0",
47
+ "@allurereport/summary": "3.17.0",
48
48
  "glob": "^13.0.6",
49
49
  "handlebars": "^4.7.9",
50
50
  "jiti": "^2.5.1",