@allurereport/core 3.18.0 → 3.19.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.js CHANGED
@@ -91,6 +91,7 @@ export const validateConfig = (config) => {
91
91
  "historyPath",
92
92
  "historyBaseUrl",
93
93
  "historyLimit",
94
+ "flakyDetection",
94
95
  "resolutions",
95
96
  "plugins",
96
97
  "defaultLabels",
@@ -184,12 +185,25 @@ const resolveConfigEnvironments = (config) => {
184
185
  allowedEnvironments,
185
186
  };
186
187
  };
188
+ const validateFlakyDetectionConfig = (config) => {
189
+ if (config === undefined) {
190
+ return;
191
+ }
192
+ const { historyDepth, includePassedTests } = config;
193
+ if (historyDepth !== undefined && (!Number.isInteger(historyDepth) || historyDepth < -1)) {
194
+ throw new Error("flakyDetection.historyDepth must be an integer greater than or equal to -1");
195
+ }
196
+ if (includePassedTests !== undefined && typeof includePassedTests !== "boolean") {
197
+ throw new TypeError("flakyDetection.includePassedTests must be a boolean");
198
+ }
199
+ };
187
200
  export const resolveConfig = async (config, override = {}) => {
188
201
  const validationResult = validateConfig(config);
189
202
  if (!validationResult.valid) {
190
203
  throw new Error(`The provided Allure config contains unsupported fields: ${validationResult.fields.join(", ")}`);
191
204
  }
192
205
  validateResolutionsConfig(config.resolutions);
206
+ validateFlakyDetectionConfig(config.flakyDetection);
193
207
  const { environments, environment, allowedEnvironments } = resolveConfigEnvironments(config);
194
208
  const name = override.name ?? config.name ?? "Allure Report";
195
209
  const open = override.open ?? config.open ?? false;
@@ -246,6 +260,7 @@ export const resolveConfig = async (config, override = {}) => {
246
260
  environments,
247
261
  appendHistory,
248
262
  historyLimit,
263
+ flakyDetection: config.flakyDetection,
249
264
  historyPath: historyPath ? resolve(historyPath) : undefined,
250
265
  historyBaseUrl,
251
266
  dump,
package/dist/history.js CHANGED
@@ -19,8 +19,8 @@ import { normalizeHistoryDataPointUrls, } from "@allurereport/core-api";
19
19
  import { isFileNotFoundError } from "./utils/misc.js";
20
20
  const createHistoryItems = (testResults, remoteUrl) => {
21
21
  return testResults
22
- .filter((tr) => tr.historyId)
23
- .map(({ id, name, fullName, environment, historyId, status, error: { message, trace } = {}, start, stop, duration, labels, }) => {
22
+ .filter((tr) => tr.retryHash)
23
+ .map(({ id, name, fullName, environment, retryHash, status, error: { message, trace } = {}, start, stop, duration, labels, }) => {
24
24
  return {
25
25
  id,
26
26
  name,
@@ -34,12 +34,12 @@ const createHistoryItems = (testResults, remoteUrl) => {
34
34
  duration,
35
35
  labels,
36
36
  url: remoteUrl,
37
- historyId: historyId,
37
+ retryHash: retryHash,
38
38
  reportLinks: [],
39
39
  };
40
40
  })
41
41
  .reduce((acc, item) => {
42
- acc[item.historyId] = item;
42
+ acc[item.retryHash] = item;
43
43
  return acc;
44
44
  }, {});
45
45
  };
@@ -228,7 +228,9 @@ export class AllureLocalHistory {
228
228
  }
229
229
  finally {
230
230
  const closing = historyFile.close();
231
- dst?.destroy();
231
+ if (historyFile.fd !== -1) {
232
+ dst?.destroy();
233
+ }
232
234
  await closing;
233
235
  if (limit !== 0) {
234
236
  __classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f").push(normalizedData);
@@ -71,7 +71,7 @@ export class QualityGate {
71
71
  }
72
72
  trsToValidateById.set(tr.id, tr);
73
73
  }
74
- const trsToValidate = trsToValidateById.values().toArray();
74
+ const trsToValidate = [...trsToValidateById.values()];
75
75
  const { rules, use = [...qualityGateDefaultRules] } = this.config;
76
76
  const results = [];
77
77
  let fastFailed = false;
@@ -41,12 +41,17 @@ const metricActual = (metrics, key, value) => {
41
41
  ...(sample?.unit ? { unit: sample.unit } : {}),
42
42
  };
43
43
  };
44
+ const formatQualityGateNumber = (value) => {
45
+ if (!Number.isFinite(value)) {
46
+ return String(value);
47
+ }
48
+ return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(3)));
49
+ };
44
50
  const formatMetricValue = (value, unit) => {
45
51
  if (!Number.isFinite(value)) {
46
52
  return "n/a";
47
53
  }
48
- const finiteValue = value;
49
- const formatted = Number.isInteger(finiteValue) ? String(finiteValue) : String(Number(finiteValue.toFixed(3)));
54
+ const formatted = formatQualityGateNumber(value);
50
55
  return unit ? `${formatted} ${unit}` : formatted;
51
56
  };
52
57
  const metricRuleTitle = (actual) => actual.title ?? actual.key;
@@ -103,8 +108,8 @@ export const minTestsCountRule = {
103
108
  };
104
109
  export const successRateRule = {
105
110
  rule: "successRate",
106
- message: ({ actual, expected }) => `Success rate ${bold(String(actual))} is less, than expected ${bold(String(expected))}`,
107
- successMessage: ({ actual, expected }) => `Success rate ${bold(String(actual))} is not less, than expected ${bold(String(expected))}`,
111
+ message: ({ actual, expected }) => `Success rate ${bold(formatQualityGateNumber(actual))} is less, than expected ${bold(formatQualityGateNumber(expected))}`,
112
+ successMessage: ({ actual, expected }) => `Success rate ${bold(formatQualityGateNumber(actual))} is not less, than expected ${bold(formatQualityGateNumber(expected))}`,
108
113
  validate: async ({ trs, expected, state }) => {
109
114
  const previous = successRateStateValue(state.getResult());
110
115
  const eligibleTrs = trs.filter(filterIncludedInSuccessRate);
package/dist/report.js CHANGED
@@ -299,6 +299,7 @@ export class AllureReport {
299
299
  }
300
300
  const resultsDirPath = resolve(resultsDir);
301
301
  if (await readXcResultBundle(__classPrivateFieldGet(this, _AllureReport_store, "f"), resultsDirPath)) {
302
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
302
303
  return;
303
304
  }
304
305
  try {
@@ -319,12 +320,14 @@ export class AllureReport {
319
320
  catch (e) {
320
321
  console.error("can't read directory", e);
321
322
  }
323
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
322
324
  });
323
325
  this.readFile = async (resultsFile) => measurePerf(PERF_METRIC_NAMES.generateReadResults, async () => {
324
326
  if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") !== "running") {
325
327
  throw new Error(INIT_REQUIRED_ERROR_MESSAGE);
326
328
  }
327
329
  await this.readResult(new PathResultFile(resultsFile));
330
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
328
331
  });
329
332
  this.readResult = async (data) => {
330
333
  if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") !== "running") {
@@ -345,6 +348,9 @@ export class AllureReport {
345
348
  };
346
349
  this.validate = async (params) => {
347
350
  const { trs, state, environment } = params;
351
+ if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") !== "done") {
352
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags(trs.filter(Boolean));
353
+ }
348
354
  const qualityGateEnvironment = environment === undefined
349
355
  ? undefined
350
356
  : (environmentIdentityById(__classPrivateFieldGet(this, _AllureReport_environments, "f"), environment)?.name ??
@@ -370,6 +376,7 @@ export class AllureReport {
370
376
  if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") === "done") {
371
377
  throw new Error("the report is already stopped, the restart isn't supported at the moment");
372
378
  }
379
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
373
380
  __classPrivateFieldSet(this, _AllureReport_executionStage, "running", "f");
374
381
  __classPrivateFieldSet(this, _AllureReport_endGeneratePerfSpan, startPerfSpan(PERF_METRIC_NAMES.generateTotal), "f");
375
382
  if (__classPrivateFieldGet(this, _AllureReport_globalAttachments, "f")?.length) {
@@ -399,6 +406,7 @@ export class AllureReport {
399
406
  if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") !== "running") {
400
407
  return;
401
408
  }
409
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
402
410
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
403
411
  await plugin.update?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
404
412
  });
@@ -449,7 +457,7 @@ export class AllureReport {
449
457
  const addJsonDumpEntry = async (entryName, value) => {
450
458
  await addRequiredDumpEntry(Buffer.from(JSON.stringify(value)), entryName);
451
459
  };
452
- const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], metrics = [], }) => [
460
+ const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByRetryHash = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], metrics = [], }) => [
453
461
  [AllureStoreDumpFiles.TestResults, testResults],
454
462
  [AllureStoreDumpFiles.TestCases, testCases],
455
463
  [AllureStoreDumpFiles.Fixtures, fixtures],
@@ -461,7 +469,7 @@ export class AllureReport {
461
469
  [AllureStoreDumpFiles.GlobalAttachments, globalAttachmentIds],
462
470
  [AllureStoreDumpFiles.GlobalErrors, globalErrors],
463
471
  [AllureStoreDumpFiles.IndexAttachmentsByTestResults, indexAttachmentByTestResult],
464
- [AllureStoreDumpFiles.IndexTestResultsByHistoryId, indexTestResultByHistoryId],
472
+ [AllureStoreDumpFiles.IndexTestResultsByRetryHash, indexTestResultByRetryHash],
465
473
  [AllureStoreDumpFiles.IndexTestResultsByTestCase, indexTestResultByTestCase],
466
474
  [AllureStoreDumpFiles.IndexTestResultsByResolutionIssue, indexTestResultByResolutionIssue],
467
475
  [AllureStoreDumpFiles.IndexAttachmentsByFixture, indexAttachmentByFixture],
@@ -537,6 +545,7 @@ export class AllureReport {
537
545
  this.restoreState = async (dumps) => {
538
546
  __classPrivateFieldGet(this, _AllureReport_store, "f").resetIngestOrder();
539
547
  await __classPrivateFieldGet(this, _AllureReport_restoreStateDumps, "f").call(this, dumps.map((path) => ({ artifactPath: path, path })));
548
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
540
549
  };
541
550
  _AllureReport_recordArtifact.set(this, (filePath, name = basename(filePath)) => {
542
551
  const artifact = createReportArtifact(filePath, __classPrivateFieldGet(this, _AllureReport_getCwd, "f").call(this), name);
@@ -607,8 +616,6 @@ export class AllureReport {
607
616
  const globalAttachmentsEntry = await requiredEntryData(AllureStoreDumpFiles.GlobalAttachments);
608
617
  const globalErrorsEntry = await requiredEntryData(AllureStoreDumpFiles.GlobalErrors);
609
618
  const indexAttachmentsEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByTestResults);
610
- const indexTestResultsByHistoryId = await requiredEntryData(AllureStoreDumpFiles.IndexTestResultsByHistoryId);
611
- const indexTestResultsByTestCaseEntry = await requiredEntryData(AllureStoreDumpFiles.IndexTestResultsByTestCase);
612
619
  const indexTestResultsByResolutionIssueEntry = await optionalEntryData(AllureStoreDumpFiles.IndexTestResultsByResolutionIssue);
613
620
  const indexAttachmentsByFixtureEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByFixture);
614
621
  const indexFixturesByTestResultEntry = await requiredEntryData(AllureStoreDumpFiles.IndexFixturesByTestResult);
@@ -630,7 +637,7 @@ export class AllureReport {
630
637
  case AllureStoreDumpFiles.GlobalAttachments:
631
638
  case AllureStoreDumpFiles.GlobalErrors:
632
639
  case AllureStoreDumpFiles.IndexAttachmentsByTestResults:
633
- case AllureStoreDumpFiles.IndexTestResultsByHistoryId:
640
+ case AllureStoreDumpFiles.IndexTestResultsByRetryHash:
634
641
  case AllureStoreDumpFiles.IndexTestResultsByTestCase:
635
642
  case AllureStoreDumpFiles.IndexTestResultsByResolutionIssue:
636
643
  case AllureStoreDumpFiles.IndexAttachmentsByFixture:
@@ -659,8 +666,8 @@ export class AllureReport {
659
666
  globalAttachmentIds: JSON.parse(globalAttachmentsEntry.toString("utf8")),
660
667
  globalErrors: JSON.parse(globalErrorsEntry.toString("utf8")),
661
668
  indexAttachmentByTestResult: JSON.parse(indexAttachmentsEntry.toString("utf8")),
662
- indexTestResultByHistoryId: JSON.parse(indexTestResultsByHistoryId.toString("utf8")),
663
- indexTestResultByTestCase: JSON.parse(indexTestResultsByTestCaseEntry.toString("utf8")),
669
+ indexTestResultByRetryHash: {},
670
+ indexTestResultByTestCase: {},
664
671
  indexTestResultByResolutionIssue: indexTestResultsByResolutionIssueEntry
665
672
  ? JSON.parse(indexTestResultsByResolutionIssueEntry.toString("utf8"))
666
673
  : {},
@@ -840,6 +847,7 @@ export class AllureReport {
840
847
  console.error("realtime update failed during shutdown", e);
841
848
  }
842
849
  __classPrivateFieldSet(this, _AllureReport_executionStage, "done", "f");
850
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
843
851
  if (__classPrivateFieldGet(this, _AllureReport_dump, "f")) {
844
852
  await this.dumpState();
845
853
  return;
@@ -1005,7 +1013,7 @@ export class AllureReport {
1005
1013
  }
1006
1014
  }
1007
1015
  });
1008
- const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyBaseUrl, historyLimit, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, performance, dump, categories, allureService, globalAttachments, cwd, } = opts;
1016
+ const { name, readers = [allure1, allure2, cucumberjson, 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;
1009
1017
  const allureServiceAccessToken = allureService?.accessToken;
1010
1018
  if (allureServiceAccessToken) {
1011
1019
  const allureServiceClientConfig = {
@@ -1024,7 +1032,11 @@ export class AllureReport {
1024
1032
  __classPrivateFieldSet(this, _AllureReport_ci, detect(), "f");
1025
1033
  const reportTitleSuffix = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestName ?? __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobRunName;
1026
1034
  this.reportName = [name, reportTitleSuffix].filter(Boolean).join(" – ");
1027
- __classPrivateFieldSet(this, _AllureReport_realtimeChannel, new RealtimeChannel(), "f");
1035
+ __classPrivateFieldSet(this, _AllureReport_realtimeChannel, new RealtimeChannel(() => {
1036
+ if (__classPrivateFieldGet(this, _AllureReport_executionStage, "f") === "running") {
1037
+ __classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
1038
+ }
1039
+ }), "f");
1028
1040
  __classPrivateFieldSet(this, _AllureReport_realtimeUpdateScheduler, new RealtimeUpdateScheduler(__classPrivateFieldGet(this, _AllureReport_runRealtimeUpdate, "f")), "f");
1029
1041
  __classPrivateFieldSet(this, _AllureReport_realTime, realTime, "f");
1030
1042
  __classPrivateFieldSet(this, _AllureReport_dump, dump, "f");
@@ -1057,6 +1069,7 @@ export class AllureReport {
1057
1069
  reportVariables: variables,
1058
1070
  environmentsConfig: environments,
1059
1071
  history: __classPrivateFieldGet(this, _AllureReport_history, "f"),
1072
+ flakyDetection,
1060
1073
  resolutionsConfig: resolutions,
1061
1074
  defaultLabels,
1062
1075
  environment,
@@ -1,6 +1,5 @@
1
1
  import type { AttachmentLink, TestCase, TestFixtureResult, TestResult } from "@allurereport/core-api";
2
2
  import type { RawFixtureResult, RawTestResult, ReaderContext } from "@allurereport/reader-api";
3
- export declare const __unknown = "#___unknown_value___#";
4
3
  export type StateData = {
5
4
  testCases: Map<string, TestCase>;
6
5
  attachments: Map<string, AttachmentLink>;
@@ -1,9 +1,19 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { findByLabelName, notNull } from "@allurereport/core-api";
2
+ import { UNKNOWN_PARAMETER_VALUE, calculateParametersHash, calculateRetryHash, calculateTestCaseHash, findByLabelName, notNull, } from "@allurereport/core-api";
3
3
  import { md5 } from "@allurereport/plugin-api";
4
4
  import { extension, lookupContentType } from "@allurereport/reader-api";
5
5
  const defaultStatus = "unknown";
6
- export const __unknown = "#___unknown_value___#";
6
+ const convertRawError = (raw) => ({
7
+ message: raw.message,
8
+ trace: raw.trace,
9
+ ...(raw.actual && raw.expected ? { expected: raw.expected, actual: raw.actual } : {}),
10
+ });
11
+ const convertTestErrors = (raw) => {
12
+ const errors = raw.errors?.filter(notNull);
13
+ if (errors?.length) {
14
+ return errors.map(convertRawError);
15
+ }
16
+ };
7
17
  export const testFixtureResultRawToState = (stateData, raw, context) => {
8
18
  const name = raw.name || "Unknown test";
9
19
  return {
@@ -26,24 +36,32 @@ export const testFixtureResultRawToState = (stateData, raw, context) => {
26
36
  };
27
37
  };
28
38
  export const testResultRawToState = (stateData, raw, context) => {
39
+ const legacyTestCaseHash = calculateLegacyTestCaseHash(raw);
40
+ const allureId = raw.labels
41
+ ?.find((label) => (label?.name === "ALLURE_ID" || label?.name === "AS_ID") && label.value?.trim())
42
+ ?.value?.trim();
29
43
  const labels = convertLabels(raw.labels);
30
44
  const hostId = findByLabelName(labels, "host");
31
45
  const threadId = findByLabelName(labels, "thread");
32
46
  const name = raw.name || "Unknown test";
33
- const testCase = processTestCase(stateData, raw);
34
47
  const parameters = convertParameters(raw.parameters);
48
+ const error = convertRawError(raw);
49
+ const errors = convertTestErrors(raw);
50
+ const testCaseHash = calculateTestCaseHash(raw.testId, raw.fullName);
51
+ const parametersHash = calculateParametersHash(parameters);
52
+ const testCase = processTestCase({ stateData, raw, allureId, testCaseHash });
35
53
  return {
36
54
  id: md5(raw.uuid || randomUUID()),
37
55
  name,
38
56
  testCase,
39
57
  fullName: raw.fullName,
40
- historyId: calculateHistoryId(testCase, parameters),
58
+ testCaseHash,
59
+ parametersHash,
60
+ environmentHash: null,
61
+ retryHash: calculateRetryHash({ testCaseHash, parametersHash }),
41
62
  status: raw.status ?? defaultStatus,
42
- error: {
43
- message: raw.message,
44
- trace: raw.trace,
45
- ...(raw.actual && raw.expected ? { expected: raw.expected, actual: raw.actual } : {}),
46
- },
63
+ error: errors?.[0] ?? error,
64
+ errors,
47
65
  ...processTimings(raw),
48
66
  description: raw.description,
49
67
  descriptionHtml: raw.descriptionHtml,
@@ -64,25 +82,43 @@ export const testResultRawToState = (stateData, raw, context) => {
64
82
  sourceMetadata: {
65
83
  readerId: context.readerId,
66
84
  metadata: context.metadata ?? {},
85
+ legacyHistoryId: raw.historyId?.length ? raw.historyId : undefined,
86
+ reportedFlaky: raw.flaky ?? false,
87
+ legacyTestCaseHash,
67
88
  },
68
89
  titlePath: raw.titlePath ?? [],
69
90
  };
70
91
  };
71
- const processTestCase = ({ testCases }, raw) => {
72
- const [id, allureId] = calculateTestId(raw);
73
- if (id) {
74
- const maybeTestCase = testCases.get(id);
92
+ const calculateLegacyTestCaseHash = (raw) => {
93
+ const maybeAllureId = raw.labels?.find((label) => (label?.name === "ALLURE_ID" || label?.name === "AS_ID") && label.value !== "-1")?.value;
94
+ if (maybeAllureId) {
95
+ return md5(`ALLURE_ID=${maybeAllureId}`);
96
+ }
97
+ if (raw.testId) {
98
+ return md5(raw.testId);
99
+ }
100
+ if (raw.fullName) {
101
+ return md5(raw.fullName);
102
+ }
103
+ return undefined;
104
+ };
105
+ const processTestCase = ({ stateData: { testCases }, raw, allureId, testCaseHash, }) => {
106
+ if (testCaseHash) {
107
+ const maybeTestCase = testCases.get(testCaseHash);
75
108
  if (maybeTestCase) {
109
+ if (!maybeTestCase.allureId && allureId) {
110
+ maybeTestCase.allureId = allureId;
111
+ }
76
112
  return maybeTestCase;
77
113
  }
78
114
  const testCase = {
79
- id,
115
+ id: testCaseHash,
80
116
  allureId,
81
117
  externalId: raw.testId,
82
- name: raw.testCaseName ?? raw.name ?? __unknown,
118
+ name: raw.testCaseName ?? raw.name ?? UNKNOWN_PARAMETER_VALUE,
83
119
  fullName: raw.fullName,
84
120
  };
85
- testCases.set(id, testCase);
121
+ testCases.set(testCaseHash, testCase);
86
122
  return testCase;
87
123
  }
88
124
  return undefined;
@@ -100,7 +136,7 @@ const processAttachmentLink = ({ attachments, visitAttachmentLink }, attach) =>
100
136
  originalFileName: undefined,
101
137
  contentType: attach.contentType,
102
138
  ext: "",
103
- name: attach.name ?? __unknown,
139
+ name: attach.name ?? UNKNOWN_PARAMETER_VALUE,
104
140
  missed: true,
105
141
  used: true,
106
142
  });
@@ -128,7 +164,7 @@ const processAttachmentLink = ({ attachments, visitAttachmentLink }, attach) =>
128
164
  id: randomUUID(),
129
165
  originalFileName: undefined,
130
166
  ext: "",
131
- name: attach.name ?? attach.originalFileName ?? __unknown,
167
+ name: attach.name ?? attach.originalFileName ?? UNKNOWN_PARAMETER_VALUE,
132
168
  contentType: attach.contentType,
133
169
  missed: true,
134
170
  used: true,
@@ -165,11 +201,11 @@ const processTimings = ({ start, stop, duration, }) => {
165
201
  return { start: undefined, stop: undefined, duration };
166
202
  };
167
203
  const convertLabels = (labels) => {
168
- return labels?.filter(notNull)?.map(convertLabel)?.flatMap(processTagLabels) ?? [];
204
+ return labels?.filter(notNull)?.map(convertLabel)?.flatMap(processTagLabels).map(normalizeAllureIdLabel) ?? [];
169
205
  };
170
206
  const convertLabel = (label) => {
171
207
  return {
172
- name: label.name ?? __unknown,
208
+ name: label.name ?? UNKNOWN_PARAMETER_VALUE,
173
209
  value: label.value,
174
210
  };
175
211
  };
@@ -196,7 +232,7 @@ const convertStep = (stateData, step) => {
196
232
  return {
197
233
  convertedStep: {
198
234
  stepId: md5(`${step.name}${step.start}`),
199
- name: step.name ?? __unknown,
235
+ name: step.name ?? UNKNOWN_PARAMETER_VALUE,
200
236
  status: step.status ?? defaultStatus,
201
237
  steps: subSteps,
202
238
  parameters: convertParameters(step.parameters),
@@ -217,11 +253,11 @@ const convertStep = (stateData, step) => {
217
253
  };
218
254
  const convertParameters = (parameters) => parameters
219
255
  ?.filter(notNull)
220
- ?.filter((p) => p.name)
256
+ ?.filter((p) => typeof p.name === "string" && p.name.length > 0)
221
257
  ?.map(convertParameter) ?? [];
222
258
  const convertParameter = (param) => ({
223
- name: param.name ?? __unknown,
224
- value: param.value ?? __unknown,
259
+ name: param.name ?? UNKNOWN_PARAMETER_VALUE,
260
+ value: param.value ?? UNKNOWN_PARAMETER_VALUE,
225
261
  hidden: param.hidden ?? false,
226
262
  excluded: param.excluded ?? false,
227
263
  masked: param.masked ?? false,
@@ -232,42 +268,9 @@ const convertLinks = (links) => links
232
268
  ?.map(convertLink) ?? [];
233
269
  const convertLink = (link) => ({
234
270
  name: link.name,
235
- url: link.url ?? __unknown,
271
+ url: link.url ?? UNKNOWN_PARAMETER_VALUE,
236
272
  type: link.type,
237
273
  });
238
- const calculateTestId = (raw) => {
239
- const maybeAllureId = raw.labels?.find((label) => (label.name === "ALLURE_ID" || label.name === "AS_ID") && label.value !== "-1")?.value;
240
- if (maybeAllureId) {
241
- return [md5(`ALLURE_ID=${maybeAllureId}`), maybeAllureId];
242
- }
243
- if (raw.testId) {
244
- return [md5(raw.testId), undefined];
245
- }
246
- if (raw.fullName) {
247
- return [md5(raw.fullName), undefined];
248
- }
249
- return [undefined, undefined];
250
- };
251
- const calculateHistoryId = (testCase, parameters) => {
252
- if (!testCase) {
253
- return undefined;
254
- }
255
- const paramsPart = stringifyParams(parameters);
256
- return `${testCase.id}.${md5(paramsPart)}`;
257
- };
258
- const parametersCompare = (a, b) => {
259
- return (a.name ?? "").localeCompare(b.name) || a.value.localeCompare(b.value ?? "");
260
- };
261
- const stringifyParams = (parameters) => {
262
- if (!parameters) {
263
- return "";
264
- }
265
- return parameters
266
- .filter((p) => !p?.excluded)
267
- .sort(parametersCompare)
268
- .map((p) => `${p.name}:${p.value}`)
269
- .join(",");
270
- };
271
274
  const idLabelMatcher = new RegExp("^@?allure\\.id[:=](?<id>.+)$");
272
275
  const tagLabelMatcher = new RegExp("^@?allure\\.label\\.(?<name>.+)[:=](?<value>.+)$");
273
276
  const processTagLabels = (label) => {
@@ -286,3 +289,13 @@ const processTagLabels = (label) => {
286
289
  }
287
290
  return label.name ? [{ name: label.name, value: label.value }] : [];
288
291
  };
292
+ const normalizeAllureIdLabel = (label) => {
293
+ if (label.name !== "ALLURE_ID" && label.name !== "AS_ID") {
294
+ return label;
295
+ }
296
+ const value = label.value?.trim();
297
+ return {
298
+ name: "ALLURE_ID",
299
+ value: value?.length ? value : undefined,
300
+ };
301
+ };
@@ -1,6 +1,4 @@
1
- import type { TestParameter, TestResult } from "@allurereport/core-api";
2
- export declare const calculateParametersHash: (parameters?: TestParameter[]) => string;
3
- export declare const calculateRetryHash: (testCaseId: string | undefined, parametersHash: string, environmentId: string | undefined) => string | undefined;
1
+ import type { TestResult } from "@allurereport/core-api";
4
2
  export declare class RetrySubstore {
5
3
  #private;
6
4
  recordIngestOrder(testResultId: string): void;
@@ -4,21 +4,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
5
  };
6
6
  var _RetrySubstore_instances, _RetrySubstore_testResultsByRetryHash, _RetrySubstore_testResultIngestIndexById, _RetrySubstore_getIngestIndex, _RetrySubstore_compareResults;
7
- import { md5 } from "../utils/crypto.js";
8
7
  const NO_RETRIES = [];
9
- const compareByNameThenValue = (first, second) => first.name.localeCompare(second.name) || first.value.localeCompare(second.value);
10
- const stringifyRetryParameters = (parameters = []) => parameters
11
- .filter((parameter) => !parameter.excluded)
12
- .sort(compareByNameThenValue)
13
- .map((parameter) => `${parameter.name}:${parameter.value}`)
14
- .join(",");
15
- export const calculateParametersHash = (parameters = []) => md5(stringifyRetryParameters(parameters));
16
- export const calculateRetryHash = (testCaseId, parametersHash, environmentId) => {
17
- if (!testCaseId) {
18
- return undefined;
19
- }
20
- return md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`);
21
- };
22
8
  export class RetrySubstore {
23
9
  constructor() {
24
10
  _RetrySubstore_instances.add(this);
@@ -1,4 +1,4 @@
1
- import { type AllureCheckResult, type AllureHistory, type AllurePerformanceResult, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type ResolutionIssue, type ResolutionsConfig, type MetricSample, type PerformanceConfig, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
1
+ import { type AllureCheckResult, type AllureHistory, type AllurePerformanceResult, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type FlakyDetectionConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type ResolutionIssue, type ResolutionsConfig, type MetricSample, type PerformanceConfig, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
2
2
  import { type AllureStore, type AllureStoreDump, type ExitCode, type PluginGlobalError, type QualityGateValidationResult, type RealtimeEventsDispatcher, type RealtimeSubscriber, type ResultFile, type TestResultFilter } from "@allurereport/plugin-api";
3
3
  import type { RawFixtureResult, RawGlobals, RawMetadata, RawTestResult, ReaderContext, ResultsVisitor } from "@allurereport/reader-api";
4
4
  export declare const mapToObject: <K extends string | number | symbol, T = any>(map: Map<K, T>) => Record<K, T>;
@@ -7,12 +7,13 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
7
7
  #private;
8
8
  readonly indexTestResultByTestCase: Map<string, TestResult[]>;
9
9
  readonly indexTestResultByEnvironmentId: Map<string, TestResult[]>;
10
- readonly indexTestResultByHistoryId: Map<string, TestResult[]>;
10
+ readonly indexTestResultByRetryHash: Map<string, TestResult[]>;
11
11
  readonly indexAttachmentByTestResult: Map<string, AttachmentLink[]>;
12
12
  readonly indexAttachmentByFixture: Map<string, AttachmentLink[]>;
13
13
  readonly indexFixturesByTestResult: Map<string, TestFixtureResult[]>;
14
14
  constructor(params?: {
15
15
  history?: AllureHistory;
16
+ flakyDetection?: FlakyDetectionConfig;
16
17
  resolutionsConfig?: ResolutionsConfig;
17
18
  realtimeDispatcher?: RealtimeEventsDispatcher;
18
19
  realtimeSubscriber?: RealtimeSubscriber;
@@ -24,6 +25,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
24
25
  performance?: PerformanceConfig;
25
26
  });
26
27
  readHistory(): Promise<HistoryDataPoint[]>;
28
+ updateHistoryFlags(testResults?: Iterable<TestResult>): void;
27
29
  appendHistory(history: HistoryDataPoint): Promise<void>;
28
30
  addCheckResult(result: AllureCheckResult): Promise<void>;
29
31
  allCheckResults(): Promise<AllureCheckResult[]>;