@allurereport/core 3.20.1 → 3.22.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/config.d.ts CHANGED
@@ -7,6 +7,7 @@ export interface ConfigOverride {
7
7
  output?: Config["output"];
8
8
  open?: Config["open"];
9
9
  port?: Config["port"];
10
+ host?: Config["host"];
10
11
  hideLabels?: Config["hideLabels"];
11
12
  historyPath?: Config["historyPath"];
12
13
  historyBaseUrl?: Config["historyBaseUrl"];
package/dist/config.js CHANGED
@@ -87,6 +87,7 @@ export const validateConfig = (config) => {
87
87
  "output",
88
88
  "open",
89
89
  "port",
90
+ "host",
90
91
  "hideLabels",
91
92
  "historyPath",
92
93
  "historyBaseUrl",
@@ -189,10 +190,13 @@ const validateFlakyDetectionConfig = (config) => {
189
190
  if (config === undefined) {
190
191
  return;
191
192
  }
192
- const { historyDepth, includePassedTests } = config;
193
+ const { historyDepth, includePassedTests, stabilizationPeriod } = config;
193
194
  if (historyDepth !== undefined && (!Number.isInteger(historyDepth) || historyDepth < -1)) {
194
195
  throw new Error("flakyDetection.historyDepth must be an integer greater than or equal to -1");
195
196
  }
197
+ if (stabilizationPeriod !== undefined && (!Number.isInteger(stabilizationPeriod) || stabilizationPeriod < 1)) {
198
+ throw new Error("flakyDetection.stabilizationPeriod must be a positive integer");
199
+ }
196
200
  if (includePassedTests !== undefined && typeof includePassedTests !== "boolean") {
197
201
  throw new TypeError("flakyDetection.includePassedTests must be a boolean");
198
202
  }
@@ -208,6 +212,7 @@ export const resolveConfig = async (config, override = {}) => {
208
212
  const name = override.name ?? config.name ?? "Allure Report";
209
213
  const open = override.open ?? config.open ?? false;
210
214
  const port = override.port ?? config.port ?? undefined;
215
+ const host = override.host ?? config.host ?? undefined;
211
216
  const hideLabels = override.hideLabels ?? config.hideLabels;
212
217
  const historyPath = override.historyPath ?? config.historyPath;
213
218
  const historyBaseUrl = override.historyBaseUrl ?? config.historyBaseUrl;
@@ -252,6 +257,7 @@ export const resolveConfig = async (config, override = {}) => {
252
257
  output,
253
258
  open,
254
259
  port,
260
+ host,
255
261
  hideLabels,
256
262
  resolutions,
257
263
  environment,
package/dist/history.js CHANGED
@@ -9,7 +9,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
- var _AllureLocalHistory_cachedHistory, _AllureLocalHistory_openFileToReadIfExists, _AllureLocalHistory_ensureFileOpenedToAppend, _AllureLocalHistory_findFirstEntryAddress, _AllureLocalHistory_throwUnexpectedReadError;
12
+ var _AllureLocalHistory_cachedHistory, _AllureLocalHistory_closeStreams, _AllureLocalHistory_openFileToReadIfExists, _AllureLocalHistory_ensureFileOpenedToAppend, _AllureLocalHistory_findFirstEntryAddress, _AllureLocalHistory_throwUnexpectedReadError;
13
13
  import { once } from "node:events";
14
14
  import { mkdir, open } from "node:fs/promises";
15
15
  import path from "node:path";
@@ -20,13 +20,14 @@ import { isFileNotFoundError } from "./utils/misc.js";
20
20
  const createHistoryItems = (testResults, remoteUrl) => {
21
21
  return testResults
22
22
  .filter((tr) => tr.retryHash)
23
- .map(({ id, name, fullName, environment, retryHash, status, error: { message, trace } = {}, start, stop, duration, labels, }) => {
23
+ .map(({ id, name, fullName, environment, retryHash, status, retries = [], error: { message, trace } = {}, start, stop, duration, labels, }) => {
24
24
  return {
25
25
  id,
26
26
  name,
27
27
  fullName,
28
28
  environment,
29
29
  status,
30
+ retries: retries.map((retry) => retry.status).reverse(),
30
31
  message,
31
32
  trace,
32
33
  start,
@@ -82,10 +83,25 @@ export const createHistory = (reportUuid, reportName = "Allure Report", testCase
82
83
  url: remoteUrl,
83
84
  };
84
85
  };
86
+ const closeStream = async (stream) => {
87
+ if (stream.closed) {
88
+ return;
89
+ }
90
+ await new Promise((resolve) => {
91
+ stream.once("error", () => { });
92
+ stream.once("close", () => resolve());
93
+ stream.destroy();
94
+ });
95
+ };
85
96
  export class AllureLocalHistory {
86
97
  constructor(params) {
87
98
  this.params = params;
88
99
  _AllureLocalHistory_cachedHistory.set(this, []);
100
+ _AllureLocalHistory_closeStreams.set(this, async (streams) => {
101
+ for (const stream of streams) {
102
+ await closeStream(stream);
103
+ }
104
+ });
89
105
  _AllureLocalHistory_openFileToReadIfExists.set(this, async (filePath) => {
90
106
  try {
91
107
  return await open(filePath, "r");
@@ -175,9 +191,11 @@ export class AllureLocalHistory {
175
191
  if (historyFile === undefined) {
176
192
  return [];
177
193
  }
194
+ const openedStreams = [];
178
195
  try {
179
196
  const start = await __classPrivateFieldGet(this, _AllureLocalHistory_findFirstEntryAddress, "f").call(this, historyFile, this.params.limit);
180
197
  const stream = historyFile.createReadStream({ start, encoding: "utf-8", autoClose: false });
198
+ openedStreams.push(stream);
181
199
  const historyPoints = [];
182
200
  const readlineInterface = readline
183
201
  .createInterface({ input: stream, terminal: false, crlfDelay: Infinity })
@@ -192,6 +210,7 @@ export class AllureLocalHistory {
192
210
  return __classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f");
193
211
  }
194
212
  finally {
213
+ await __classPrivateFieldGet(this, _AllureLocalHistory_closeStreams, "f").call(this, openedStreams);
195
214
  await historyFile.close();
196
215
  }
197
216
  }
@@ -202,6 +221,7 @@ export class AllureLocalHistory {
202
221
  const { limit } = this.params;
203
222
  await mkdir(parentDir, { recursive: true });
204
223
  const { file: historyFile, exists: historyExists } = await __classPrivateFieldGet(this, _AllureLocalHistory_ensureFileOpenedToAppend, "f").call(this, fullPath);
224
+ const openedStreams = [];
205
225
  let dst;
206
226
  try {
207
227
  if (limit === 0) {
@@ -210,9 +230,11 @@ export class AllureLocalHistory {
210
230
  return;
211
231
  }
212
232
  dst = historyFile.createWriteStream({ encoding: "utf-8", start: 0, autoClose: false });
233
+ openedStreams.push(dst);
213
234
  if (historyExists) {
214
235
  const start = await __classPrivateFieldGet(this, _AllureLocalHistory_findFirstEntryAddress, "f").call(this, historyFile, limit ? limit - 1 : undefined);
215
236
  const src = historyFile.createReadStream({ start, autoClose: false });
237
+ openedStreams.push(src);
216
238
  await pipeline(src, dst, { end: false });
217
239
  }
218
240
  const sources = [JSON.stringify(normalizedData), Buffer.from([0x0a])];
@@ -222,11 +244,8 @@ export class AllureLocalHistory {
222
244
  }
223
245
  }
224
246
  finally {
225
- const closing = historyFile.close();
226
- if (historyFile.fd !== -1) {
227
- dst?.destroy();
228
- }
229
- await closing;
247
+ await __classPrivateFieldGet(this, _AllureLocalHistory_closeStreams, "f").call(this, openedStreams);
248
+ await historyFile.close();
230
249
  if (limit !== 0) {
231
250
  __classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f").push(normalizedData);
232
251
  }
@@ -236,4 +255,4 @@ export class AllureLocalHistory {
236
255
  }
237
256
  }
238
257
  }
239
- _AllureLocalHistory_cachedHistory = new WeakMap(), _AllureLocalHistory_openFileToReadIfExists = new WeakMap(), _AllureLocalHistory_ensureFileOpenedToAppend = new WeakMap(), _AllureLocalHistory_findFirstEntryAddress = new WeakMap(), _AllureLocalHistory_throwUnexpectedReadError = new WeakMap();
258
+ _AllureLocalHistory_cachedHistory = new WeakMap(), _AllureLocalHistory_closeStreams = new WeakMap(), _AllureLocalHistory_openFileToReadIfExists = new WeakMap(), _AllureLocalHistory_ensureFileOpenedToAppend = new WeakMap(), _AllureLocalHistory_findFirstEntryAddress = new WeakMap(), _AllureLocalHistory_throwUnexpectedReadError = new WeakMap();
@@ -65,8 +65,9 @@ export class QualityGate {
65
65
  async validate(payload) {
66
66
  const { state, trs, metrics = [], previousHistory = [], environment } = payload;
67
67
  const trsToValidateById = new Map();
68
+ const resolutionEvaluatedAt = Date.now();
68
69
  for (const tr of trs) {
69
- if (tr.isRetry || isIgnoredFailure(tr) || state?.isTestResultProcessed(tr.id)) {
70
+ if (tr.isRetry || isIgnoredFailure(tr, resolutionEvaluatedAt) || state?.isTestResultProcessed(tr.id)) {
70
71
  continue;
71
72
  }
72
73
  trsToValidateById.set(tr.id, tr);
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_historyBaseUrl, _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_resolvedReportUrl, _AllureReport_reportUrl, _AllureReport_resolveReportUrl, _AllureReport_createHistoryDataPoint, _AllureReport_ingestSelfPerfMetrics, _AllureReport_prepareReportFiles, _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;
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_historyBaseUrl, _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_overviewHref, _AllureReport_testResultsRegistryPath, _AllureReport_summariesByPluginId, _AllureReport_publishedRemoteHrefs, _AllureReport_artifactFilesByPath, _AllureReport_published, _AllureReport_endGeneratePerfSpan, _AllureReport_resolvedReportUrl, _AllureReport_reportUrl, _AllureReport_resolveReportUrl, _AllureReport_createHistoryDataPoint, _AllureReport_ingestSelfPerfMetrics, _AllureReport_prepareReportFiles, _AllureReport_publish, _AllureReport_getCwd, _AllureReport_runRealtimeUpdate, _AllureReport_recordArtifact, _AllureReport_restoreStateDumps, _AllureReport_resolveOverviewHref, _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";
@@ -18,11 +18,11 @@ import { lstat, mkdtemp, readdir, rename, rm, writeFile } from "node:fs/promises
18
18
  import { tmpdir } from "node:os";
19
19
  import { basename, dirname, join, resolve } from "node:path";
20
20
  import { promisify } from "node:util";
21
- import { detect } from "@allurereport/ci";
21
+ import { createReportCiContext, detect } from "@allurereport/ci";
22
22
  import { createProgressLogger } from "@allurereport/cli-commons";
23
23
  import { normalizeCategoriesConfig, parseIntegerConfigValue } from "@allurereport/core-api";
24
24
  import { AllureStoreDumpFiles, createTestResultRegistry, } from "@allurereport/plugin-api";
25
- import { allure1, allure2, attachments, cucumberjson, cucumberMessages, junitXml, readXcResultBundle, } from "@allurereport/reader";
25
+ import { allure1, allure2, attachments, cucumberjson, cucumberMessages, junitXml, openTestReporting, readXcResultBundle, } from "@allurereport/reader";
26
26
  import { PathResultFile } from "@allurereport/reader-api";
27
27
  import { AllureRemoteHistory, AllureServiceClient, AllureTestOpsClient, KnownError, UnknownError, } from "@allurereport/service";
28
28
  import { generateSummary } from "@allurereport/summary";
@@ -48,6 +48,7 @@ const MAX_READ_CONCURRENCY = 256;
48
48
  const DEFAULT_UPLOAD_CONCURRENCY = 10;
49
49
  const TEST_RESULTS_REGISTRY_FILENAME = "test-results.json";
50
50
  const QUALITY_GATE_RESULTS_FILENAME = "quality-gate.json";
51
+ const AUXILIARY_OUTPUT_DIRECTORIES = new Set(["export"]);
51
52
  const ROOT_INTEGRATION_FILENAMES = new Set([
52
53
  TEST_RESULTS_REGISTRY_FILENAME,
53
54
  QUALITY_GATE_RESULTS_FILENAME,
@@ -66,11 +67,15 @@ const readConcurrency = () => {
66
67
  return Math.min(MAX_READ_CONCURRENCY, Math.max(1, parsed));
67
68
  };
68
69
  const clonePluginSummary = (summary) => structuredClone(summary);
69
- const remoteReportParams = (ci) => {
70
+ const remoteBranchParams = (ci, branch) => {
70
71
  const repo = ci?.repoName;
71
- const branch = ci?.jobRunBranch;
72
72
  return repo && branch ? { repo, branch } : {};
73
73
  };
74
+ const remoteHistoryParams = (ci) => {
75
+ const historyBranch = ci?.targetBranch || ci?.jobRunBranch;
76
+ return remoteBranchParams(ci, historyBranch);
77
+ };
78
+ const remoteReportParams = (ci) => remoteBranchParams(ci, ci?.jobRunBranch);
74
79
  const errorDetails = (err) => (err instanceof Error ? (err.stack ?? err.message) : String(err));
75
80
  const getExecutorReportUrl = (executor) => {
76
81
  if (!executor || typeof executor !== "object" || !("reportUrl" in executor)) {
@@ -126,6 +131,7 @@ export class AllureReport {
126
131
  _AllureReport_executionStage.set(this, "init");
127
132
  _AllureReport_historyDataPoint.set(this, void 0);
128
133
  _AllureReport_summaryPath.set(this, void 0);
134
+ _AllureReport_overviewHref.set(this, void 0);
129
135
  _AllureReport_testResultsRegistryPath.set(this, void 0);
130
136
  _AllureReport_summariesByPluginId.set(this, new Map());
131
137
  _AllureReport_publishedRemoteHrefs.set(this, new Set());
@@ -149,7 +155,10 @@ export class AllureReport {
149
155
  __classPrivateFieldSet(this, _AllureReport_resolvedReportUrl, executorReportUrl, "f");
150
156
  });
151
157
  _AllureReport_createHistoryDataPoint.set(this, async (flattenReport) => {
152
- const allTrs = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults();
158
+ const allTrs = await Promise.all((await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults()).map(async (tr) => ({
159
+ ...tr,
160
+ retries: await __classPrivateFieldGet(this, _AllureReport_store, "f").retriesByTrId(tr.id),
161
+ })));
153
162
  const allTcs = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestCases();
154
163
  const historyUrl = __classPrivateFieldGet(this, _AllureReport_reportUrl, "f") ?? (!flattenReport && __classPrivateFieldGet(this, _AllureReport_historyBaseUrl, "f") ? __classPrivateFieldGet(this, _AllureReport_historyBaseUrl, "f") : __classPrivateFieldGet(this, _AllureReport_resolvedReportUrl, "f"));
155
164
  return createHistory(this.reportUuid, this.reportName, allTcs, allTrs, historyUrl ?? "", await __classPrivateFieldGet(this, _AllureReport_store, "f").allMetrics());
@@ -747,6 +756,19 @@ export class AllureReport {
747
756
  }
748
757
  return restoredCount;
749
758
  }));
759
+ _AllureReport_resolveOverviewHref.set(this, async () => {
760
+ if (__classPrivateFieldGet(this, _AllureReport_realTime, "f")) {
761
+ return undefined;
762
+ }
763
+ let summariesCount = 0;
764
+ await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
765
+ const summary = await plugin.info?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
766
+ if (summary) {
767
+ summariesCount += 1;
768
+ }
769
+ });
770
+ return summariesCount > 1 ? "../index.html" : undefined;
771
+ });
750
772
  _AllureReport_getReportsToPublish.set(this, async () => {
751
773
  const reports = [];
752
774
  for (const { enabled, id, options } of __classPrivateFieldGet(this, _AllureReport_plugins, "f")) {
@@ -866,6 +888,7 @@ export class AllureReport {
866
888
  return;
867
889
  }
868
890
  await __classPrivateFieldGet(this, _AllureReport_resolveReportUrl, "f").call(this);
891
+ __classPrivateFieldSet(this, _AllureReport_overviewHref, await __classPrivateFieldGet(this, _AllureReport_resolveOverviewHref, "f").call(this), "f");
869
892
  await measurePerf(PERF_METRIC_NAMES.generatePluginsDone, async () => {
870
893
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
871
894
  await measurePerf(`${PERF_METRIC_PREFIXES.generatePluginDone}${context.id}`, async () => {
@@ -875,6 +898,7 @@ export class AllureReport {
875
898
  });
876
899
  __classPrivateFieldGet(this, _AllureReport_finishGeneratePerfSpan, "f").call(this);
877
900
  await __classPrivateFieldGet(this, _AllureReport_ingestSelfPerfMetrics, "f").call(this);
901
+ const ciContext = createReportCiContext(__classPrivateFieldGet(this, _AllureReport_ci, "f"));
878
902
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
879
903
  const summary = await plugin?.info?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
880
904
  if (!summary) {
@@ -883,6 +907,7 @@ export class AllureReport {
883
907
  summary.pluginId = context.id;
884
908
  summary.pullRequestHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestUrl;
885
909
  summary.jobHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobRunUrl;
910
+ Object.assign(summary, ciContext);
886
911
  summaries.push({
887
912
  ...summary,
888
913
  href: `${context.id}/`,
@@ -899,7 +924,8 @@ export class AllureReport {
899
924
  catch { }
900
925
  const outputEntries = await Promise.all(outputDirFiles.map(async (file) => ({ file, stats: await lstat(join(__classPrivateFieldGet(this, _AllureReport_output, "f"), file)) })));
901
926
  const outputDirectoryEntries = outputEntries.filter(({ stats }) => stats.isDirectory());
902
- const shouldFlattenOutput = outputDirectoryEntries.length === 1;
927
+ const reportDirectoryEntries = outputDirectoryEntries.filter(({ file }) => !AUXILIARY_OUTPUT_DIRECTORIES.has(file));
928
+ const shouldFlattenOutput = reportDirectoryEntries.length === 1 && __classPrivateFieldGet(this, _AllureReport_plugins, "f").some(({ id }) => id === reportDirectoryEntries[0].file);
903
929
  __classPrivateFieldSet(this, _AllureReport_historyDataPoint, await __classPrivateFieldGet(this, _AllureReport_createHistoryDataPoint, "f").call(this, shouldFlattenOutput), "f");
904
930
  await __classPrivateFieldGet(this, _AllureReport_publish, "f").call(this);
905
931
  if (__classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f")) {
@@ -909,7 +935,7 @@ export class AllureReport {
909
935
  return;
910
936
  }
911
937
  if (shouldFlattenOutput) {
912
- const reportPath = join(__classPrivateFieldGet(this, _AllureReport_output, "f"), outputDirectoryEntries[0].file);
938
+ const reportPath = join(__classPrivateFieldGet(this, _AllureReport_output, "f"), reportDirectoryEntries[0].file);
913
939
  const reportContent = await readdir(reportPath);
914
940
  for (const entry of reportContent) {
915
941
  if (ROOT_INTEGRATION_FILENAMES.has(entry)) {
@@ -1015,6 +1041,7 @@ export class AllureReport {
1015
1041
  }
1016
1042
  : {}),
1017
1043
  reportUrl: this.reportUrl,
1044
+ overviewHref: __classPrivateFieldGet(this, _AllureReport_overviewHref, "f"),
1018
1045
  realTime: !!__classPrivateFieldGet(this, _AllureReport_realTime, "f"),
1019
1046
  output: __classPrivateFieldGet(this, _AllureReport_output, "f"),
1020
1047
  ci: __classPrivateFieldGet(this, _AllureReport_ci, "f"),
@@ -1035,7 +1062,7 @@ export class AllureReport {
1035
1062
  }
1036
1063
  }
1037
1064
  });
1038
- const { name, readers = [allure1, allure2, cucumberjson, cucumberMessages, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyBaseUrl, historyLimit, flakyDetection, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, performance, dump, categories, allureService, globalAttachments, cwd, } = opts;
1065
+ const { name, readers = [allure1, allure2, cucumberjson, cucumberMessages, openTestReporting, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyBaseUrl, historyLimit, flakyDetection, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, performance, dump, categories, allureService, globalAttachments, cwd, } = opts;
1039
1066
  const allureServiceAccessToken = allureService?.accessToken;
1040
1067
  if (allureServiceAccessToken) {
1041
1068
  const allureServiceClientConfig = {
@@ -1075,7 +1102,7 @@ export class AllureReport {
1075
1102
  if (__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")) {
1076
1103
  __classPrivateFieldSet(this, _AllureReport_history, new AllureRemoteHistory({
1077
1104
  limit: historyLimit,
1078
- ...remoteReportParams(__classPrivateFieldGet(this, _AllureReport_ci, "f")),
1105
+ ...remoteHistoryParams(__classPrivateFieldGet(this, _AllureReport_ci, "f")),
1079
1106
  allureServiceClient: __classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f"),
1080
1107
  }), "f");
1081
1108
  }
@@ -1122,6 +1149,6 @@ export class AllureReport {
1122
1149
  __classPrivateFieldSet(this, _AllureReport_reportUrl, url, "f");
1123
1150
  }
1124
1151
  }
1125
- _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_historyBaseUrl = 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_resolvedReportUrl = new WeakMap(), _AllureReport_reportUrl = new WeakMap(), _AllureReport_resolveReportUrl = new WeakMap(), _AllureReport_createHistoryDataPoint = new WeakMap(), _AllureReport_ingestSelfPerfMetrics = new WeakMap(), _AllureReport_prepareReportFiles = 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) {
1152
+ _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_historyBaseUrl = 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_overviewHref = 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_resolvedReportUrl = new WeakMap(), _AllureReport_reportUrl = new WeakMap(), _AllureReport_resolveReportUrl = new WeakMap(), _AllureReport_createHistoryDataPoint = new WeakMap(), _AllureReport_ingestSelfPerfMetrics = new WeakMap(), _AllureReport_prepareReportFiles = new WeakMap(), _AllureReport_publish = new WeakMap(), _AllureReport_getCwd = new WeakMap(), _AllureReport_runRealtimeUpdate = new WeakMap(), _AllureReport_recordArtifact = new WeakMap(), _AllureReport_restoreStateDumps = new WeakMap(), _AllureReport_resolveOverviewHref = 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) {
1126
1153
  return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
1127
1154
  };
@@ -11,7 +11,7 @@ export declare const KNOWN_FROM_RESULT_RESOLUTION: {
11
11
  readonly comment: "Accepted from result (known)";
12
12
  };
13
13
  export declare const getResolutionByRules: (testResult: TestResult, config?: ResolutionsConfig) => ResolutionRule | undefined;
14
- export declare const isIgnoredFailure: (testResult: TestResult) => boolean;
14
+ export declare const isIgnoredFailure: (testResult: TestResult, now?: number) => boolean;
15
15
  export declare const createResolutionIssue: (rule: IssueResolutionRule, config?: ResolutionsConfig) => ResolutionIssue;
16
16
  export declare const resolveExactIssuesFilePath: (pathOrDir: string | undefined, label: string) => Promise<string | undefined>;
17
17
  export declare const writeKnownIssues: (store: AllureStore, knownIssuesPath?: string) => Promise<void>;
@@ -1,5 +1,6 @@
1
1
  import { mkdir, stat, writeFile } from "node:fs/promises";
2
2
  import { dirname, extname, resolve } from "node:path";
3
+ import { isIgnoredResolution, parseResolutionExpiresAt } from "@allurereport/core-api";
3
4
  import { isFileNotFoundError } from "./utils/misc.js";
4
5
  export const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
5
6
  const nonEmpty = (value) => typeof value === "string" && value.trim().length > 0;
@@ -82,6 +83,14 @@ const validateIgnoredRule = (rule, path, errors) => {
82
83
  if (!nonEmpty(rule.comment)) {
83
84
  errors.push(`${path}.comment must be a non-empty string`);
84
85
  }
86
+ if (rule.resolution === "accepted" && rule.expiresAt !== undefined) {
87
+ errors.push(`${path}.expiresAt is only supported for muted resolutions`);
88
+ }
89
+ if (rule.resolution === "muted" &&
90
+ rule.expiresAt !== undefined &&
91
+ (!nonEmpty(rule.expiresAt) || parseResolutionExpiresAt(rule.expiresAt) === undefined)) {
92
+ errors.push(`${path}.expiresAt must be a JavaScript-compatible ISO 8601 timestamp with an explicit timezone`);
93
+ }
85
94
  };
86
95
  export const validateResolutionsConfig = (config) => {
87
96
  if (!config) {
@@ -154,7 +163,7 @@ export const getResolutionByRules = (testResult, config) => {
154
163
  }
155
164
  return undefined;
156
165
  };
157
- export const isIgnoredFailure = (testResult) => testResult.resolution === "muted" || testResult.resolution === "accepted";
166
+ export const isIgnoredFailure = (testResult, now = Date.now()) => isIgnoredResolution(testResult, now);
158
167
  export const createResolutionIssue = (rule, config) => {
159
168
  const template = config?.links?.[rule.issue.type];
160
169
  const issue = { ...rule.issue, comment: rule.comment };
@@ -36,7 +36,8 @@ export const testFixtureResultRawToState = (stateData, raw, context) => {
36
36
  };
37
37
  };
38
38
  export const testResultRawToState = (stateData, raw, context) => {
39
- const legacyTestCaseHash = calculateLegacyTestCaseHash(raw);
39
+ const testCaseHash = calculateTestCaseHash(raw.testId, raw.fullName);
40
+ const legacyTestCaseHash = calculateLegacyTestCaseHash(raw, testCaseHash);
40
41
  const allureId = raw.labels
41
42
  ?.find((label) => (label?.name === "ALLURE_ID" || label?.name === "AS_ID") && label.value?.trim())
42
43
  ?.value?.trim();
@@ -45,9 +46,8 @@ export const testResultRawToState = (stateData, raw, context) => {
45
46
  const threadId = findByLabelName(labels, "thread");
46
47
  const name = raw.name || "Unknown test";
47
48
  const parameters = convertParameters(raw.parameters);
48
- const error = convertRawError(raw);
49
49
  const errors = convertTestErrors(raw);
50
- const testCaseHash = calculateTestCaseHash(raw.testId, raw.fullName);
50
+ const error = errors?.[0] ?? convertRawError(raw);
51
51
  const parametersHash = calculateParametersHash(parameters);
52
52
  const testCase = processTestCase({ stateData, raw, allureId, testCaseHash });
53
53
  return {
@@ -60,7 +60,7 @@ export const testResultRawToState = (stateData, raw, context) => {
60
60
  environmentHash: null,
61
61
  retryHash: calculateRetryHash({ testCaseHash, parametersHash }),
62
62
  status: raw.status ?? defaultStatus,
63
- error: errors?.[0] ?? error,
63
+ error,
64
64
  errors,
65
65
  ...processTimings(raw),
66
66
  description: raw.description,
@@ -89,18 +89,12 @@ export const testResultRawToState = (stateData, raw, context) => {
89
89
  titlePath: raw.titlePath ?? [],
90
90
  };
91
91
  };
92
- const calculateLegacyTestCaseHash = (raw) => {
92
+ const calculateLegacyTestCaseHash = (raw, testCaseHash) => {
93
93
  const maybeAllureId = raw.labels?.find((label) => (label?.name === "ALLURE_ID" || label?.name === "AS_ID") && label.value !== "-1")?.value;
94
94
  if (maybeAllureId) {
95
95
  return md5(`ALLURE_ID=${maybeAllureId}`);
96
96
  }
97
- if (raw.testId) {
98
- return md5(raw.testId);
99
- }
100
- if (raw.fullName) {
101
- return md5(raw.fullName);
102
- }
103
- return undefined;
97
+ return testCaseHash ?? undefined;
104
98
  };
105
99
  const processTestCase = ({ stateData: { testCases }, raw, allureId, testCaseHash, }) => {
106
100
  if (testCaseHash) {
@@ -201,7 +195,16 @@ const processTimings = ({ start, stop, duration, }) => {
201
195
  return { start: undefined, stop: undefined, duration };
202
196
  };
203
197
  const convertLabels = (labels) => {
204
- return labels?.filter(notNull)?.map(convertLabel)?.flatMap(processTagLabels).map(normalizeAllureIdLabel) ?? [];
198
+ const convertedLabels = [];
199
+ for (const label of labels ?? []) {
200
+ if (!notNull(label)) {
201
+ continue;
202
+ }
203
+ for (const expandedLabel of processTagLabels(convertLabel(label))) {
204
+ convertedLabels.push(normalizeAllureIdLabel(expandedLabel));
205
+ }
206
+ }
207
+ return convertedLabels;
205
208
  };
206
209
  const convertLabel = (label) => {
207
210
  return {
@@ -210,16 +213,19 @@ const convertLabel = (label) => {
210
213
  };
211
214
  };
212
215
  const convertSteps = (stateData, steps) => {
213
- const convertedStepData = steps?.filter(notNull)?.map((step) => convertStep(stateData, step)) ?? [];
214
- return {
215
- convertedSteps: convertedStepData.map(({ convertedStep }) => convertedStep),
216
- messages: new Set(convertedStepData.reduce((acc, { messages }) => {
217
- if (messages) {
218
- acc.push(...messages);
219
- }
220
- return acc;
221
- }, [])),
222
- };
216
+ const convertedSteps = [];
217
+ const messages = new Set();
218
+ for (const step of steps ?? []) {
219
+ if (!notNull(step)) {
220
+ continue;
221
+ }
222
+ const converted = convertStep(stateData, step);
223
+ convertedSteps.push(converted.convertedStep);
224
+ for (const message of converted.messages ?? []) {
225
+ messages.add(message);
226
+ }
227
+ }
228
+ return { convertedSteps, messages };
223
229
  };
224
230
  const convertStep = (stateData, step) => {
225
231
  if (step.type === "step") {
@@ -251,10 +257,15 @@ const convertStep = (stateData, step) => {
251
257
  },
252
258
  };
253
259
  };
254
- const convertParameters = (parameters) => parameters
255
- ?.filter(notNull)
256
- ?.filter((p) => typeof p.name === "string" && p.name.length > 0)
257
- ?.map(convertParameter) ?? [];
260
+ const convertParameters = (parameters) => {
261
+ const convertedParameters = [];
262
+ for (const parameter of parameters ?? []) {
263
+ if (notNull(parameter) && typeof parameter.name === "string" && parameter.name.length > 0) {
264
+ convertedParameters.push(convertParameter(parameter));
265
+ }
266
+ }
267
+ return convertedParameters;
268
+ };
258
269
  const convertParameter = (param) => ({
259
270
  name: param.name ?? UNKNOWN_PARAMETER_VALUE,
260
271
  value: param.value ?? UNKNOWN_PARAMETER_VALUE,
@@ -262,10 +273,15 @@ const convertParameter = (param) => ({
262
273
  excluded: param.excluded ?? false,
263
274
  masked: param.masked ?? false,
264
275
  });
265
- const convertLinks = (links) => links
266
- ?.filter(notNull)
267
- ?.filter((l) => l.url)
268
- ?.map(convertLink) ?? [];
276
+ const convertLinks = (links) => {
277
+ const convertedLinks = [];
278
+ for (const link of links ?? []) {
279
+ if (notNull(link) && link.url) {
280
+ convertedLinks.push(convertLink(link));
281
+ }
282
+ }
283
+ return convertedLinks;
284
+ };
269
285
  const convertLink = (link) => ({
270
286
  name: link.name,
271
287
  url: link.url ?? UNKNOWN_PARAMETER_VALUE,
@@ -41,7 +41,13 @@ export class RetrySubstore {
41
41
  __classPrivateFieldGet(this, _RetrySubstore_testResultsByRetryHash, "f").set(testResult.retryHash, [testResult]);
42
42
  return;
43
43
  }
44
- results.push(testResult);
44
+ const existingIndex = results.findIndex((attempt) => attempt.id === testResult.id);
45
+ if (existingIndex !== -1) {
46
+ results.splice(existingIndex, 1, testResult);
47
+ }
48
+ else {
49
+ results.push(testResult);
50
+ }
45
51
  results.sort((first, second) => __classPrivateFieldGet(this, _RetrySubstore_instances, "m", _RetrySubstore_compareResults).call(this, first, second));
46
52
  results.forEach((attempt, index) => {
47
53
  attempt.isRetry = index !== 0;
@@ -11,7 +11,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
11
11
  };
12
12
  var _DefaultAllureStore_instances, _DefaultAllureStore_testResults, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_detectFlaky, _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_globalAttachmentIds, _DefaultAllureStore_globalAttachmentIdsByEnv, _DefaultAllureStore_globalErrors, _DefaultAllureStore_globalErrorsByEnv, _DefaultAllureStore_processGlobalAttachmentIds, _DefaultAllureStore_processGlobalAttachmentIdsByEnv, _DefaultAllureStore_processGlobalErrors, _DefaultAllureStore_processGlobalErrorsByEnv, _DefaultAllureStore_globalExitCode, _DefaultAllureStore_checkResultsById, _DefaultAllureStore_qualityGateResults, _DefaultAllureStore_metrics, _DefaultAllureStore_performance, _DefaultAllureStore_historyPoints, _DefaultAllureStore_historyLookup, _DefaultAllureStore_environments, _DefaultAllureStore_mergeEnvironmentIdentity, _DefaultAllureStore_environmentIdForLookup, _DefaultAllureStore_environmentIdByName, _DefaultAllureStore_addEnvironments, _DefaultAllureStore_setTestResultEnvironmentId, _DefaultAllureStore_assertAllowedEnvironmentId, _DefaultAllureStore_assertAllowedStoredEnvironment, _DefaultAllureStore_environmentIdByTestResult, _DefaultAllureStore_assignIdentityHashes, _DefaultAllureStore_classifyResolution, _DefaultAllureStore_associateResolutionIssue, _DefaultAllureStore_removeResolutionIssueAssociation, _DefaultAllureStore_rebuildRetrySubstore, _DefaultAllureStore_rebuildIdentityIndexes, _DefaultAllureStore_rebuildEnvironmentIndexes, _DefaultAllureStore_resolveGlobalEnvironmentIdentity, _DefaultAllureStore_globalAttachmentId, _DefaultAllureStore_processGlobalAttachmentId, _DefaultAllureStore_indexGlobalError, _DefaultAllureStore_addGlobalError, _DefaultAllureStore_indexGlobalAttachment, _DefaultAllureStore_addGlobalAttachment, _DefaultAllureStore_resetProcessGlobals, _DefaultAllureStore_getHistoryLookup, _DefaultAllureStore_historyFor, _DefaultAllureStore_applyHistoryFlags, _DefaultAllureStore_restoreAttachmentContent, _DefaultAllureStore_relinkRestoredAttachmentSteps, _DefaultAllureStore_retriesByTr, _DefaultAllureStore_historyByTr;
13
13
  import { extname } from "node:path";
14
- import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, calculateEnvironmentHash, calculateParametersHash, calculateRetryHash, calculateTestCaseHash, compareBy, createDictionary, fallbackTestCaseIdLabelName, getWorstStatus, createHistoryTestResultLookup, normalizeHistoryDataPoint, ordinal, reverse, resolveMetricSamples, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
14
+ import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, calculateEnvironmentHash, calculateParametersHash, calculateRetryHash, calculateTestCaseHash, compareBy, createDictionary, fallbackTestCaseIdLabelName, getWorstStatus, createHistoryTestResultLookup, hasRetriesStatusChange, normalizeHistoryDataPoint, ordinal, reverse, resolveMetricSamples, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
15
15
  import { md5, } from "@allurereport/plugin-api";
16
16
  import { createResolutionIssue, getResolutionByRules, isIgnoredFailure } from "../resolutions.js";
17
17
  import { environmentIdentityById, normalizeEnvironmentDescriptorMap, resolveEnvironmentIdentity, resolveStoredEnvironmentIdentity, validateAllowedEnvironmentId, } from "../utils/environment.js";
@@ -22,10 +22,11 @@ import { testFixtureResultRawToState, testResultRawToState } from "./convert.js"
22
22
  import { RetrySubstore } from "./retrySubstore.js";
23
23
  const index = (indexMap, key, ...items) => {
24
24
  if (key) {
25
- if (!indexMap.has(key)) {
26
- indexMap.set(key, []);
25
+ let current = indexMap.get(key);
26
+ if (!current) {
27
+ current = [];
28
+ indexMap.set(key, current);
27
29
  }
28
- const current = indexMap.get(key);
29
30
  current.push(...items);
30
31
  }
31
32
  };
@@ -722,7 +723,8 @@ export class DefaultAllureStore {
722
723
  }
723
724
  async blockingFailedTestResults() {
724
725
  const failedTestResults = await this.failedTestResults();
725
- return failedTestResults.filter((tr) => !isIgnoredFailure(tr));
726
+ const resolutionEvaluatedAt = Date.now();
727
+ return failedTestResults.filter((tr) => !isIgnoredFailure(tr, resolutionEvaluatedAt));
726
728
  }
727
729
  async unknownFailedTestResults() {
728
730
  return this.blockingFailedTestResults();
@@ -776,6 +778,9 @@ export class DefaultAllureStore {
776
778
  if (retries.length > 0) {
777
779
  statistic.retries = (statistic.retries ?? 0) + 1;
778
780
  }
781
+ if (hasRetriesStatusChange(tr, retries)) {
782
+ statistic.retriesStatusChange = (statistic.retriesStatusChange ?? 0) + 1;
783
+ }
779
784
  if (tr.flaky) {
780
785
  statistic.flaky = (statistic.flaky ?? 0) + 1;
781
786
  }
@@ -939,6 +944,7 @@ export class DefaultAllureStore {
939
944
  for (const testResult of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()) {
940
945
  delete testResult.resolution;
941
946
  delete testResult.resolutionComment;
947
+ delete testResult.resolutionExpiresAt;
942
948
  }
943
949
  const normalizedEnvironments = environments
944
950
  .map((environmentValue) => {
@@ -989,6 +995,7 @@ export class DefaultAllureStore {
989
995
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
990
996
  delete testResult.resolution;
991
997
  delete testResult.resolutionComment;
998
+ delete testResult.resolutionExpiresAt;
992
999
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
993
1000
  const envId = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
994
1001
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_assertAllowedEnvironmentId).call(this, envId, `restored testResults[${JSON.stringify(testResult.id)}]`);
@@ -1198,6 +1205,10 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments
1198
1205
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
1199
1206
  testResult.resolution = rule.resolution;
1200
1207
  testResult.resolutionComment = rule.comment;
1208
+ delete testResult.resolutionExpiresAt;
1209
+ if (rule.resolution === "muted" && rule.expiresAt !== undefined) {
1210
+ testResult.resolutionExpiresAt = rule.expiresAt;
1211
+ }
1201
1212
  if (rule.resolution === "issue") {
1202
1213
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_associateResolutionIssue).call(this, createResolutionIssue(rule, __classPrivateFieldGet(this, _DefaultAllureStore_resolutionsConfig, "f")), testResult.id);
1203
1214
  }
@@ -1298,13 +1309,11 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments
1298
1309
  }, _DefaultAllureStore_getHistoryLookup = function _DefaultAllureStore_getHistoryLookup() {
1299
1310
  return (__classPrivateFieldSet(this, _DefaultAllureStore_historyLookup, __classPrivateFieldGet(this, _DefaultAllureStore_historyLookup, "f") ?? createHistoryTestResultLookup(__classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()), "f"));
1300
1311
  }, _DefaultAllureStore_historyFor = function _DefaultAllureStore_historyFor(result, lookup) {
1301
- return __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f").flatMap((point) => {
1302
- const historicalResult = lookup(point, result);
1303
- return historicalResult ? [historicalResult] : [];
1304
- });
1312
+ return __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f").map((point) => lookup(point, result));
1305
1313
  }, _DefaultAllureStore_applyHistoryFlags = function _DefaultAllureStore_applyHistoryFlags(result, history) {
1306
1314
  result.flaky = result.sourceMetadata?.reportedFlaky === true || __classPrivateFieldGet(this, _DefaultAllureStore_detectFlaky, "f").call(this, result, history);
1307
- result.transition = getStatusTransition(result, history);
1315
+ const resolvedHistory = history.filter((entry) => entry !== undefined);
1316
+ result.transition = getStatusTransition(result, resolvedHistory);
1308
1317
  }, _DefaultAllureStore_restoreAttachmentContent = function _DefaultAllureStore_restoreAttachmentContent(id, content) {
1309
1318
  const attachment = __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").get(id);
1310
1319
  __classPrivateFieldGet(this, _DefaultAllureStore_attachmentContents, "f").set(id, content);
@@ -1,2 +1,2 @@
1
+ export { md5 } from "@allurereport/plugin-api";
1
2
  export declare const shortHash: () => string;
2
- export declare const md5: (data: string) => string;
@@ -1,3 +1,3 @@
1
- import { randomBytes, createHash } from "node:crypto";
1
+ import { randomBytes } from "node:crypto";
2
+ export { md5 } from "@allurereport/plugin-api";
2
3
  export const shortHash = () => randomBytes(8).toString("hex");
3
- export const md5 = (data) => createHash("md5").update(data).digest("hex");
@@ -1,2 +1,2 @@
1
- import { type FlakyDetectionConfig, type HistoryTestResult, type TestResult } from "@allurereport/core-api";
2
- export declare const createFlakyDetector: ({ historyDepth, includePassedTests, }?: FlakyDetectionConfig) => ((tr: TestResult, history: HistoryTestResult[]) => boolean);
1
+ import { type HistoryTestResult, type FlakyDetectionConfig, type TestResult } from "@allurereport/core-api";
2
+ export declare const createFlakyDetector: ({ includePassedTests, historyDepth, stabilizationPeriod }?: FlakyDetectionConfig) => (tr: TestResult, history: (HistoryTestResult | undefined)[]) => boolean;
@@ -1,40 +1,7 @@
1
- import { DEFAULT_ENVIRONMENT, } from "@allurereport/core-api";
2
- const DEFAULT_HISTORY_DEPTH = 5;
3
- const TRANSITION_HALF_LIFE = 2;
4
- const STABILITY_PRIOR_WEIGHT = 3;
5
- const FLAKY_SCORE_THRESHOLD = 30;
6
- const badStatuses = ["failed", "broken"];
7
- const isFlakyByWeightedTransitions = (tr, history, historyDepth, includePassedTests) => {
8
- const currentPassed = tr.status === "passed";
9
- const isEligibleStatus = badStatuses.includes(tr.status) || (includePassedTests && currentPassed);
10
- if (historyDepth === -1 || !isEligibleStatus) {
1
+ import { getTestFlakiness, } from "@allurereport/core-api";
2
+ export const createFlakyDetector = ({ includePassedTests = false, historyDepth, stabilizationPeriod } = {}) => (tr, history) => {
3
+ if (tr.status !== "failed" && tr.status !== "broken" && !(includePassedTests && tr.status === "passed")) {
11
4
  return false;
12
5
  }
13
- let previousPassed = currentPassed;
14
- let comparisons = 0;
15
- let weightedTransitions = 0;
16
- let totalWeight = 0;
17
- for (const result of history) {
18
- if (historyDepth !== 0 && comparisons >= historyDepth) {
19
- break;
20
- }
21
- if (result.id === tr.id ||
22
- (result.environment ?? DEFAULT_ENVIRONMENT) !== (tr.environment ?? DEFAULT_ENVIRONMENT) ||
23
- (result.status !== "passed" && !badStatuses.includes(result.status))) {
24
- continue;
25
- }
26
- const passed = result.status === "passed";
27
- const weight = 2 ** (-comparisons / TRANSITION_HALF_LIFE);
28
- if (passed !== previousPassed) {
29
- weightedTransitions += weight;
30
- }
31
- totalWeight += weight;
32
- previousPassed = passed;
33
- comparisons++;
34
- }
35
- const score = (100 * weightedTransitions) / (STABILITY_PRIOR_WEIGHT + totalWeight);
36
- return score >= FLAKY_SCORE_THRESHOLD;
37
- };
38
- export const createFlakyDetector = ({ historyDepth = DEFAULT_HISTORY_DEPTH, includePassedTests = false, } = {}) => {
39
- return (tr, history) => isFlakyByWeightedTransitions(tr, history, historyDepth, includePassedTests);
6
+ return getTestFlakiness(tr, history, { historyDepth, stabilizationPeriod }) === true;
40
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.20.1",
3
+ "version": "3.22.0",
4
4
  "description": "Collection of generic Allure utilities used across the entire project",
5
5
  "keywords": [
6
6
  "allure"
@@ -20,33 +20,36 @@
20
20
  "scripts": {
21
21
  "build": "run clean && tsc --project ./tsconfig.json",
22
22
  "clean": "rimraf ./dist",
23
- "test": "vitest run",
24
- "lint": "oxlint --import-plugin src test features stories",
25
- "lint:fix": "oxlint --import-plugin --fix src test features stories"
23
+ "test": "rimraf out && vp test run",
24
+ "test:unit": "yarn test",
25
+ "lint": "vp lint --import-plugin src test features stories",
26
+ "lint:fix": "vp lint --import-plugin --fix src test features stories"
26
27
  },
27
28
  "dependencies": {
28
- "@allurereport/ci": "3.20.1",
29
- "@allurereport/cli-commons": "3.20.1",
30
- "@allurereport/core-api": "3.20.1",
31
- "@allurereport/plugin-agent": "3.20.1",
32
- "@allurereport/plugin-allure2": "3.20.1",
33
- "@allurereport/plugin-api": "3.20.1",
34
- "@allurereport/plugin-awesome": "3.20.1",
35
- "@allurereport/plugin-classic": "3.20.1",
36
- "@allurereport/plugin-csv": "3.20.1",
37
- "@allurereport/plugin-dashboard": "3.20.1",
38
- "@allurereport/plugin-jira": "3.20.1",
39
- "@allurereport/plugin-log": "3.20.1",
40
- "@allurereport/plugin-progress": "3.20.1",
41
- "@allurereport/plugin-slack": "3.20.1",
42
- "@allurereport/plugin-testops": "3.20.1",
43
- "@allurereport/plugin-testplan": "3.20.1",
44
- "@allurereport/reader": "3.20.1",
45
- "@allurereport/reader-api": "3.20.1",
46
- "@allurereport/service": "3.20.1",
47
- "@allurereport/summary": "3.20.1",
29
+ "@allurereport/ci": "3.22.0",
30
+ "@allurereport/cli-commons": "3.22.0",
31
+ "@allurereport/core-api": "3.22.0",
32
+ "@allurereport/plugin-agent": "3.22.0",
33
+ "@allurereport/plugin-allure2": "3.22.0",
34
+ "@allurereport/plugin-api": "3.22.0",
35
+ "@allurereport/plugin-awesome": "3.22.0",
36
+ "@allurereport/plugin-classic": "3.22.0",
37
+ "@allurereport/plugin-csv": "3.22.0",
38
+ "@allurereport/plugin-dashboard": "3.22.0",
39
+ "@allurereport/plugin-jira": "3.22.0",
40
+ "@allurereport/plugin-log": "3.22.0",
41
+ "@allurereport/plugin-metrics": "3.22.0",
42
+ "@allurereport/plugin-progress": "3.22.0",
43
+ "@allurereport/plugin-slack": "3.22.0",
44
+ "@allurereport/plugin-testops": "3.22.0",
45
+ "@allurereport/plugin-testplan": "3.22.0",
46
+ "@allurereport/plugin-xray": "3.22.0",
47
+ "@allurereport/reader": "3.22.0",
48
+ "@allurereport/reader-api": "3.22.0",
49
+ "@allurereport/service": "3.22.0",
50
+ "@allurereport/summary": "3.22.0",
48
51
  "glob": "^13.0.6",
49
- "handlebars": "^4.7.9",
52
+ "handlebars": "4.7.10",
50
53
  "jiti": "^2.5.1",
51
54
  "node-stream-zip": "^1.15.0",
52
55
  "p-limit": "^7.3.0",
@@ -55,15 +58,19 @@
55
58
  "zip-stream": "^7.0.5"
56
59
  },
57
60
  "devDependencies": {
61
+ "@allurereport/common-config": "3.22.0",
58
62
  "@types/node": "^20",
59
63
  "@types/zip-stream": "^7.0.0",
64
+ "@vitest/coverage-istanbul": "5.0.4",
60
65
  "@vitest/runner": "^2",
61
- "@vitest/snapshot": "^5.0.1",
66
+ "@vitest/snapshot": "5.0.4",
62
67
  "allure-js-commons": "^3",
63
68
  "allure-vitest": "^3",
64
69
  "rimraf": "^6",
65
70
  "tslib": "^2.7.0",
66
71
  "typescript": "^5",
67
- "vitest": "^4.1.0"
72
+ "vite": "@voidzero-dev/vite-plus-core@1.2.0",
73
+ "vite-plus": "1.2.0",
74
+ "vitest": "5.0.4"
68
75
  }
69
76
  }