@allurereport/core 3.21.0 → 3.22.1

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.js CHANGED
@@ -190,10 +190,13 @@ const validateFlakyDetectionConfig = (config) => {
190
190
  if (config === undefined) {
191
191
  return;
192
192
  }
193
- const { historyDepth, includePassedTests } = config;
193
+ const { historyDepth, includePassedTests, stabilizationPeriod } = config;
194
194
  if (historyDepth !== undefined && (!Number.isInteger(historyDepth) || historyDepth < -1)) {
195
195
  throw new Error("flakyDetection.historyDepth must be an integer greater than or equal to -1");
196
196
  }
197
+ if (stabilizationPeriod !== undefined && (!Number.isInteger(stabilizationPeriod) || stabilizationPeriod < 1)) {
198
+ throw new Error("flakyDetection.stabilizationPeriod must be a positive integer");
199
+ }
197
200
  if (includePassedTests !== undefined && typeof includePassedTests !== "boolean") {
198
201
  throw new TypeError("flakyDetection.includePassedTests must be a boolean");
199
202
  }
package/dist/history.js CHANGED
@@ -93,16 +93,6 @@ const closeStream = async (stream) => {
93
93
  stream.destroy();
94
94
  });
95
95
  };
96
- const closeFileHandle = async (file) => {
97
- try {
98
- await file.close();
99
- }
100
- catch (err) {
101
- if (err?.code !== "EBADF") {
102
- throw err;
103
- }
104
- }
105
- };
106
96
  export class AllureLocalHistory {
107
97
  constructor(params) {
108
98
  this.params = params;
@@ -221,7 +211,7 @@ export class AllureLocalHistory {
221
211
  }
222
212
  finally {
223
213
  await __classPrivateFieldGet(this, _AllureLocalHistory_closeStreams, "f").call(this, openedStreams);
224
- await closeFileHandle(historyFile);
214
+ await historyFile.close();
225
215
  }
226
216
  }
227
217
  async appendHistory(data) {
@@ -255,7 +245,7 @@ export class AllureLocalHistory {
255
245
  }
256
246
  finally {
257
247
  await __classPrivateFieldGet(this, _AllureLocalHistory_closeStreams, "f").call(this, openedStreams);
258
- await closeFileHandle(historyFile);
248
+ await historyFile.close();
259
249
  if (limit !== 0) {
260
250
  __classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f").push(normalizedData);
261
251
  }
@@ -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_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;
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_failedGenerationPlugins, _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_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";
@@ -137,6 +137,7 @@ export class AllureReport {
137
137
  _AllureReport_publishedRemoteHrefs.set(this, new Set());
138
138
  _AllureReport_artifactFilesByPath.set(this, new Map());
139
139
  _AllureReport_published.set(this, false);
140
+ _AllureReport_failedGenerationPlugins.set(this, new Set());
140
141
  _AllureReport_endGeneratePerfSpan.set(this, void 0);
141
142
  _AllureReport_resolvedReportUrl.set(this, void 0);
142
143
  _AllureReport_reportUrl.set(this, void 0);
@@ -192,12 +193,14 @@ export class AllureReport {
192
193
  if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") !== "done") {
193
194
  throw new Error("report is not completed. Call the done() method first.");
194
195
  }
195
- if (__classPrivateFieldGet(this, _AllureReport_realTime, "f") || !__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")) {
196
+ if (__classPrivateFieldGet(this, _AllureReport_realTime, "f")) {
196
197
  __classPrivateFieldSet(this, _AllureReport_published, true, "f");
197
198
  return;
198
199
  }
199
- const reportsToPublish = (await __classPrivateFieldGet(this, _AllureReport_getReportsToPublish, "f").call(this)).filter((report) => report.publish && Object.keys(report.files).length > 0);
200
- if (reportsToPublish.length === 0) {
200
+ const reportsToPublish = (await __classPrivateFieldGet(this, _AllureReport_getReportsToPublish, "f").call(this)).filter(({ pluginId, publish, files }) => publish &&
201
+ !__classPrivateFieldGet(this, _AllureReport_failedGenerationPlugins, "f").has(pluginId) &&
202
+ Object.keys(files).some((filename) => filename !== "summary.json"));
203
+ if (!__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f") || reportsToPublish.length === 0) {
201
204
  __classPrivateFieldSet(this, _AllureReport_published, true, "f");
202
205
  return;
203
206
  }
@@ -250,12 +253,19 @@ export class AllureReport {
250
253
  onProgress: incrementUploadProgress,
251
254
  });
252
255
  }
253
- const changedPluginIds = __classPrivateFieldGet(this, _AllureReport_applyPublishLinksToSummaries, "f").call(this, linksByPluginId);
254
- summariesMutated = changedPluginIds.size > 0;
255
- if (changedPluginIds.size > 0) {
256
- await __classPrivateFieldGet(this, _AllureReport_writeSummaryFiles, "f").call(this);
257
- await __classPrivateFieldGet(this, _AllureReport_generateRootSummary, "f").call(this);
256
+ for (const { pluginId } of reportsToPublish) {
257
+ const summary = __classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").get(pluginId);
258
+ if (!summary) {
259
+ continue;
260
+ }
261
+ summary.published = true;
262
+ if (linksByPluginId[pluginId]) {
263
+ summary.remoteHref = linksByPluginId[pluginId];
264
+ }
258
265
  }
266
+ summariesMutated = true;
267
+ await __classPrivateFieldGet(this, _AllureReport_writeSummaryFiles, "f").call(this);
268
+ await __classPrivateFieldGet(this, _AllureReport_generateRootSummary, "f").call(this);
259
269
  const updatedReports = await __classPrivateFieldGet(this, _AllureReport_getReportsToPublish, "f").call(this);
260
270
  const updatedReportsByPluginId = new Map(updatedReports.map((report) => [report.pluginId, report]));
261
271
  for (const report of reportsToPublish) {
@@ -410,9 +420,10 @@ export class AllureReport {
410
420
  __classPrivateFieldGet(this, _AllureReport_recordArtifact, "f").call(this, absoluteFilePath, originalFileName);
411
421
  }
412
422
  }
413
- await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, true, async (plugin, context) => {
423
+ const failedPlugins = await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, true, async (plugin, context) => {
414
424
  await plugin.start?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"), __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").subscriber);
415
425
  });
426
+ failedPlugins.forEach((id) => __classPrivateFieldGet(this, _AllureReport_failedGenerationPlugins, "f").add(id));
416
427
  if (__classPrivateFieldGet(this, _AllureReport_realTime, "f")) {
417
428
  await __classPrivateFieldGet(this, _AllureReport_runRealtimeUpdate, "f").call(this);
418
429
  __classPrivateFieldGet(this, _AllureReport_realtimeChannel, "f").onResultLikeChanged(() => {
@@ -804,20 +815,6 @@ export class AllureReport {
804
815
  console.error(errorDetails(err));
805
816
  }
806
817
  });
807
- _AllureReport_applyPublishLinksToSummaries.set(this, (linksByPluginId) => {
808
- const changedPluginIds = new Set();
809
- if (__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").size === 0) {
810
- return changedPluginIds;
811
- }
812
- for (const [pluginId, remoteHref] of Object.entries(linksByPluginId)) {
813
- const summary = __classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").get(pluginId);
814
- if (summary && remoteHref) {
815
- summary.remoteHref = remoteHref;
816
- changedPluginIds.add(pluginId);
817
- }
818
- }
819
- return changedPluginIds;
820
- });
821
818
  _AllureReport_cloneSummariesByPluginId.set(this, () => new Map([...__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f")].map(([pluginId, summary]) => [pluginId, clonePluginSummary(summary)])));
822
819
  _AllureReport_writeSummaryFiles.set(this, async (pluginIds) => {
823
820
  if (__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").size === 0) {
@@ -889,23 +886,30 @@ export class AllureReport {
889
886
  }
890
887
  await __classPrivateFieldGet(this, _AllureReport_resolveReportUrl, "f").call(this);
891
888
  __classPrivateFieldSet(this, _AllureReport_overviewHref, await __classPrivateFieldGet(this, _AllureReport_resolveOverviewHref, "f").call(this), "f");
892
- await measurePerf(PERF_METRIC_NAMES.generatePluginsDone, async () => {
893
- await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
894
- await measurePerf(`${PERF_METRIC_PREFIXES.generatePluginDone}${context.id}`, async () => {
895
- await plugin.done?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
896
- });
889
+ const failedPlugins = await measurePerf(PERF_METRIC_NAMES.generatePluginsDone, () => __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
890
+ await measurePerf(`${PERF_METRIC_PREFIXES.generatePluginDone}${context.id}`, async () => {
891
+ await plugin.done?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
897
892
  });
898
- });
893
+ }));
894
+ failedPlugins.forEach((id) => __classPrivateFieldGet(this, _AllureReport_failedGenerationPlugins, "f").add(id));
899
895
  __classPrivateFieldGet(this, _AllureReport_finishGeneratePerfSpan, "f").call(this);
900
896
  await __classPrivateFieldGet(this, _AllureReport_ingestSelfPerfMetrics, "f").call(this);
897
+ const ciContext = createReportCiContext(__classPrivateFieldGet(this, _AllureReport_ci, "f"));
901
898
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
902
899
  const summary = await plugin?.info?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
903
900
  if (!summary) {
904
901
  return;
905
902
  }
903
+ if (context.publish && !__classPrivateFieldGet(this, _AllureReport_realTime, "f")) {
904
+ summary.published = false;
905
+ }
906
+ else {
907
+ delete summary.published;
908
+ }
906
909
  summary.pluginId = context.id;
907
910
  summary.pullRequestHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestUrl;
908
911
  summary.jobHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobRunUrl;
912
+ Object.assign(summary, ciContext);
909
913
  summaries.push({
910
914
  ...summary,
911
915
  href: `${context.id}/`,
@@ -926,6 +930,11 @@ export class AllureReport {
926
930
  const shouldFlattenOutput = reportDirectoryEntries.length === 1 && __classPrivateFieldGet(this, _AllureReport_plugins, "f").some(({ id }) => id === reportDirectoryEntries[0].file);
927
931
  __classPrivateFieldSet(this, _AllureReport_historyDataPoint, await __classPrivateFieldGet(this, _AllureReport_createHistoryDataPoint, "f").call(this, shouldFlattenOutput), "f");
928
932
  await __classPrivateFieldGet(this, _AllureReport_publish, "f").call(this);
933
+ const unpublished = [...__classPrivateFieldGet(this, _AllureReport_summariesByPluginId, "f").values()].filter((summary) => summary.published === false);
934
+ if (unpublished.length > 0) {
935
+ console.warn("Expected reports were not published:");
936
+ unpublished.forEach(({ pluginId }) => console.warn(`- ${pluginId}`));
937
+ }
929
938
  if (__classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f")) {
930
939
  await writeKnownIssues(__classPrivateFieldGet(this, _AllureReport_store, "f"), __classPrivateFieldGet(this, _AllureReport_knownIssuesPath, "f"));
931
940
  }
@@ -996,12 +1005,14 @@ export class AllureReport {
996
1005
  if (initState) {
997
1006
  __classPrivateFieldSet(this, _AllureReport_state, {}, "f");
998
1007
  }
1008
+ const failed = new Set();
999
1009
  for (const { enabled, id, plugin, options } of __classPrivateFieldGet(this, _AllureReport_plugins, "f")) {
1000
1010
  if (!enabled) {
1001
1011
  continue;
1002
1012
  }
1003
1013
  const pluginState = __classPrivateFieldGet(this, _AllureReport_instances, "m", _AllureReport_getPluginState).call(this, initState, id);
1004
1014
  if (!pluginState) {
1015
+ failed.add(id);
1005
1016
  console.error("plugin error: state is empty");
1006
1017
  continue;
1007
1018
  }
@@ -1056,11 +1067,13 @@ export class AllureReport {
1056
1067
  }
1057
1068
  }
1058
1069
  catch (e) {
1070
+ failed.add(id);
1059
1071
  console.error(`plugin ${id} error`, e);
1060
1072
  }
1061
1073
  }
1074
+ return failed;
1062
1075
  });
1063
- 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;
1076
+ 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;
1064
1077
  const allureServiceAccessToken = allureService?.accessToken;
1065
1078
  if (allureServiceAccessToken) {
1066
1079
  const allureServiceClientConfig = {
@@ -1147,6 +1160,6 @@ export class AllureReport {
1147
1160
  __classPrivateFieldSet(this, _AllureReport_reportUrl, url, "f");
1148
1161
  }
1149
1162
  }
1150
- _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) {
1163
+ _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_failedGenerationPlugins = 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_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) {
1151
1164
  return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
1152
1165
  };
@@ -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 };
@@ -723,7 +723,8 @@ export class DefaultAllureStore {
723
723
  }
724
724
  async blockingFailedTestResults() {
725
725
  const failedTestResults = await this.failedTestResults();
726
- return failedTestResults.filter((tr) => !isIgnoredFailure(tr));
726
+ const resolutionEvaluatedAt = Date.now();
727
+ return failedTestResults.filter((tr) => !isIgnoredFailure(tr, resolutionEvaluatedAt));
727
728
  }
728
729
  async unknownFailedTestResults() {
729
730
  return this.blockingFailedTestResults();
@@ -943,6 +944,7 @@ export class DefaultAllureStore {
943
944
  for (const testResult of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()) {
944
945
  delete testResult.resolution;
945
946
  delete testResult.resolutionComment;
947
+ delete testResult.resolutionExpiresAt;
946
948
  }
947
949
  const normalizedEnvironments = environments
948
950
  .map((environmentValue) => {
@@ -993,6 +995,7 @@ export class DefaultAllureStore {
993
995
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
994
996
  delete testResult.resolution;
995
997
  delete testResult.resolutionComment;
998
+ delete testResult.resolutionExpiresAt;
996
999
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
997
1000
  const envId = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
998
1001
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_assertAllowedEnvironmentId).call(this, envId, `restored testResults[${JSON.stringify(testResult.id)}]`);
@@ -1202,6 +1205,10 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments
1202
1205
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
1203
1206
  testResult.resolution = rule.resolution;
1204
1207
  testResult.resolutionComment = rule.comment;
1208
+ delete testResult.resolutionExpiresAt;
1209
+ if (rule.resolution === "muted" && rule.expiresAt !== undefined) {
1210
+ testResult.resolutionExpiresAt = rule.expiresAt;
1211
+ }
1205
1212
  if (rule.resolution === "issue") {
1206
1213
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_associateResolutionIssue).call(this, createResolutionIssue(rule, __classPrivateFieldGet(this, _DefaultAllureStore_resolutionsConfig, "f")), testResult.id);
1207
1214
  }
@@ -1302,13 +1309,11 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments
1302
1309
  }, _DefaultAllureStore_getHistoryLookup = function _DefaultAllureStore_getHistoryLookup() {
1303
1310
  return (__classPrivateFieldSet(this, _DefaultAllureStore_historyLookup, __classPrivateFieldGet(this, _DefaultAllureStore_historyLookup, "f") ?? createHistoryTestResultLookup(__classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()), "f"));
1304
1311
  }, _DefaultAllureStore_historyFor = function _DefaultAllureStore_historyFor(result, lookup) {
1305
- return __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f").flatMap((point) => {
1306
- const historicalResult = lookup(point, result);
1307
- return historicalResult ? [historicalResult] : [];
1308
- });
1312
+ return __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f").map((point) => lookup(point, result));
1309
1313
  }, _DefaultAllureStore_applyHistoryFlags = function _DefaultAllureStore_applyHistoryFlags(result, history) {
1310
1314
  result.flaky = result.sourceMetadata?.reportedFlaky === true || __classPrivateFieldGet(this, _DefaultAllureStore_detectFlaky, "f").call(this, result, history);
1311
- result.transition = getStatusTransition(result, history);
1315
+ const resolvedHistory = history.filter((entry) => entry !== undefined);
1316
+ result.transition = getStatusTransition(result, resolvedHistory);
1312
1317
  }, _DefaultAllureStore_restoreAttachmentContent = function _DefaultAllureStore_restoreAttachmentContent(id, content) {
1313
1318
  const attachment = __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").get(id);
1314
1319
  __classPrivateFieldGet(this, _DefaultAllureStore_attachmentContents, "f").set(id, content);
@@ -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.21.0",
3
+ "version": "3.22.1",
4
4
  "description": "Collection of generic Allure utilities used across the entire project",
5
5
  "keywords": [
6
6
  "allure"
@@ -20,34 +20,34 @@
20
20
  "scripts": {
21
21
  "build": "run clean && tsc --project ./tsconfig.json",
22
22
  "clean": "rimraf ./dist",
23
- "test": "vitest run",
23
+ "test": "rimraf out && vp test run",
24
24
  "test:unit": "yarn test",
25
- "lint": "oxlint --import-plugin src test features stories",
26
- "lint:fix": "oxlint --import-plugin --fix src test features stories"
25
+ "lint": "vp lint --import-plugin src test features stories",
26
+ "lint:fix": "vp lint --import-plugin --fix src test features stories"
27
27
  },
28
28
  "dependencies": {
29
- "@allurereport/ci": "3.21.0",
30
- "@allurereport/cli-commons": "3.21.0",
31
- "@allurereport/core-api": "3.21.0",
32
- "@allurereport/plugin-agent": "3.21.0",
33
- "@allurereport/plugin-allure2": "3.21.0",
34
- "@allurereport/plugin-api": "3.21.0",
35
- "@allurereport/plugin-awesome": "3.21.0",
36
- "@allurereport/plugin-classic": "3.21.0",
37
- "@allurereport/plugin-csv": "3.21.0",
38
- "@allurereport/plugin-dashboard": "3.21.0",
39
- "@allurereport/plugin-jira": "3.21.0",
40
- "@allurereport/plugin-log": "3.21.0",
41
- "@allurereport/plugin-metrics": "3.21.0",
42
- "@allurereport/plugin-progress": "3.21.0",
43
- "@allurereport/plugin-slack": "3.21.0",
44
- "@allurereport/plugin-testops": "3.21.0",
45
- "@allurereport/plugin-testplan": "3.21.0",
46
- "@allurereport/plugin-xray": "3.21.0",
47
- "@allurereport/reader": "3.21.0",
48
- "@allurereport/reader-api": "3.21.0",
49
- "@allurereport/service": "3.21.0",
50
- "@allurereport/summary": "3.21.0",
29
+ "@allurereport/ci": "3.22.1",
30
+ "@allurereport/cli-commons": "3.22.1",
31
+ "@allurereport/core-api": "3.22.1",
32
+ "@allurereport/plugin-agent": "3.22.1",
33
+ "@allurereport/plugin-allure2": "3.22.1",
34
+ "@allurereport/plugin-api": "3.22.1",
35
+ "@allurereport/plugin-awesome": "3.22.1",
36
+ "@allurereport/plugin-classic": "3.22.1",
37
+ "@allurereport/plugin-csv": "3.22.1",
38
+ "@allurereport/plugin-dashboard": "3.22.1",
39
+ "@allurereport/plugin-jira": "3.22.1",
40
+ "@allurereport/plugin-log": "3.22.1",
41
+ "@allurereport/plugin-metrics": "3.22.1",
42
+ "@allurereport/plugin-progress": "3.22.1",
43
+ "@allurereport/plugin-slack": "3.22.1",
44
+ "@allurereport/plugin-testops": "3.22.1",
45
+ "@allurereport/plugin-testplan": "3.22.1",
46
+ "@allurereport/plugin-xray": "3.22.1",
47
+ "@allurereport/reader": "3.22.1",
48
+ "@allurereport/reader-api": "3.22.1",
49
+ "@allurereport/service": "3.22.1",
50
+ "@allurereport/summary": "3.22.1",
51
51
  "glob": "^13.0.6",
52
52
  "handlebars": "4.7.10",
53
53
  "jiti": "^2.5.1",
@@ -58,17 +58,19 @@
58
58
  "zip-stream": "^7.0.5"
59
59
  },
60
60
  "devDependencies": {
61
- "@allurereport/test-config": "3.21.0",
61
+ "@allurereport/common-config": "3.22.1",
62
62
  "@types/node": "^20",
63
63
  "@types/zip-stream": "^7.0.0",
64
- "@vitest/coverage-istanbul": "^4.1.0",
64
+ "@vitest/coverage-istanbul": "5.0.4",
65
65
  "@vitest/runner": "^2",
66
- "@vitest/snapshot": "^5.0.1",
66
+ "@vitest/snapshot": "5.0.4",
67
67
  "allure-js-commons": "^3",
68
68
  "allure-vitest": "^3",
69
69
  "rimraf": "^6",
70
70
  "tslib": "^2.7.0",
71
71
  "typescript": "^5",
72
- "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"
73
75
  }
74
76
  }