@allurereport/web-commons 3.3.1 → 3.4.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/attachments.d.ts +9 -0
- package/dist/attachments.js +115 -0
- package/dist/charts/generateStabilityDistributionChart.d.ts +2 -0
- package/dist/charts/generateStabilityDistributionChart.js +123 -31
- package/dist/charts/generateStatusAgePyramid.js +2 -1
- package/dist/charts/generators.d.ts +1 -1
- package/dist/charts/generators.js +81 -30
- package/dist/data.js +19 -5
- package/dist/environments.d.ts +3 -0
- package/dist/environments.js +14 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +12 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +15 -24
package/dist/attachments.d.ts
CHANGED
|
@@ -29,6 +29,15 @@ export declare const blobAttachment: (id: string, ext: string, contentType: stri
|
|
|
29
29
|
export declare const downloadAttachment: (id: string, ext: string, contentType: string) => Promise<void>;
|
|
30
30
|
export declare const openAttachmentInNewTab: (id: string, ext: string, contentType: string) => Promise<void>;
|
|
31
31
|
export type AttachmentType = "css" | "json" | "image" | "svg" | "code" | "text" | "html" | "table" | "video" | "uri" | "archive" | "image-diff";
|
|
32
|
+
export declare const PREVIEWABLE_CONTENT_TYPES: readonly ["text/html", "text/csv", "text/markdown", "text/tab-separated-values", "text/uri-list"];
|
|
33
|
+
export declare const isPreviewableContentType: (type?: string) => boolean;
|
|
34
|
+
export declare const extname: (str: string) => string | undefined;
|
|
35
|
+
export declare const isSyntaxHighlightSupported: (payload?: {
|
|
36
|
+
contentType?: string;
|
|
37
|
+
ext?: string;
|
|
38
|
+
name?: string;
|
|
39
|
+
originalFileName?: string;
|
|
40
|
+
}) => boolean;
|
|
32
41
|
export declare const attachmentType: (type?: string) => AttachmentType | null;
|
|
33
42
|
export declare const restrictedContentTypes: string[];
|
|
34
43
|
export {};
|
package/dist/attachments.js
CHANGED
|
@@ -65,6 +65,121 @@ export const openAttachmentInNewTab = async (id, ext, contentType) => {
|
|
|
65
65
|
const linkUrl = URL.createObjectURL(blob);
|
|
66
66
|
globalThis.open(linkUrl, "_blank");
|
|
67
67
|
};
|
|
68
|
+
export const PREVIEWABLE_CONTENT_TYPES = [
|
|
69
|
+
"text/html",
|
|
70
|
+
"text/csv",
|
|
71
|
+
"text/markdown",
|
|
72
|
+
"text/tab-separated-values",
|
|
73
|
+
"text/uri-list",
|
|
74
|
+
];
|
|
75
|
+
export const isPreviewableContentType = (type) => !!type && PREVIEWABLE_CONTENT_TYPES.includes(type);
|
|
76
|
+
const HIGHLIGHT_EXTS = new Set([
|
|
77
|
+
"js",
|
|
78
|
+
"mjs",
|
|
79
|
+
"cjs",
|
|
80
|
+
"jsx",
|
|
81
|
+
"ts",
|
|
82
|
+
"mts",
|
|
83
|
+
"cts",
|
|
84
|
+
"tsx",
|
|
85
|
+
"json",
|
|
86
|
+
"html",
|
|
87
|
+
"htm",
|
|
88
|
+
"xml",
|
|
89
|
+
"css",
|
|
90
|
+
"csv",
|
|
91
|
+
"tsv",
|
|
92
|
+
"md",
|
|
93
|
+
"markdown",
|
|
94
|
+
"yaml",
|
|
95
|
+
"yml",
|
|
96
|
+
"java",
|
|
97
|
+
"py",
|
|
98
|
+
"rb",
|
|
99
|
+
"go",
|
|
100
|
+
"php",
|
|
101
|
+
"sql",
|
|
102
|
+
"kt",
|
|
103
|
+
"swift",
|
|
104
|
+
"rs",
|
|
105
|
+
"c",
|
|
106
|
+
"cpp",
|
|
107
|
+
"cs",
|
|
108
|
+
"scala",
|
|
109
|
+
"dart",
|
|
110
|
+
"lua",
|
|
111
|
+
"haskell",
|
|
112
|
+
"r",
|
|
113
|
+
"perl",
|
|
114
|
+
]);
|
|
115
|
+
const NO_HIGHLIGHT_TYPES = new Set(["text/plain", "text/*", "text/uri-list"]);
|
|
116
|
+
const HIGHLIGHT_TYPES = new Set([
|
|
117
|
+
"text/markdown",
|
|
118
|
+
"text/html",
|
|
119
|
+
"text/csv",
|
|
120
|
+
"text/tab-separated-values",
|
|
121
|
+
"text/xml",
|
|
122
|
+
"text/json",
|
|
123
|
+
"text/yaml",
|
|
124
|
+
"text/javascript",
|
|
125
|
+
"text/typescript",
|
|
126
|
+
"text/ruby",
|
|
127
|
+
"text/python",
|
|
128
|
+
"text/php",
|
|
129
|
+
"text/java",
|
|
130
|
+
"text/csharp",
|
|
131
|
+
"text/cpp",
|
|
132
|
+
"text/c",
|
|
133
|
+
"text/go",
|
|
134
|
+
"text/rust",
|
|
135
|
+
"text/swift",
|
|
136
|
+
"text/kotlin",
|
|
137
|
+
"text/scala",
|
|
138
|
+
"text/perl",
|
|
139
|
+
"text/r",
|
|
140
|
+
"text/dart",
|
|
141
|
+
"text/lua",
|
|
142
|
+
"text/haskell",
|
|
143
|
+
"text/sql",
|
|
144
|
+
"text/x-yaml",
|
|
145
|
+
"text/css",
|
|
146
|
+
"application/yaml",
|
|
147
|
+
"application/x-yaml",
|
|
148
|
+
"application/xml",
|
|
149
|
+
"application/json",
|
|
150
|
+
]);
|
|
151
|
+
export const extname = (str) => {
|
|
152
|
+
const i = str.lastIndexOf(".");
|
|
153
|
+
if (i <= 0 || i === str.length - 1)
|
|
154
|
+
return undefined;
|
|
155
|
+
return str.slice(i);
|
|
156
|
+
};
|
|
157
|
+
export const isSyntaxHighlightSupported = (payload) => {
|
|
158
|
+
if (!payload) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const contentType = payload.contentType?.toLowerCase();
|
|
162
|
+
let ext;
|
|
163
|
+
for (const part of [payload.ext, payload.name, payload.originalFileName]) {
|
|
164
|
+
if (!part) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const withDot = extname(part);
|
|
168
|
+
const normalized = (withDot ?? part.replace(/^\./, "")).toLowerCase();
|
|
169
|
+
const single = normalized.includes(".") ? normalized.split(".").pop() : normalized;
|
|
170
|
+
if (single) {
|
|
171
|
+
ext = single;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (contentType && NO_HIGHLIGHT_TYPES.has(contentType)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (contentType && HIGHLIGHT_TYPES.has(contentType)) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
return !!ext && HIGHLIGHT_EXTS.has(ext);
|
|
182
|
+
};
|
|
68
183
|
export const attachmentType = (type) => {
|
|
69
184
|
switch (type) {
|
|
70
185
|
case "image/bmp":
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { AllureChartsStoreData, StabilityDistributionChartData, StabilityDistributionChartOptions } from "@allurereport/charts-api";
|
|
2
|
+
import type { TestStatus } from "@allurereport/core-api";
|
|
3
|
+
export declare const getStabilityScore: (statuses: TestStatus[], historyDepth: number, stabilizationPeriod: number) => number | undefined;
|
|
2
4
|
export declare const generateStabilityDistributionChart: (props: {
|
|
3
5
|
options: StabilityDistributionChartOptions;
|
|
4
6
|
storeData: AllureChartsStoreData;
|
|
@@ -1,61 +1,153 @@
|
|
|
1
1
|
import { ChartType } from "@allurereport/charts-api";
|
|
2
2
|
import { createHashStorage, createMapWithDefault } from "./utils.js";
|
|
3
|
+
const DEFAULT_LIMIT = 10;
|
|
4
|
+
const DEFAULT_STABILIZATION_PERIOD = 5;
|
|
3
5
|
const DEFAULT_THRESHOLD = 90;
|
|
4
6
|
const DEFAULT_GROUP_BY = "feature";
|
|
5
7
|
const CUSTOM_LABEL_NAME_PREFIX = "label-name:";
|
|
8
|
+
const SIGNIFICANT_STATUSES = new Set(["passed", "failed", "broken"]);
|
|
9
|
+
const isSignificantStatus = (status) => SIGNIFICANT_STATUSES.has(status);
|
|
6
10
|
const getTrLabelValue = (testResult, labelName) => {
|
|
7
|
-
return testResult.labels
|
|
8
|
-
};
|
|
9
|
-
const getStabilityRate = (passed, total) => {
|
|
10
|
-
return Math.floor((passed / total) * 10000) / 100;
|
|
11
|
+
return testResult.labels?.find((label) => label.name === labelName)?.value;
|
|
11
12
|
};
|
|
12
13
|
const NON_SIGNIFICANT_STATUSES = ["unknown", "skipped"];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const findLatestConsecutiveSameBlock = (statuses, sequenceLength, blockSize) => {
|
|
15
|
+
for (let start = sequenceLength - blockSize; start >= 0; start--) {
|
|
16
|
+
const first = statuses[start];
|
|
17
|
+
let allSame = true;
|
|
18
|
+
for (let k = 1; k < blockSize; k++) {
|
|
19
|
+
if (statuses[start + k] !== first) {
|
|
20
|
+
allSame = false;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (allSame) {
|
|
25
|
+
return { endIndex: start + blockSize - 1, status: first };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
const computeTransitionsInRange = (statuses, firstEdge, lastEdge) => {
|
|
31
|
+
let transitionCount = 0;
|
|
32
|
+
let firstTransitionIdx = 0;
|
|
33
|
+
let secondTransitionIdx = 0;
|
|
34
|
+
let instabilitySumNumerator = 0;
|
|
35
|
+
for (let i = firstEdge; i <= lastEdge; i++) {
|
|
36
|
+
if (statuses[i - 1] === statuses[i]) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
transitionCount++;
|
|
40
|
+
if (transitionCount === 1) {
|
|
41
|
+
firstTransitionIdx = i;
|
|
42
|
+
}
|
|
43
|
+
else if (transitionCount === 2) {
|
|
44
|
+
secondTransitionIdx = i;
|
|
45
|
+
}
|
|
46
|
+
instabilitySumNumerator += i;
|
|
47
|
+
}
|
|
48
|
+
return { transitionCount, firstTransitionIdx, secondTransitionIdx, instabilitySumNumerator };
|
|
49
|
+
};
|
|
50
|
+
const isRecoveryTolerancePattern = (statuses, firstTransitionIdx, secondTransitionIdx) => {
|
|
51
|
+
const s0 = statuses[firstTransitionIdx - 1];
|
|
52
|
+
const s1 = statuses[firstTransitionIdx];
|
|
53
|
+
const s2 = statuses[secondTransitionIdx - 1];
|
|
54
|
+
const s3 = statuses[secondTransitionIdx];
|
|
55
|
+
return ((s0 === "passed" && s1 === "broken" && s2 === "broken" && s3 === "passed") ||
|
|
56
|
+
(s0 === "passed" && s1 === "failed" && s2 === "failed" && s3 === "passed"));
|
|
57
|
+
};
|
|
58
|
+
export const getStabilityScore = (statuses, historyDepth, stabilizationPeriod) => {
|
|
59
|
+
const effectiveSequenceLength = Math.min(statuses.length, historyDepth);
|
|
60
|
+
if (effectiveSequenceLength === 0) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
if (effectiveSequenceLength === 1) {
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
const stabilBlock = findLatestConsecutiveSameBlock(statuses, effectiveSequenceLength, stabilizationPeriod);
|
|
67
|
+
if (stabilBlock !== null && stabilBlock.endIndex === effectiveSequenceLength - 1) {
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
const firstEdgeAfterStabilBlock = stabilBlock !== null ? stabilBlock.endIndex + 1 : 1;
|
|
71
|
+
const lastEdge = effectiveSequenceLength - 1;
|
|
72
|
+
const denominator = ((effectiveSequenceLength - 1) * effectiveSequenceLength) / 2;
|
|
73
|
+
const { transitionCount, firstTransitionIdx, secondTransitionIdx, instabilitySumNumerator } = computeTransitionsInRange(statuses, firstEdgeAfterStabilBlock, lastEdge);
|
|
74
|
+
if (transitionCount === 1) {
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
if (transitionCount === 2 && isRecoveryTolerancePattern(statuses, firstTransitionIdx, secondTransitionIdx)) {
|
|
78
|
+
return 1;
|
|
16
79
|
}
|
|
17
|
-
|
|
18
|
-
|
|
80
|
+
return instabilitySumNumerator / denominator;
|
|
81
|
+
};
|
|
82
|
+
const getStatusSequence = (historyDataPoints, tr) => {
|
|
83
|
+
let block = [];
|
|
84
|
+
for (const hdp of historyDataPoints) {
|
|
85
|
+
const htr = hdp.testResults[tr.historyId];
|
|
86
|
+
if (!htr) {
|
|
87
|
+
block = [];
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (isSignificantStatus(htr.status)) {
|
|
91
|
+
block.push(htr.status);
|
|
92
|
+
}
|
|
19
93
|
}
|
|
20
|
-
|
|
21
|
-
|
|
94
|
+
if (isSignificantStatus(tr.status)) {
|
|
95
|
+
block.push(tr.status);
|
|
96
|
+
}
|
|
97
|
+
return block;
|
|
98
|
+
};
|
|
22
99
|
export const generateStabilityDistributionChart = (props) => {
|
|
23
100
|
const { options, storeData } = props;
|
|
24
|
-
const { title, threshold = DEFAULT_THRESHOLD, skipStatuses = NON_SIGNIFICANT_STATUSES, groupBy = DEFAULT_GROUP_BY, groupValues = [], } = options;
|
|
25
|
-
const { testResults } = storeData;
|
|
101
|
+
const { title, limit = DEFAULT_LIMIT, stabilizationPeriod = DEFAULT_STABILIZATION_PERIOD, threshold = DEFAULT_THRESHOLD, skipStatuses: skipStatusesList = NON_SIGNIFICANT_STATUSES, groupBy = DEFAULT_GROUP_BY, groupValues = [], } = options;
|
|
102
|
+
const { testResults, historyDataPoints } = storeData;
|
|
103
|
+
const effectiveStabilizationPeriod = Math.min(stabilizationPeriod, limit);
|
|
104
|
+
if (historyDataPoints.length < effectiveStabilizationPeriod) {
|
|
105
|
+
return {
|
|
106
|
+
data: [],
|
|
107
|
+
keys: {},
|
|
108
|
+
type: ChartType.StabilityDistribution,
|
|
109
|
+
title,
|
|
110
|
+
threshold,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const limitedHistoryDataPoints = historyDataPoints.slice(0, limit).sort((a, b) => a.timestamp - b.timestamp);
|
|
26
114
|
const labelName = groupBy.startsWith(CUSTOM_LABEL_NAME_PREFIX)
|
|
27
115
|
? groupBy.slice(CUSTOM_LABEL_NAME_PREFIX.length)
|
|
28
116
|
: groupBy;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
passed: 0,
|
|
32
|
-
total: 0,
|
|
33
|
-
});
|
|
117
|
+
const groupValuesSet = new Set(groupValues ?? []);
|
|
118
|
+
const stabilityScoresByGroup = createMapWithDefault([]);
|
|
34
119
|
const keys = {};
|
|
35
120
|
const hashes = createHashStorage();
|
|
121
|
+
const skipStatuses = new Set(skipStatusesList);
|
|
36
122
|
for (const tr of testResults) {
|
|
37
|
-
|
|
38
|
-
if (!labelValue) {
|
|
123
|
+
if (!tr.historyId) {
|
|
39
124
|
continue;
|
|
40
125
|
}
|
|
41
|
-
if (!
|
|
126
|
+
if (!isSignificantStatus(tr.status)) {
|
|
42
127
|
continue;
|
|
43
128
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
129
|
+
if (skipStatuses.has(tr.status)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const labelValue = getTrLabelValue(tr, labelName);
|
|
133
|
+
if (!labelValue || (groupValuesSet.size > 0 && !groupValuesSet.has(labelValue))) {
|
|
47
134
|
continue;
|
|
48
135
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
136
|
+
const statusSequence = getStatusSequence(limitedHistoryDataPoints, tr);
|
|
137
|
+
const stabilityScore = getStabilityScore(statusSequence, limit, effectiveStabilizationPeriod);
|
|
138
|
+
if (stabilityScore === undefined) {
|
|
139
|
+
continue;
|
|
52
140
|
}
|
|
141
|
+
const labelValueHash = hashes.get(labelValue);
|
|
142
|
+
keys[labelValueHash] = labelValue;
|
|
143
|
+
stabilityScoresByGroup.get(labelValueHash).push(stabilityScore);
|
|
53
144
|
}
|
|
54
145
|
return {
|
|
55
|
-
data:
|
|
56
|
-
|
|
57
|
-
stabilityRate
|
|
58
|
-
|
|
146
|
+
data: stabilityScoresByGroup.entries.map(([id, scores]) => {
|
|
147
|
+
const avg = scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0;
|
|
148
|
+
const stabilityRate = Math.floor(avg * 10000) / 100;
|
|
149
|
+
return { id, stabilityRate };
|
|
150
|
+
}),
|
|
59
151
|
keys,
|
|
60
152
|
type: ChartType.StabilityDistribution,
|
|
61
153
|
title,
|
|
@@ -28,13 +28,14 @@ export const generateStatusAgePyramid = (props) => {
|
|
|
28
28
|
statuses: STATUSES,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
const currTrIds = new Set(testResults.map((tr) => tr.historyId ?? tr.id));
|
|
31
32
|
const hdps = limitedHistoryPoints.map((datapoint) => ({
|
|
32
33
|
...datapoint,
|
|
33
34
|
testResults: Object.values(datapoint.testResults).reduce((acc, testResult) => {
|
|
34
35
|
if (!testResult.historyId) {
|
|
35
36
|
return acc;
|
|
36
37
|
}
|
|
37
|
-
const isInCurrentRun =
|
|
38
|
+
const isInCurrentRun = currTrIds.has(testResult.historyId);
|
|
38
39
|
if (isInCurrentRun) {
|
|
39
40
|
acc[testResult.historyId] = testResult;
|
|
40
41
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ChartOptions, type GeneratedChartsData } from "@allurereport/charts-api";
|
|
2
|
-
import type
|
|
2
|
+
import { type TestResult } from "@allurereport/core-api";
|
|
3
3
|
import { type AllureStore } from "@allurereport/plugin-api";
|
|
4
4
|
type ChartsWidgetData = {
|
|
5
5
|
general: GeneratedChartsData;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChartType, } from "@allurereport/charts-api";
|
|
2
|
-
import { DEFAULT_ENVIRONMENT } from "@allurereport/core-api";
|
|
2
|
+
import { DEFAULT_ENVIRONMENT, emptyStatistic, getFallbackHistoryId, incrementStatistic, } from "@allurereport/core-api";
|
|
3
3
|
import { generateCurrentStatusChart } from "./generateCurrentStatusChart.js";
|
|
4
4
|
import { generateDurationDynamicsChart } from "./generateDurationDynamicsChart.js";
|
|
5
5
|
import { generateDurationsChart } from "./generateDurationsChart.js";
|
|
@@ -12,13 +12,67 @@ import { generateTestingPyramidChart } from "./generateTestingPyramidChart.js";
|
|
|
12
12
|
import { generateTrSeveritiesChart } from "./generateTrSeveritiesChart.js";
|
|
13
13
|
import { generateHeatMapChart } from "./heatMap.js";
|
|
14
14
|
import { generateTreeMapChart } from "./treeMap.js";
|
|
15
|
+
const buildFallbackHistoryAliases = (testResults) => {
|
|
16
|
+
const aliases = new Map();
|
|
17
|
+
const ambiguousAliases = new Set();
|
|
18
|
+
for (const testResult of testResults) {
|
|
19
|
+
if (!testResult.historyId) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const fallbackHistoryId = getFallbackHistoryId(testResult);
|
|
23
|
+
if (!fallbackHistoryId || fallbackHistoryId === testResult.historyId) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const existingAlias = aliases.get(fallbackHistoryId);
|
|
27
|
+
if (existingAlias && existingAlias !== testResult.historyId) {
|
|
28
|
+
aliases.delete(fallbackHistoryId);
|
|
29
|
+
ambiguousAliases.add(fallbackHistoryId);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!ambiguousAliases.has(fallbackHistoryId)) {
|
|
33
|
+
aliases.set(fallbackHistoryId, testResult.historyId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return aliases;
|
|
37
|
+
};
|
|
38
|
+
const normalizeHistoryDataPointsByAliases = (historyDataPoints, aliases) => {
|
|
39
|
+
if (aliases.size === 0) {
|
|
40
|
+
return historyDataPoints;
|
|
41
|
+
}
|
|
42
|
+
return historyDataPoints.map((historyDataPoint) => {
|
|
43
|
+
let testResults = historyDataPoint.testResults;
|
|
44
|
+
for (const [legacyHistoryId, primaryHistoryId] of aliases) {
|
|
45
|
+
if (!Object.prototype.hasOwnProperty.call(testResults, legacyHistoryId)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (Object.prototype.hasOwnProperty.call(testResults, primaryHistoryId)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (testResults === historyDataPoint.testResults) {
|
|
52
|
+
testResults = { ...testResults };
|
|
53
|
+
}
|
|
54
|
+
testResults[primaryHistoryId] = {
|
|
55
|
+
...testResults[legacyHistoryId],
|
|
56
|
+
historyId: primaryHistoryId,
|
|
57
|
+
};
|
|
58
|
+
delete testResults[legacyHistoryId];
|
|
59
|
+
}
|
|
60
|
+
if (testResults === historyDataPoint.testResults) {
|
|
61
|
+
return historyDataPoint;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
...historyDataPoint,
|
|
65
|
+
testResults,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
};
|
|
15
69
|
const generateChartData = async (props) => {
|
|
16
|
-
const {
|
|
70
|
+
const { envId, chartsOptions, store, generateUuid, filter } = props;
|
|
17
71
|
const result = {};
|
|
18
72
|
const getTrs = async () => {
|
|
19
73
|
let trs = [];
|
|
20
|
-
if (
|
|
21
|
-
trs = await store.
|
|
74
|
+
if (envId) {
|
|
75
|
+
trs = await store.testResultsByEnvironmentId(envId);
|
|
22
76
|
}
|
|
23
77
|
else {
|
|
24
78
|
trs = await store.allTestResults();
|
|
@@ -30,10 +84,12 @@ const generateChartData = async (props) => {
|
|
|
30
84
|
};
|
|
31
85
|
const getHistoryDataPoints = async () => {
|
|
32
86
|
let historyDataPoints = [];
|
|
33
|
-
if (
|
|
34
|
-
historyDataPoints = await store.
|
|
87
|
+
if (envId) {
|
|
88
|
+
historyDataPoints = await store.allHistoryDataPointsByEnvironmentId(envId);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
historyDataPoints = await store.allHistoryDataPoints();
|
|
35
92
|
}
|
|
36
|
-
historyDataPoints = await store.allHistoryDataPoints();
|
|
37
93
|
if (typeof filter === "function") {
|
|
38
94
|
historyDataPoints = historyDataPoints.map((hdp) => {
|
|
39
95
|
const trsEntries = Object.entries(hdp.testResults);
|
|
@@ -53,26 +109,21 @@ const generateChartData = async (props) => {
|
|
|
53
109
|
}
|
|
54
110
|
return historyDataPoints;
|
|
55
111
|
};
|
|
56
|
-
const getStatistic = () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
return true;
|
|
65
|
-
});
|
|
112
|
+
const getStatistic = async () => {
|
|
113
|
+
const statistic = emptyStatistic();
|
|
114
|
+
for (const tr of await getTrs()) {
|
|
115
|
+
incrementStatistic(statistic, tr.status);
|
|
116
|
+
}
|
|
117
|
+
return statistic;
|
|
66
118
|
};
|
|
67
|
-
const storeData = await Promise.all([
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}));
|
|
119
|
+
const storeData = await Promise.all([getHistoryDataPoints(), getTrs(), getStatistic()]).then(([historyDataPoints, testResults, statistic]) => {
|
|
120
|
+
const fallbackHistoryAliases = buildFallbackHistoryAliases(testResults);
|
|
121
|
+
return {
|
|
122
|
+
historyDataPoints: normalizeHistoryDataPointsByAliases(historyDataPoints, fallbackHistoryAliases),
|
|
123
|
+
testResults,
|
|
124
|
+
statistic,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
76
127
|
for (const chartOption of chartsOptions) {
|
|
77
128
|
const chartId = generateUuid();
|
|
78
129
|
switch (chartOption.type) {
|
|
@@ -122,10 +173,10 @@ const generateChartData = async (props) => {
|
|
|
122
173
|
return result;
|
|
123
174
|
};
|
|
124
175
|
const hasOnlyDefaultEnvironment = (environments) => {
|
|
125
|
-
return environments.length === 1 && environments[0] === DEFAULT_ENVIRONMENT;
|
|
176
|
+
return environments.length === 1 && environments[0].id === DEFAULT_ENVIRONMENT;
|
|
126
177
|
};
|
|
127
178
|
export const generateCharts = async (chartsOptions, store, reportName, generateUuid, filter) => {
|
|
128
|
-
const environments = await store.
|
|
179
|
+
const environments = await store.allEnvironmentIdentities();
|
|
129
180
|
const chartsData = {
|
|
130
181
|
general: await generateChartData({ chartsOptions, store, reportName, generateUuid, filter }),
|
|
131
182
|
byEnv: {},
|
|
@@ -134,11 +185,11 @@ export const generateCharts = async (chartsOptions, store, reportName, generateU
|
|
|
134
185
|
return chartsData;
|
|
135
186
|
}
|
|
136
187
|
for (const environment of environments) {
|
|
137
|
-
chartsData.byEnv[environment] = await generateChartData({
|
|
188
|
+
chartsData.byEnv[environment.id] = await generateChartData({
|
|
138
189
|
chartsOptions,
|
|
139
190
|
store,
|
|
140
191
|
reportName,
|
|
141
|
-
|
|
192
|
+
envId: environment.id,
|
|
142
193
|
generateUuid,
|
|
143
194
|
filter,
|
|
144
195
|
});
|
package/dist/data.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toPosixPath } from "@allurereport/core-api";
|
|
1
2
|
export const ALLURE_LIVE_RELOAD_HASH_STORAGE_KEY = "__allure_report_live_reload_hash__";
|
|
2
3
|
export const ensureReportDataReady = () => new Promise((resolve) => {
|
|
3
4
|
const waitForReady = () => {
|
|
@@ -11,12 +12,19 @@ export const ensureReportDataReady = () => new Promise((resolve) => {
|
|
|
11
12
|
export const loadReportData = async (name) => {
|
|
12
13
|
await ensureReportDataReady();
|
|
13
14
|
return new Promise((resolve, reject) => {
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const dataByName = globalThis.allureReportData ?? {};
|
|
16
|
+
if (name in dataByName) {
|
|
17
|
+
return resolve(dataByName[name]);
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
const posixKey = toPosixPath(name);
|
|
20
|
+
if (posixKey in dataByName) {
|
|
21
|
+
return resolve(dataByName[posixKey]);
|
|
19
22
|
}
|
|
23
|
+
const legacyBackslashKey = posixKey.replace(/\//g, "\\");
|
|
24
|
+
if (legacyBackslashKey in dataByName) {
|
|
25
|
+
return resolve(dataByName[legacyBackslashKey]);
|
|
26
|
+
}
|
|
27
|
+
return reject(new Error(`Data "${name}" not found!`));
|
|
20
28
|
});
|
|
21
29
|
};
|
|
22
30
|
export const reportDataUrl = async (path, contentType = "application/octet-stream", params) => {
|
|
@@ -44,7 +52,13 @@ export class ReportFetchError extends Error {
|
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
export const fetchReportJsonData = async (path, params) => {
|
|
47
|
-
|
|
55
|
+
let url;
|
|
56
|
+
try {
|
|
57
|
+
url = await reportDataUrl(path, undefined, params);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new ReportFetchError(`Failed to fetch ${path}: data not found`, new Response(null, { status: 404, statusText: "Not Found" }));
|
|
61
|
+
}
|
|
48
62
|
const res = await globalThis.fetch(url);
|
|
49
63
|
if (!res.ok) {
|
|
50
64
|
throw new ReportFetchError(`Failed to fetch ${url}, response status: ${res.status}`, res);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { EnvironmentIdentity } from "@allurereport/core-api";
|
|
2
|
+
export declare const environmentNameById: (environments: EnvironmentIdentity[], environmentId: string) => string;
|
|
3
|
+
export declare const migrateStoredEnvironmentSelection: (storedEnvironment: string, environments: EnvironmentIdentity[]) => string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const environmentNameById = (environments, environmentId) => environments.find(({ id }) => id === environmentId)?.name ?? environmentId;
|
|
2
|
+
export const migrateStoredEnvironmentSelection = (storedEnvironment, environments) => {
|
|
3
|
+
if (!storedEnvironment) {
|
|
4
|
+
return "";
|
|
5
|
+
}
|
|
6
|
+
if (environments.some(({ id }) => id === storedEnvironment)) {
|
|
7
|
+
return storedEnvironment;
|
|
8
|
+
}
|
|
9
|
+
const matches = environments.filter(({ name }) => name === storedEnvironment);
|
|
10
|
+
if (matches.length === 1) {
|
|
11
|
+
return matches[0].id;
|
|
12
|
+
}
|
|
13
|
+
return "";
|
|
14
|
+
};
|
package/dist/i18n.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const AVAILABLE_LOCALES: readonly ["en", "en-iso", "ru", "uk", "pl", "es", "pt", "de", "hy", "az", "fr", "it", "ja", "he", "ka", "kr", "nl", "sv", "tr", "zh"];
|
|
1
|
+
export declare const AVAILABLE_LOCALES: readonly ["en", "en-iso", "ru", "uk", "pl", "es", "pt", "de", "hy", "ar", "az", "fr", "it", "ja", "he", "ka", "kr", "nl", "sv", "tr", "zh", "zh-TW"];
|
|
2
2
|
export declare const DEFAULT_LOCALE = "en";
|
|
3
3
|
export type LangLocale = (typeof AVAILABLE_LOCALES)[number];
|
|
4
4
|
export type DateTimeFormatKind = "date" | "dateTime" | "dateTimeNoSeconds";
|
package/dist/i18n.js
CHANGED
|
@@ -8,6 +8,7 @@ export const AVAILABLE_LOCALES = [
|
|
|
8
8
|
"pt",
|
|
9
9
|
"de",
|
|
10
10
|
"hy",
|
|
11
|
+
"ar",
|
|
11
12
|
"az",
|
|
12
13
|
"fr",
|
|
13
14
|
"it",
|
|
@@ -19,6 +20,7 @@ export const AVAILABLE_LOCALES = [
|
|
|
19
20
|
"sv",
|
|
20
21
|
"tr",
|
|
21
22
|
"zh",
|
|
23
|
+
"zh-TW",
|
|
22
24
|
];
|
|
23
25
|
export const DEFAULT_LOCALE = "en";
|
|
24
26
|
export const LOCALE_DATE_TIME_OVERRIDES = {
|
|
@@ -110,6 +112,11 @@ export const LANG_LOCALE = {
|
|
|
110
112
|
full: "Հայերեն",
|
|
111
113
|
iso: "hy-AM",
|
|
112
114
|
},
|
|
115
|
+
"ar": {
|
|
116
|
+
short: "Ar",
|
|
117
|
+
full: "العربية",
|
|
118
|
+
iso: "ar-SA",
|
|
119
|
+
},
|
|
113
120
|
"az": {
|
|
114
121
|
short: "Az",
|
|
115
122
|
full: "Azərbaycan",
|
|
@@ -165,4 +172,9 @@ export const LANG_LOCALE = {
|
|
|
165
172
|
full: "中文",
|
|
166
173
|
iso: "zh-CN",
|
|
167
174
|
},
|
|
175
|
+
"zh-TW": {
|
|
176
|
+
short: "Zh-TW",
|
|
177
|
+
full: "繁體中文",
|
|
178
|
+
iso: "zh-TW",
|
|
179
|
+
},
|
|
168
180
|
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,60 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/web-commons",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"description": "Collection of utilities used across the web Allure reports",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
7
7
|
"testing"
|
|
8
8
|
],
|
|
9
|
-
"repository": "https://github.com/allure-framework/allure3",
|
|
10
9
|
"license": "Apache-2.0",
|
|
11
10
|
"author": "Qameta Software",
|
|
11
|
+
"repository": "https://github.com/allure-framework/allure3",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
12
15
|
"type": "module",
|
|
16
|
+
"module": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
13
18
|
"exports": {
|
|
14
19
|
".": "./dist/index.js"
|
|
15
20
|
},
|
|
16
|
-
"module": "dist/index.js",
|
|
17
|
-
"types": "dist/index.d.ts",
|
|
18
|
-
"files": [
|
|
19
|
-
"dist"
|
|
20
|
-
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "run clean && tsc --project ./tsconfig.json",
|
|
23
23
|
"clean": "rimraf ./dist",
|
|
24
|
-
"test": "vitest run"
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"lint": "oxlint --import-plugin src test features stories",
|
|
26
|
+
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
|
-
"@allurereport/aql": "3.
|
|
28
|
-
"@allurereport/charts-api": "3.
|
|
29
|
-
"@allurereport/core-api": "3.
|
|
30
|
-
"@allurereport/plugin-api": "3.
|
|
29
|
+
"@allurereport/aql": "3.4.1",
|
|
30
|
+
"@allurereport/charts-api": "3.4.1",
|
|
31
|
+
"@allurereport/core-api": "3.4.1",
|
|
32
|
+
"@allurereport/plugin-api": "3.4.1",
|
|
31
33
|
"@preact/signals": "^2.6.1",
|
|
32
34
|
"@preact/signals-core": "^1.12.2",
|
|
33
35
|
"ansi-to-html": "^0.7.2",
|
|
34
36
|
"d3-interpolate": "^3.0.1",
|
|
35
37
|
"d3-scale": "^4.0.2",
|
|
36
38
|
"d3-shape": "^3.2.0",
|
|
37
|
-
"dompurify": "^3.2
|
|
39
|
+
"dompurify": "^3.3.2",
|
|
38
40
|
"nanoid": "^5.1.6"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
|
-
"@stylistic/eslint-plugin": "^2.6.1",
|
|
42
43
|
"@types/d3-interpolate": "^3.0.4",
|
|
43
44
|
"@types/d3-scale": "^4.0.9",
|
|
44
45
|
"@types/d3-shape": "^3.1.6",
|
|
45
|
-
"@types/eslint": "^8.56.11",
|
|
46
|
-
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
47
|
-
"@typescript-eslint/parser": "^8.0.0",
|
|
48
46
|
"@vitest/runner": "^2.1.9",
|
|
49
47
|
"allure-js-commons": "^3.3.0",
|
|
50
48
|
"allure-vitest": "^3.3.0",
|
|
51
|
-
"eslint": "^8.57.0",
|
|
52
|
-
"eslint-config-prettier": "^9.1.0",
|
|
53
|
-
"eslint-plugin-import": "^2.29.1",
|
|
54
|
-
"eslint-plugin-jsdoc": "^50.0.0",
|
|
55
|
-
"eslint-plugin-n": "^17.10.1",
|
|
56
|
-
"eslint-plugin-no-null": "^1.0.2",
|
|
57
|
-
"eslint-plugin-prefer-arrow": "^1.2.3",
|
|
58
49
|
"jsdom": "^26.1.0",
|
|
59
50
|
"rimraf": "^6.0.1",
|
|
60
51
|
"tslib": "^2.7.0",
|