@allurereport/core-api 3.17.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/categories.d.ts +1 -1
- package/dist/config.d.ts +6 -1
- package/dist/config.js +7 -1
- package/dist/history.d.ts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -0
- package/dist/model.d.ts +9 -2
- package/dist/utils/history.d.ts +4 -5
- package/dist/utils/history.js +83 -39
- package/dist/utils/identity.d.ts +17 -0
- package/dist/utils/identity.js +50 -0
- package/dist/utils/successRate.d.ts +3 -0
- package/dist/utils/successRate.js +6 -0
- package/package.json +1 -1
package/dist/categories.d.ts
CHANGED
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export type DefaultLabelsConfig = Record<string, string | string[]>;
|
|
2
|
+
export type FlakyDetectionConfig = {
|
|
3
|
+
historyDepth?: number;
|
|
4
|
+
includePassedTests?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare const parseIntegerConfigValue: (value: unknown, minValue?: number) => number | undefined;
|
|
2
7
|
export type AllureServiceConfig = {
|
|
3
8
|
accessToken?: string;
|
|
4
9
|
private?: boolean;
|
|
@@ -6,4 +11,4 @@ export type AllureServiceConfig = {
|
|
|
6
11
|
uploadMaxAttempts?: number;
|
|
7
12
|
uploadMaxSimultaneousFailures?: number;
|
|
8
13
|
};
|
|
9
|
-
export type ResolvedAllureServiceConfig = AllureServiceConfig & Required<Pick<AllureServiceConfig, "
|
|
14
|
+
export type ResolvedAllureServiceConfig = AllureServiceConfig & Required<Pick<AllureServiceConfig, "uploadMaxAttempts" | "uploadMaxSimultaneousFailures">>;
|
package/dist/config.js
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export const parseIntegerConfigValue = (value, minValue) => {
|
|
2
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const normalized = Math.floor(value);
|
|
6
|
+
return minValue === undefined || normalized >= minValue ? normalized : undefined;
|
|
7
|
+
};
|
package/dist/history.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface HistoryTestResult {
|
|
|
12
12
|
duration?: number;
|
|
13
13
|
labels?: TestLabel[];
|
|
14
14
|
url: string;
|
|
15
|
-
|
|
15
|
+
retryHash?: string;
|
|
16
16
|
reportLinks?: any[];
|
|
17
17
|
}
|
|
18
18
|
export interface HistoryDataPoint {
|
|
@@ -24,10 +24,12 @@ export interface HistoryDataPoint {
|
|
|
24
24
|
metrics: Record<string, number>;
|
|
25
25
|
url: string;
|
|
26
26
|
}
|
|
27
|
+
export type HistoryTestResultUrlResolver = (historyUrl: string, pluginId: string, historicalResultId: string) => string;
|
|
27
28
|
export interface AllureHistory {
|
|
28
29
|
readHistory(params?: {
|
|
29
30
|
repo?: string;
|
|
30
31
|
branch?: string;
|
|
31
32
|
}): Promise<HistoryDataPoint[]>;
|
|
32
33
|
appendHistory(history: HistoryDataPoint): Promise<void>;
|
|
34
|
+
resolveTestResultUrl?: HistoryTestResultUrlResolver;
|
|
33
35
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export * from "./metrics.js";
|
|
|
10
10
|
export type * from "./model.js";
|
|
11
11
|
export type * from "./testCase.js";
|
|
12
12
|
export type * from "./testPlan.js";
|
|
13
|
-
export
|
|
13
|
+
export * from "./config.js";
|
|
14
14
|
export * from "./static.js";
|
|
15
15
|
export * from "./categories.js";
|
|
16
16
|
export * from "./utils/step.js";
|
|
@@ -22,8 +22,10 @@ export * from "./utils/label.js";
|
|
|
22
22
|
export * from "./utils/parameter.js";
|
|
23
23
|
export * from "./utils/testplan.js";
|
|
24
24
|
export * from "./utils/status.js";
|
|
25
|
+
export * from "./utils/successRate.js";
|
|
25
26
|
export * from "./utils/environment.js";
|
|
26
27
|
export * from "./utils/history.js";
|
|
28
|
+
export * from "./utils/identity.js";
|
|
27
29
|
export * from "./utils/strings.js";
|
|
28
30
|
export * from "./utils/dictionary.js";
|
|
29
31
|
export * from "./utils/path.js";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from "./constants.js";
|
|
|
2
2
|
export * from "./ci.js";
|
|
3
3
|
export * from "./gitFlow.js";
|
|
4
4
|
export * from "./metrics.js";
|
|
5
|
+
export * from "./config.js";
|
|
5
6
|
export * from "./static.js";
|
|
6
7
|
export * from "./categories.js";
|
|
7
8
|
export * from "./utils/step.js";
|
|
@@ -12,8 +13,10 @@ export * from "./utils/label.js";
|
|
|
12
13
|
export * from "./utils/parameter.js";
|
|
13
14
|
export * from "./utils/testplan.js";
|
|
14
15
|
export * from "./utils/status.js";
|
|
16
|
+
export * from "./utils/successRate.js";
|
|
15
17
|
export * from "./utils/environment.js";
|
|
16
18
|
export * from "./utils/history.js";
|
|
19
|
+
export * from "./utils/identity.js";
|
|
17
20
|
export * from "./utils/strings.js";
|
|
18
21
|
export * from "./utils/dictionary.js";
|
|
19
22
|
export * from "./utils/path.js";
|
package/dist/model.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ export interface SourceMetadata {
|
|
|
21
21
|
metadata: {
|
|
22
22
|
[key: string]: any;
|
|
23
23
|
};
|
|
24
|
+
legacyHistoryId?: string;
|
|
25
|
+
reportedFlaky?: boolean;
|
|
26
|
+
legacyTestCaseHash?: string;
|
|
24
27
|
}
|
|
25
28
|
export interface TestError {
|
|
26
29
|
message?: string;
|
|
@@ -33,10 +36,13 @@ export interface TestResult {
|
|
|
33
36
|
name: string;
|
|
34
37
|
status: TestStatus;
|
|
35
38
|
error?: TestError;
|
|
39
|
+
errors?: TestError[];
|
|
36
40
|
testCase?: TestCase;
|
|
37
41
|
environment?: string;
|
|
38
42
|
fullName?: string;
|
|
39
|
-
|
|
43
|
+
testCaseHash: string | null;
|
|
44
|
+
parametersHash: string;
|
|
45
|
+
environmentHash: string | null;
|
|
40
46
|
description?: string;
|
|
41
47
|
descriptionHtml?: string;
|
|
42
48
|
precondition?: string;
|
|
@@ -48,10 +54,11 @@ export interface TestResult {
|
|
|
48
54
|
duration?: number;
|
|
49
55
|
flaky: boolean;
|
|
50
56
|
muted: boolean;
|
|
57
|
+
known: boolean;
|
|
51
58
|
resolution?: ResolutionCategory;
|
|
52
59
|
resolutionComment?: string;
|
|
53
60
|
transition?: TestStatusTransition;
|
|
54
|
-
retryHash
|
|
61
|
+
retryHash: string | null;
|
|
55
62
|
isRetry: boolean;
|
|
56
63
|
hostId?: string;
|
|
57
64
|
threadId?: string;
|
package/dist/utils/history.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { HistoryDataPoint, HistoryTestResult } from "../history.js";
|
|
2
|
-
import type { TestParameter } from "../metadata.js";
|
|
3
2
|
import type { TestResult } from "../model.js";
|
|
4
|
-
export declare const
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export declare const createHistoryTestResultLookup: (testResults: Iterable<TestResult | HistoryTestResult>) => <T extends TestResult | HistoryTestResult>(point: {
|
|
4
|
+
testResults?: Record<string, T>;
|
|
5
|
+
}, result: TestResult | HistoryTestResult) => T | undefined;
|
|
7
6
|
export declare const normalizeHistoryDataPoint: (historyDataPoint: HistoryDataPoint) => HistoryDataPoint;
|
|
8
7
|
export declare const normalizeHistoryDataPointUrls: (historyDataPoint: HistoryDataPoint) => HistoryDataPoint;
|
|
9
|
-
export declare const selectHistoryTestResults: (historyDataPoints: HistoryDataPoint[],
|
|
8
|
+
export declare const selectHistoryTestResults: (historyDataPoints: HistoryDataPoint[], retryHashes: readonly string[]) => HistoryTestResult[];
|
|
10
9
|
export declare const htrsByTr: (hdps: HistoryDataPoint[], tr: TestResult | HistoryTestResult) => HistoryTestResult[];
|
package/dist/utils/history.js
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { fallbackTestCaseIdLabelName } from "../constants.js";
|
|
3
|
-
import { findLastByLabelName } from "./label.js";
|
|
4
|
-
const md5 = (data) => createHash("md5").update(data).digest("hex");
|
|
1
|
+
import { DEFAULT_ENVIRONMENT } from "./environment.js";
|
|
5
2
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
3
|
const normalizeHistoryTestResults = (testResults) => {
|
|
7
4
|
if (!isRecord(testResults)) {
|
|
8
5
|
return {};
|
|
9
6
|
}
|
|
10
|
-
return Object.fromEntries(Object.entries(testResults).
|
|
7
|
+
return Object.fromEntries(Object.entries(testResults).flatMap(([retryHash, value]) => {
|
|
8
|
+
if (!isRecord(value)) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
const historyTestResult = { ...value };
|
|
12
|
+
delete historyTestResult.historyId;
|
|
13
|
+
return [
|
|
14
|
+
[
|
|
15
|
+
retryHash,
|
|
16
|
+
{
|
|
17
|
+
...historyTestResult,
|
|
18
|
+
retryHash,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
];
|
|
22
|
+
}));
|
|
11
23
|
};
|
|
12
24
|
const normalizeHistoryMetrics = (metrics) => {
|
|
13
25
|
if (!isRecord(metrics)) {
|
|
@@ -15,33 +27,64 @@ const normalizeHistoryMetrics = (metrics) => {
|
|
|
15
27
|
}
|
|
16
28
|
return Object.fromEntries(Object.entries(metrics).filter(([, value]) => Number.isFinite(value)));
|
|
17
29
|
};
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const isDefaultEnvironment = (result) => result.environment === undefined || result.environment === DEFAULT_ENVIRONMENT;
|
|
31
|
+
export const createHistoryTestResultLookup = (testResults) => {
|
|
32
|
+
const canonicalKeys = new Set();
|
|
33
|
+
const retryHashByLegacyHistoryId = new Map();
|
|
34
|
+
for (const result of testResults) {
|
|
35
|
+
if (!result.retryHash) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
canonicalKeys.add(result.retryHash);
|
|
39
|
+
const legacyId = "sourceMetadata" in result ? result.sourceMetadata?.legacyHistoryId : undefined;
|
|
40
|
+
if (!legacyId || !isDefaultEnvironment(result) || ("environmentHash" in result && result.environmentHash)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (!retryHashByLegacyHistoryId.has(legacyId)) {
|
|
44
|
+
retryHashByLegacyHistoryId.set(legacyId, result.retryHash);
|
|
45
|
+
}
|
|
46
|
+
else if (retryHashByLegacyHistoryId.get(legacyId) !== result.retryHash) {
|
|
47
|
+
retryHashByLegacyHistoryId.set(legacyId, undefined);
|
|
48
|
+
}
|
|
32
49
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (tr.historyId) {
|
|
38
|
-
result.push(tr.historyId);
|
|
50
|
+
for (const [legacyId, retryHash] of retryHashByLegacyHistoryId) {
|
|
51
|
+
if (retryHash === undefined || (canonicalKeys.has(legacyId) && legacyId !== retryHash)) {
|
|
52
|
+
retryHashByLegacyHistoryId.set(legacyId, undefined);
|
|
53
|
+
}
|
|
39
54
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
const legacyByCanonical = new Map();
|
|
56
|
+
for (const [legacyId, retryHash] of retryHashByLegacyHistoryId) {
|
|
57
|
+
if (!retryHash || legacyId === retryHash) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
legacyByCanonical.set(retryHash, legacyByCanonical.has(retryHash) ? undefined : legacyId);
|
|
43
61
|
}
|
|
44
|
-
return result
|
|
62
|
+
return (point, result) => {
|
|
63
|
+
if (!result.retryHash) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const historicalDefault = !("sourceMetadata" in result) && isDefaultEnvironment(result);
|
|
67
|
+
const retryHash = historicalDefault
|
|
68
|
+
? (retryHashByLegacyHistoryId.get(result.retryHash) ?? result.retryHash)
|
|
69
|
+
: result.retryHash;
|
|
70
|
+
const canonical = Object.hasOwn(point.testResults ?? {}, retryHash) ? point.testResults?.[retryHash] : undefined;
|
|
71
|
+
if (canonical) {
|
|
72
|
+
return canonical;
|
|
73
|
+
}
|
|
74
|
+
const legacyId = "sourceMetadata" in result
|
|
75
|
+
? result.sourceMetadata?.legacyHistoryId
|
|
76
|
+
: historicalDefault
|
|
77
|
+
? legacyByCanonical.get(retryHash)
|
|
78
|
+
: undefined;
|
|
79
|
+
if (!legacyId ||
|
|
80
|
+
retryHashByLegacyHistoryId.get(legacyId) !== retryHash ||
|
|
81
|
+
!isDefaultEnvironment(result) ||
|
|
82
|
+
("environmentHash" in result && result.environmentHash)) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const legacy = Object.hasOwn(point.testResults ?? {}, legacyId) ? point.testResults?.[legacyId] : undefined;
|
|
86
|
+
return legacy && isDefaultEnvironment(legacy) ? legacy : undefined;
|
|
87
|
+
};
|
|
45
88
|
};
|
|
46
89
|
export const normalizeHistoryDataPoint = (historyDataPoint) => ({
|
|
47
90
|
...historyDataPoint,
|
|
@@ -57,14 +100,14 @@ export const normalizeHistoryDataPointUrls = (historyDataPoint) => {
|
|
|
57
100
|
return normalizedHistoryDataPoint;
|
|
58
101
|
}
|
|
59
102
|
let testResults = normalizedHistoryDataPoint.testResults;
|
|
60
|
-
for (const [
|
|
103
|
+
for (const [retryHash, historyTestResult] of Object.entries(normalizedHistoryDataPoint.testResults)) {
|
|
61
104
|
if (historyTestResult.url) {
|
|
62
105
|
continue;
|
|
63
106
|
}
|
|
64
107
|
if (testResults === normalizedHistoryDataPoint.testResults) {
|
|
65
108
|
testResults = { ...normalizedHistoryDataPoint.testResults };
|
|
66
109
|
}
|
|
67
|
-
testResults[
|
|
110
|
+
testResults[retryHash] = {
|
|
68
111
|
...historyTestResult,
|
|
69
112
|
url,
|
|
70
113
|
};
|
|
@@ -77,13 +120,13 @@ export const normalizeHistoryDataPointUrls = (historyDataPoint) => {
|
|
|
77
120
|
testResults,
|
|
78
121
|
};
|
|
79
122
|
};
|
|
80
|
-
export const selectHistoryTestResults = (historyDataPoints,
|
|
81
|
-
if (
|
|
123
|
+
export const selectHistoryTestResults = (historyDataPoints, retryHashes) => {
|
|
124
|
+
if (retryHashes.length === 0) {
|
|
82
125
|
return [];
|
|
83
126
|
}
|
|
84
127
|
return historyDataPoints.reduce((acc, historyDataPoint) => {
|
|
85
|
-
for (const
|
|
86
|
-
const historyTestResult = historyDataPoint.testResults?.[
|
|
128
|
+
for (const retryHash of retryHashes) {
|
|
129
|
+
const historyTestResult = historyDataPoint.testResults?.[retryHash];
|
|
87
130
|
if (!historyTestResult) {
|
|
88
131
|
continue;
|
|
89
132
|
}
|
|
@@ -94,8 +137,9 @@ export const selectHistoryTestResults = (historyDataPoints, historyIdCandidates)
|
|
|
94
137
|
}, []);
|
|
95
138
|
};
|
|
96
139
|
export const htrsByTr = (hdps, tr) => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
140
|
+
const lookup = createHistoryTestResultLookup([tr]);
|
|
141
|
+
return hdps.flatMap((point) => {
|
|
142
|
+
const result = lookup(point, tr);
|
|
143
|
+
return result ? [result] : [];
|
|
144
|
+
});
|
|
101
145
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const UNKNOWN_PARAMETER_VALUE = "#___unknown_value___#";
|
|
2
|
+
export type IdentityParameter = {
|
|
3
|
+
name?: string | null;
|
|
4
|
+
value?: string | null;
|
|
5
|
+
excluded?: boolean | null;
|
|
6
|
+
};
|
|
7
|
+
export declare const compareUtf8: (first: string, second: string) => number;
|
|
8
|
+
export declare const md5Utf8: (value: string) => string;
|
|
9
|
+
export declare const calculateTestCaseHash: (testCaseId: string | null | undefined, fullName: string | null | undefined) => string | null;
|
|
10
|
+
export declare const stringifyIdentityParameters: (parameters: readonly (IdentityParameter | null | undefined)[] | null | undefined) => string;
|
|
11
|
+
export declare const calculateParametersHash: (parameters: readonly (IdentityParameter | null | undefined)[] | null | undefined) => string;
|
|
12
|
+
export declare const calculateEnvironmentHash: (namedEnvironmentId: string | null | undefined) => string | null;
|
|
13
|
+
export declare const calculateRetryHash: ({ testCaseHash, parametersHash, environmentHash, }: {
|
|
14
|
+
testCaseHash: string | null | undefined;
|
|
15
|
+
parametersHash: string;
|
|
16
|
+
environmentHash?: string | null;
|
|
17
|
+
}) => string | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { DEFAULT_ENVIRONMENT } from "./environment.js";
|
|
3
|
+
export const UNKNOWN_PARAMETER_VALUE = "#___unknown_value___#";
|
|
4
|
+
const utf8Encoder = new TextEncoder();
|
|
5
|
+
export const compareUtf8 = (first, second) => {
|
|
6
|
+
const firstBytes = utf8Encoder.encode(first);
|
|
7
|
+
const secondBytes = utf8Encoder.encode(second);
|
|
8
|
+
const length = Math.min(firstBytes.length, secondBytes.length);
|
|
9
|
+
for (let index = 0; index < length; index++) {
|
|
10
|
+
const comparison = firstBytes[index] - secondBytes[index];
|
|
11
|
+
if (comparison !== 0) {
|
|
12
|
+
return comparison;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return firstBytes.length - secondBytes.length;
|
|
16
|
+
};
|
|
17
|
+
export const md5Utf8 = (value) => createHash("md5").update(value, "utf8").digest("hex");
|
|
18
|
+
export const calculateTestCaseHash = (testCaseId, fullName) => {
|
|
19
|
+
const identity = testCaseId?.length ? testCaseId : fullName?.length ? fullName : undefined;
|
|
20
|
+
return identity === undefined ? null : md5Utf8(identity);
|
|
21
|
+
};
|
|
22
|
+
const compareIdentityParameters = (first, second) => compareUtf8(first.name, second.name) || compareUtf8(first.value, second.value);
|
|
23
|
+
export const stringifyIdentityParameters = (parameters) => {
|
|
24
|
+
const normalized = [];
|
|
25
|
+
for (const parameter of parameters ?? []) {
|
|
26
|
+
if (parameter == null) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const { name } = parameter;
|
|
30
|
+
if (typeof name !== "string" || name.length === 0 || parameter.excluded === true) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
normalized.push({
|
|
34
|
+
name,
|
|
35
|
+
value: parameter.value ?? UNKNOWN_PARAMETER_VALUE,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const unique = normalized
|
|
39
|
+
.sort(compareIdentityParameters)
|
|
40
|
+
.filter((parameter, index, all) => index === 0 || parameter.name !== all[index - 1].name || parameter.value !== all[index - 1].value);
|
|
41
|
+
return unique.map(({ name, value }) => `${name}:${value}`).join(",");
|
|
42
|
+
};
|
|
43
|
+
export const calculateParametersHash = (parameters) => md5Utf8(stringifyIdentityParameters(parameters));
|
|
44
|
+
export const calculateEnvironmentHash = (namedEnvironmentId) => namedEnvironmentId == null || namedEnvironmentId === DEFAULT_ENVIRONMENT ? null : md5Utf8(namedEnvironmentId);
|
|
45
|
+
export const calculateRetryHash = ({ testCaseHash, parametersHash, environmentHash, }) => {
|
|
46
|
+
if (!testCaseHash) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return environmentHash ? `${testCaseHash}.${parametersHash}.${environmentHash}` : `${testCaseHash}.${parametersHash}`;
|
|
50
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { includedInSuccessRate } from "../constants.js";
|
|
2
|
+
export const getSuccessRateTotal = (statistic) => Array.from(includedInSuccessRate).reduce((total, status) => total + (statistic[status] ?? 0), 0);
|
|
3
|
+
export const getSuccessRate = (statistic) => {
|
|
4
|
+
const total = getSuccessRateTotal(statistic);
|
|
5
|
+
return total > 0 ? (statistic.passed ?? 0) / total : 0;
|
|
6
|
+
};
|