@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 +15 -0
- package/dist/history.js +7 -5
- package/dist/qualityGate/qualityGate.js +1 -1
- package/dist/qualityGate/rules.js +9 -4
- package/dist/report.js +22 -9
- package/dist/store/convert.d.ts +0 -1
- package/dist/store/convert.js +71 -58
- package/dist/store/retrySubstore.d.ts +1 -3
- package/dist/store/retrySubstore.js +0 -14
- package/dist/store/store.d.ts +4 -2
- package/dist/store/store.js +159 -152
- package/dist/utils/event.d.ts +1 -1
- package/dist/utils/event.js +9 -4
- package/dist/utils/flaky.d.ts +2 -2
- package/dist/utils/flaky.js +36 -8
- package/dist/utils/realtimeChannel.d.ts +1 -0
- package/dist/utils/realtimeChannel.js +2 -2
- package/package.json +21 -21
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.
|
|
23
|
-
.map(({ id, name, fullName, environment,
|
|
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
|
-
|
|
37
|
+
retryHash: retryHash,
|
|
38
38
|
reportLinks: [],
|
|
39
39
|
};
|
|
40
40
|
})
|
|
41
41
|
.reduce((acc, item) => {
|
|
42
|
-
acc[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
|
-
|
|
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()
|
|
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
|
|
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(
|
|
107
|
-
successMessage: ({ actual, expected }) => `Success rate ${bold(
|
|
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 = {},
|
|
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.
|
|
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.
|
|
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
|
-
|
|
663
|
-
indexTestResultByTestCase:
|
|
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()
|
|
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,
|
package/dist/store/convert.d.ts
CHANGED
|
@@ -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>;
|
package/dist/store/convert.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
58
|
+
testCaseHash,
|
|
59
|
+
parametersHash,
|
|
60
|
+
environmentHash: null,
|
|
61
|
+
retryHash: calculateRetryHash({ testCaseHash, parametersHash }),
|
|
41
62
|
status: raw.status ?? defaultStatus,
|
|
42
|
-
error:
|
|
43
|
-
|
|
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
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
|
|
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 ??
|
|
118
|
+
name: raw.testCaseName ?? raw.name ?? UNKNOWN_PARAMETER_VALUE,
|
|
83
119
|
fullName: raw.fullName,
|
|
84
120
|
};
|
|
85
|
-
testCases.set(
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
224
|
-
value: param.value ??
|
|
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 ??
|
|
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 {
|
|
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);
|
package/dist/store/store.d.ts
CHANGED
|
@@ -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
|
|
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[]>;
|