@allurereport/web-commons 3.0.1 → 3.2.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/attachments.d.ts +27 -14
- package/dist/attachments.js +20 -45
- package/dist/charts/generateStatusAgePyramid.d.ts +5 -0
- package/dist/charts/{generateFBSUAgePyramid.js → generateStatusAgePyramid.js} +29 -17
- package/dist/charts/generators.d.ts +2 -1
- package/dist/charts/generators.js +59 -9
- package/dist/charts/types.d.ts +3 -3
- package/dist/charts/utils.js +1 -1
- package/dist/data.d.ts +5 -1
- package/dist/data.js +8 -2
- package/dist/filters/builders.d.ts +4 -0
- package/dist/filters/builders.js +160 -0
- package/dist/filters/index.d.ts +3 -0
- package/dist/filters/index.js +2 -0
- package/dist/filters/model.d.ts +39 -0
- package/dist/filters/model.js +1 -0
- package/dist/i18n.d.ts +10 -1
- package/dist/i18n.js +69 -20
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/prose.d.ts +2 -0
- package/dist/prose.js +390 -0
- package/dist/sanitize.d.ts +1 -0
- package/dist/sanitize.js +4 -0
- package/dist/stores/loadableStore/constants.d.ts +1 -0
- package/dist/stores/loadableStore/constants.js +1 -0
- package/dist/stores/loadableStore/index.d.ts +3 -0
- package/dist/stores/loadableStore/index.js +2 -0
- package/dist/stores/loadableStore/store.d.ts +13 -0
- package/dist/stores/loadableStore/store.js +47 -0
- package/dist/stores/loadableStore/types.d.ts +7 -0
- package/dist/stores/loadableStore/types.js +1 -0
- package/dist/stores/loadableStore/utils.d.ts +2 -0
- package/dist/stores/loadableStore/utils.js +7 -0
- package/dist/stores/persister/index.d.ts +12 -0
- package/dist/stores/persister/index.js +36 -0
- package/dist/stores/router/index.d.ts +18 -0
- package/dist/stores/router/index.js +95 -0
- package/dist/stores/theme/actions.d.ts +3 -0
- package/dist/stores/theme/actions.js +30 -0
- package/dist/stores/theme/constants.d.ts +6 -0
- package/dist/stores/theme/constants.js +5 -0
- package/dist/stores/theme/index.d.ts +2 -0
- package/dist/stores/theme/index.js +2 -0
- package/dist/stores/theme/store.d.ts +8 -0
- package/dist/stores/theme/store.js +41 -0
- package/dist/stores/theme/types.d.ts +2 -0
- package/dist/stores/theme/types.js +1 -0
- package/dist/stores/theme/utils.d.ts +4 -0
- package/dist/stores/theme/utils.js +23 -0
- package/dist/stores/url/helpers.d.ts +12 -0
- package/dist/stores/url/helpers.js +74 -0
- package/dist/stores/url/index.d.ts +2 -0
- package/dist/stores/url/index.js +2 -0
- package/dist/stores/url/store.d.ts +19 -0
- package/dist/stores/url/store.js +45 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +9 -0
- package/package.json +10 -5
- package/dist/charts/generateFBSUAgePyramid.d.ts +0 -5
package/dist/attachments.d.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
type AttachmentImageData = {
|
|
2
|
+
img: string;
|
|
3
|
+
id?: string;
|
|
4
|
+
};
|
|
5
|
+
type AttachmentTextData = {
|
|
6
|
+
text: string;
|
|
7
|
+
};
|
|
8
|
+
type AttachmentVideoData = {
|
|
9
|
+
src: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
contentType?: string;
|
|
12
|
+
};
|
|
13
|
+
type AttachmentImageDiffData = {
|
|
14
|
+
diff: {
|
|
15
|
+
actual?: string;
|
|
16
|
+
expected?: string;
|
|
17
|
+
diff?: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type AttachmentData = AttachmentImageData | AttachmentTextData | AttachmentVideoData | AttachmentImageDiffData;
|
|
21
|
+
type AttachmentPayload = {
|
|
2
22
|
id?: string;
|
|
3
23
|
ext?: string;
|
|
4
24
|
contentType?: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
export declare const fetchFromUrl: ({ id, ext, contentType }: Attachments) => Promise<Response>;
|
|
10
|
-
export declare const fetchAttachment: (id: string, ext: string, contentType?: string) => Promise<Attachments | null>;
|
|
25
|
+
};
|
|
26
|
+
export declare const fetchFromUrl: ({ id, ext, contentType }: AttachmentPayload) => Promise<Response>;
|
|
27
|
+
export declare const fetchAttachment: (id: string, ext: string, contentType?: string) => Promise<AttachmentData | null>;
|
|
11
28
|
export declare const blobAttachment: (id: string, ext: string, contentType: string) => Promise<Blob>;
|
|
12
29
|
export declare const downloadAttachment: (id: string, ext: string, contentType: string) => Promise<void>;
|
|
13
30
|
export declare const openAttachmentInNewTab: (id: string, ext: string, contentType: string) => Promise<void>;
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
icon: string;
|
|
17
|
-
} | {
|
|
18
|
-
type: null;
|
|
19
|
-
icon: string;
|
|
20
|
-
};
|
|
31
|
+
export type AttachmentType = "css" | "json" | "image" | "svg" | "code" | "text" | "html" | "table" | "video" | "uri" | "archive" | "image-diff";
|
|
32
|
+
export declare const attachmentType: (type?: string) => AttachmentType | null;
|
|
21
33
|
export declare const restrictedContentTypes: string[];
|
|
34
|
+
export {};
|
package/dist/attachments.js
CHANGED
|
@@ -9,7 +9,10 @@ export const fetchAttachment = async (id, ext, contentType) => {
|
|
|
9
9
|
}
|
|
10
10
|
const response = await fetchFromUrl({ id, ext, contentType });
|
|
11
11
|
const fileType = attachmentType(contentType);
|
|
12
|
-
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
throw new Error("Failed to fetch");
|
|
14
|
+
}
|
|
15
|
+
switch (fileType) {
|
|
13
16
|
case "svg":
|
|
14
17
|
case "image": {
|
|
15
18
|
const blob = await response.blob();
|
|
@@ -29,6 +32,10 @@ export const fetchAttachment = async (id, ext, contentType) => {
|
|
|
29
32
|
const src = URL.createObjectURL(blob);
|
|
30
33
|
return { src, id, contentType };
|
|
31
34
|
}
|
|
35
|
+
case "image-diff": {
|
|
36
|
+
const json = await response.json();
|
|
37
|
+
return { diff: json };
|
|
38
|
+
}
|
|
32
39
|
default:
|
|
33
40
|
return null;
|
|
34
41
|
}
|
|
@@ -67,10 +74,7 @@ export const attachmentType = (type) => {
|
|
|
67
74
|
case "image/jpg":
|
|
68
75
|
case "image/png":
|
|
69
76
|
case "image/*":
|
|
70
|
-
return
|
|
71
|
-
type: "image",
|
|
72
|
-
icon: "file",
|
|
73
|
-
};
|
|
77
|
+
return "image";
|
|
74
78
|
case "text/xml":
|
|
75
79
|
case "text/json":
|
|
76
80
|
case "text/yaml":
|
|
@@ -100,63 +104,34 @@ export const attachmentType = (type) => {
|
|
|
100
104
|
case "application/x-yaml":
|
|
101
105
|
case "application/xml":
|
|
102
106
|
case "application/json":
|
|
103
|
-
return
|
|
104
|
-
type: "code",
|
|
105
|
-
icon: "file",
|
|
106
|
-
};
|
|
107
|
+
return "code";
|
|
107
108
|
case "text/plain":
|
|
108
109
|
case "text/markdown":
|
|
109
110
|
case "text/*":
|
|
110
|
-
return
|
|
111
|
-
type: "text",
|
|
112
|
-
icon: "txt",
|
|
113
|
-
};
|
|
111
|
+
return "text";
|
|
114
112
|
case "text/html":
|
|
115
|
-
return
|
|
116
|
-
type: "html",
|
|
117
|
-
icon: "file",
|
|
118
|
-
};
|
|
113
|
+
return "html";
|
|
119
114
|
case "text/csv":
|
|
120
|
-
return {
|
|
121
|
-
type: "table",
|
|
122
|
-
icon: "csv",
|
|
123
|
-
};
|
|
124
115
|
case "text/tab-separated-values":
|
|
125
|
-
return
|
|
126
|
-
type: "table",
|
|
127
|
-
icon: "table",
|
|
128
|
-
};
|
|
116
|
+
return "table";
|
|
129
117
|
case "image/svg+xml":
|
|
130
|
-
return
|
|
131
|
-
type: "svg",
|
|
132
|
-
icon: "file",
|
|
133
|
-
};
|
|
118
|
+
return "svg";
|
|
134
119
|
case "video/mp4":
|
|
135
120
|
case "video/ogg":
|
|
136
121
|
case "video/webm":
|
|
137
|
-
return
|
|
138
|
-
type: "video",
|
|
139
|
-
icon: "file",
|
|
140
|
-
};
|
|
122
|
+
return "video";
|
|
141
123
|
case "text/uri-list":
|
|
142
|
-
return
|
|
143
|
-
type: "uri",
|
|
144
|
-
icon: "list",
|
|
145
|
-
};
|
|
124
|
+
return "uri";
|
|
146
125
|
case "application/x-tar":
|
|
147
126
|
case "application/x-gtar":
|
|
148
127
|
case "application/x-bzip2":
|
|
149
128
|
case "application/gzip":
|
|
150
129
|
case "application/zip":
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
130
|
+
return "archive";
|
|
131
|
+
case "application/vnd.allure.image.diff":
|
|
132
|
+
return "image-diff";
|
|
155
133
|
default:
|
|
156
|
-
return
|
|
157
|
-
type: null,
|
|
158
|
-
icon: "file",
|
|
159
|
-
};
|
|
134
|
+
return null;
|
|
160
135
|
}
|
|
161
136
|
};
|
|
162
137
|
export const restrictedContentTypes = ["application/gzip"];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AllureChartsStoreData, StatusAgePyramidChartData, StatusAgePyramidChartOptions } from "@allurereport/charts-api";
|
|
2
|
+
export declare const generateStatusAgePyramid: (props: {
|
|
3
|
+
options: StatusAgePyramidChartOptions;
|
|
4
|
+
storeData: AllureChartsStoreData;
|
|
5
|
+
}) => StatusAgePyramidChartData;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { ChartType, DEFAULT_CHART_HISTORY_LIMIT } from "@allurereport/charts-api";
|
|
2
|
-
import { htrsByTr } from "@allurereport/core-api";
|
|
3
2
|
import { limitHistoryDataPoints } from "./chart-utils.js";
|
|
4
|
-
const createEmptyStats = (
|
|
5
|
-
return
|
|
3
|
+
const createEmptyStats = () => {
|
|
4
|
+
return STATUSES.reduce((acc, status) => {
|
|
6
5
|
acc[status] = 0;
|
|
7
6
|
return acc;
|
|
8
7
|
}, {});
|
|
9
8
|
};
|
|
10
9
|
const STATUSES = ["failed", "broken", "skipped", "unknown"];
|
|
11
10
|
const isFBSUStatus = (status) => STATUSES.includes(status);
|
|
12
|
-
export const
|
|
11
|
+
export const generateStatusAgePyramid = (props) => {
|
|
13
12
|
const { options, storeData } = props;
|
|
14
13
|
const { limit = DEFAULT_CHART_HISTORY_LIMIT } = options;
|
|
15
14
|
const { historyDataPoints, testResults } = storeData;
|
|
@@ -17,23 +16,35 @@ export const generateFBSUAgePyramid = (props) => {
|
|
|
17
16
|
const limitedHistoryPoints = limitHistoryDataPoints(historyDataPoints, limit).sort((a, b) => a.timestamp - b.timestamp);
|
|
18
17
|
if (limitedHistoryPoints.length === 0) {
|
|
19
18
|
return {
|
|
20
|
-
type: ChartType.
|
|
19
|
+
type: ChartType.StatusAgePyramid,
|
|
21
20
|
title: options.title,
|
|
22
21
|
data: [
|
|
23
22
|
{
|
|
24
23
|
id: "current",
|
|
25
24
|
timestamp: currentReportTimestamp,
|
|
26
|
-
...createEmptyStats(
|
|
25
|
+
...createEmptyStats(),
|
|
27
26
|
},
|
|
28
27
|
],
|
|
29
28
|
statuses: STATUSES,
|
|
30
29
|
};
|
|
31
30
|
}
|
|
32
|
-
const
|
|
31
|
+
const hdps = limitedHistoryPoints.map((datapoint) => ({
|
|
32
|
+
...datapoint,
|
|
33
|
+
testResults: Object.values(datapoint.testResults).reduce((acc, testResult) => {
|
|
34
|
+
if (!testResult.historyId) {
|
|
35
|
+
return acc;
|
|
36
|
+
}
|
|
37
|
+
const isInCurrentRun = testResults.findIndex((tr) => tr.historyId === testResult.historyId) !== -1;
|
|
38
|
+
if (isInCurrentRun) {
|
|
39
|
+
acc[testResult.historyId] = testResult;
|
|
40
|
+
}
|
|
41
|
+
return acc;
|
|
42
|
+
}, {}),
|
|
43
|
+
}));
|
|
33
44
|
const dataPoints = [
|
|
34
45
|
...hdps.map((hdp) => ({
|
|
35
46
|
...hdp,
|
|
36
|
-
...createEmptyStats(
|
|
47
|
+
...createEmptyStats(),
|
|
37
48
|
})),
|
|
38
49
|
{
|
|
39
50
|
testResults: testResults.reduce((acc, testResult) => {
|
|
@@ -42,22 +53,23 @@ export const generateFBSUAgePyramid = (props) => {
|
|
|
42
53
|
}, {}),
|
|
43
54
|
uuid: "current",
|
|
44
55
|
timestamp: currentReportTimestamp,
|
|
45
|
-
...createEmptyStats(
|
|
56
|
+
...createEmptyStats(),
|
|
46
57
|
},
|
|
47
58
|
];
|
|
48
|
-
dataPoints.forEach((dp, index,
|
|
59
|
+
dataPoints.forEach((dp, index, dps) => {
|
|
49
60
|
const { testResults: trs } = dp;
|
|
50
|
-
const
|
|
51
|
-
const hpsPriorToCurrent = isFirst ? [earliestHdp] : dataPointsAscending.slice(0, index);
|
|
61
|
+
const historyAfter = dps.slice(index, dps.length - 1);
|
|
52
62
|
const currentTrs = Object.values(trs);
|
|
53
63
|
for (const cTr of currentTrs) {
|
|
54
|
-
|
|
64
|
+
const currentTrStatus = cTr.status;
|
|
65
|
+
if (!isFBSUStatus(currentTrStatus)) {
|
|
55
66
|
continue;
|
|
56
67
|
}
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
|
|
68
|
+
const historyAfterTrsStatuses = historyAfter.map((hdp) => hdp.testResults[cTr.historyId]?.status ?? undefined);
|
|
69
|
+
if (historyAfterTrsStatuses.some((status) => status !== currentTrStatus)) {
|
|
70
|
+
continue;
|
|
60
71
|
}
|
|
72
|
+
dp[currentTrStatus]++;
|
|
61
73
|
}
|
|
62
74
|
});
|
|
63
75
|
const data = dataPoints.map(({ uuid, timestamp, ...stats }) => ({
|
|
@@ -69,7 +81,7 @@ export const generateFBSUAgePyramid = (props) => {
|
|
|
69
81
|
unknown: stats.unknown ?? 0,
|
|
70
82
|
}));
|
|
71
83
|
return {
|
|
72
|
-
type: ChartType.
|
|
84
|
+
type: ChartType.StatusAgePyramid,
|
|
73
85
|
title: options.title,
|
|
74
86
|
data: data,
|
|
75
87
|
statuses: STATUSES,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChartOptions, type GeneratedChartsData } from "@allurereport/charts-api";
|
|
2
|
+
import type { TestResult } from "@allurereport/core-api";
|
|
2
3
|
import { type AllureStore } from "@allurereport/plugin-api";
|
|
3
4
|
type ChartsWidgetData = {
|
|
4
5
|
general: GeneratedChartsData;
|
|
@@ -6,5 +7,5 @@ type ChartsWidgetData = {
|
|
|
6
7
|
[env: string]: GeneratedChartsData;
|
|
7
8
|
};
|
|
8
9
|
};
|
|
9
|
-
export declare const generateCharts: (chartsOptions: ChartOptions[], store: AllureStore, reportName: string, generateUuid: () => string) => Promise<ChartsWidgetData>;
|
|
10
|
+
export declare const generateCharts: (chartsOptions: ChartOptions[], store: AllureStore, reportName: string, generateUuid: () => string, filter?: (testResult: TestResult) => boolean) => Promise<ChartsWidgetData>;
|
|
10
11
|
export {};
|
|
@@ -3,8 +3,8 @@ import { DEFAULT_ENVIRONMENT } 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";
|
|
6
|
-
import { generateFBSUAgePyramid } from "./generateFBSUAgePyramid.js";
|
|
7
6
|
import { generateStabilityDistributionChart } from "./generateStabilityDistributionChart.js";
|
|
7
|
+
import { generateStatusAgePyramid } from "./generateStatusAgePyramid.js";
|
|
8
8
|
import { generateStatusDynamicsChart } from "./generateStatusDynamicsChart.js";
|
|
9
9
|
import { generateStatusTransitionsChart } from "./generateStatusTransitionsChart.js";
|
|
10
10
|
import { generateTestBaseGrowthDynamicsChart } from "./generateTestBaseGrowthDynamicsChart.js";
|
|
@@ -13,12 +13,61 @@ import { generateTrSeveritiesChart } from "./generateTrSeveritiesChart.js";
|
|
|
13
13
|
import { generateHeatMapChart } from "./heatMap.js";
|
|
14
14
|
import { generateTreeMapChart } from "./treeMap.js";
|
|
15
15
|
const generateChartData = async (props) => {
|
|
16
|
-
const { env, chartsOptions, store,
|
|
16
|
+
const { env, chartsOptions, store, generateUuid, filter } = props;
|
|
17
17
|
const result = {};
|
|
18
|
+
const getTrs = async () => {
|
|
19
|
+
let trs = [];
|
|
20
|
+
if (env) {
|
|
21
|
+
trs = await store.testResultsByEnvironment(env);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
trs = await store.allTestResults();
|
|
25
|
+
}
|
|
26
|
+
if (filter) {
|
|
27
|
+
trs = trs.filter(filter);
|
|
28
|
+
}
|
|
29
|
+
return trs;
|
|
30
|
+
};
|
|
31
|
+
const getHistoryDataPoints = async () => {
|
|
32
|
+
let historyDataPoints = [];
|
|
33
|
+
if (env) {
|
|
34
|
+
historyDataPoints = await store.allHistoryDataPointsByEnvironment(env);
|
|
35
|
+
}
|
|
36
|
+
historyDataPoints = await store.allHistoryDataPoints();
|
|
37
|
+
if (typeof filter === "function") {
|
|
38
|
+
historyDataPoints = historyDataPoints.map((hdp) => {
|
|
39
|
+
const trsEntries = Object.entries(hdp.testResults);
|
|
40
|
+
const filteredTrsEntries = trsEntries.filter(([, tr]) => {
|
|
41
|
+
try {
|
|
42
|
+
return filter(tr);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
...hdp,
|
|
50
|
+
testResults: Object.fromEntries(filteredTrsEntries),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return historyDataPoints;
|
|
55
|
+
};
|
|
56
|
+
const getStatistic = () => {
|
|
57
|
+
return store.testsStatistic((tr) => {
|
|
58
|
+
if (env && tr.environment !== env) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (typeof filter === "function" && !filter(tr)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
});
|
|
66
|
+
};
|
|
18
67
|
const storeData = await Promise.all([
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
68
|
+
await getHistoryDataPoints(),
|
|
69
|
+
await getTrs(),
|
|
70
|
+
await getStatistic(),
|
|
22
71
|
]).then(([historyDataPoints, testResults, statistic]) => ({
|
|
23
72
|
historyDataPoints,
|
|
24
73
|
testResults,
|
|
@@ -48,8 +97,8 @@ const generateChartData = async (props) => {
|
|
|
48
97
|
case ChartType.TestBaseGrowthDynamics:
|
|
49
98
|
result[chartId] = generateTestBaseGrowthDynamicsChart({ options: chartOption, storeData });
|
|
50
99
|
break;
|
|
51
|
-
case ChartType.
|
|
52
|
-
result[chartId] =
|
|
100
|
+
case ChartType.StatusAgePyramid:
|
|
101
|
+
result[chartId] = generateStatusAgePyramid({ options: chartOption, storeData });
|
|
53
102
|
break;
|
|
54
103
|
case ChartType.TrSeverities:
|
|
55
104
|
result[chartId] = generateTrSeveritiesChart({ options: chartOption, storeData });
|
|
@@ -75,10 +124,10 @@ const generateChartData = async (props) => {
|
|
|
75
124
|
const hasOnlyDefaultEnvironment = (environments) => {
|
|
76
125
|
return environments.length === 1 && environments[0] === DEFAULT_ENVIRONMENT;
|
|
77
126
|
};
|
|
78
|
-
export const generateCharts = async (chartsOptions, store, reportName, generateUuid) => {
|
|
127
|
+
export const generateCharts = async (chartsOptions, store, reportName, generateUuid, filter) => {
|
|
79
128
|
const environments = await store.allEnvironments();
|
|
80
129
|
const chartsData = {
|
|
81
|
-
general: await generateChartData({ chartsOptions, store, reportName, generateUuid }),
|
|
130
|
+
general: await generateChartData({ chartsOptions, store, reportName, generateUuid, filter }),
|
|
82
131
|
byEnv: {},
|
|
83
132
|
};
|
|
84
133
|
if (hasOnlyDefaultEnvironment(environments)) {
|
|
@@ -91,6 +140,7 @@ export const generateCharts = async (chartsOptions, store, reportName, generateU
|
|
|
91
140
|
reportName,
|
|
92
141
|
env: environment,
|
|
93
142
|
generateUuid,
|
|
143
|
+
filter,
|
|
94
144
|
});
|
|
95
145
|
}
|
|
96
146
|
return chartsData;
|
package/dist/charts/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChartId, ChartType, DurationDynamicsChartData, DurationsChartData,
|
|
1
|
+
import type { ChartId, ChartType, DurationDynamicsChartData, DurationsChartData, HeatMapSerie, StabilityDistributionChartData, StatusAgePyramidChartData, StatusTransitionsChartData, TestBaseGrowthDynamicsChartData, TestingPyramidChartData, TrSeveritiesChartData, TreeMapNode } from "@allurereport/charts-api";
|
|
2
2
|
import type { Statistic, TestStatus } from "@allurereport/core-api";
|
|
3
3
|
export type TreeMapTooltipAccessor = <T>(node: T) => string[];
|
|
4
4
|
export interface Point {
|
|
@@ -58,8 +58,8 @@ export interface UITreeMapChartData extends ResponseTreeMapChartData {
|
|
|
58
58
|
export interface UIHeatMapChartData extends ResponseHeatMapChartData {
|
|
59
59
|
colors: (value: number, domain?: number[]) => string;
|
|
60
60
|
}
|
|
61
|
-
export type ChartData = CurrentStatusChartData | StatusDynamicsChartData | StatusTransitionsChartData | DurationsChartData | StabilityDistributionChartData | ResponseTreeMapChartData | ResponseHeatMapChartData | TestBaseGrowthDynamicsChartData |
|
|
62
|
-
export type UIChartData = UICurrentStatusChartData | UIStatusDynamicsChartData | UITreeMapChartData | UIHeatMapChartData | UIStatusTransitionsChartData | UIDurationsChartData | TestBaseGrowthDynamicsChartData |
|
|
61
|
+
export type ChartData = CurrentStatusChartData | StatusDynamicsChartData | StatusTransitionsChartData | DurationsChartData | StabilityDistributionChartData | ResponseTreeMapChartData | ResponseHeatMapChartData | TestBaseGrowthDynamicsChartData | StatusAgePyramidChartData | TrSeveritiesChartData | DurationDynamicsChartData | TestingPyramidChartData;
|
|
62
|
+
export type UIChartData = UICurrentStatusChartData | UIStatusDynamicsChartData | UITreeMapChartData | UIHeatMapChartData | UIStatusTransitionsChartData | UIDurationsChartData | TestBaseGrowthDynamicsChartData | StatusAgePyramidChartData | StabilityDistributionChartData | TrSeveritiesChartData | DurationDynamicsChartData | TestingPyramidChartData;
|
|
63
63
|
export type ChartsData = Record<ChartId, ChartData>;
|
|
64
64
|
export type ChartsDataWithEnvs = {
|
|
65
65
|
general: Record<ChartId, ChartData>;
|
package/dist/charts/utils.js
CHANGED
|
@@ -107,7 +107,7 @@ export const createCharts = (res) => {
|
|
|
107
107
|
else if (chart.type === ChartType.TestBaseGrowthDynamics) {
|
|
108
108
|
acc[chartId] = res[chartId];
|
|
109
109
|
}
|
|
110
|
-
else if (chart.type === ChartType.
|
|
110
|
+
else if (chart.type === ChartType.StatusAgePyramid) {
|
|
111
111
|
acc[chartId] = res[chartId];
|
|
112
112
|
}
|
|
113
113
|
else if (chart.type === ChartType.TrSeverities) {
|
package/dist/data.d.ts
CHANGED
|
@@ -4,8 +4,12 @@ export declare const loadReportData: (name: string) => Promise<string>;
|
|
|
4
4
|
export declare const reportDataUrl: (path: string, contentType?: string, params?: {
|
|
5
5
|
bustCache: boolean;
|
|
6
6
|
}) => Promise<string>;
|
|
7
|
+
export declare class ReportFetchError extends Error {
|
|
8
|
+
readonly response: Response;
|
|
9
|
+
constructor(message: string, response: Response);
|
|
10
|
+
}
|
|
7
11
|
export declare const fetchReportJsonData: <T>(path: string, params?: {
|
|
8
12
|
bustCache: boolean;
|
|
9
13
|
}) => Promise<T>;
|
|
10
14
|
export declare const fetchReportAttachment: (path: string, contentType?: string) => Promise<Response>;
|
|
11
|
-
export declare const getReportOptions: <T>() => T
|
|
15
|
+
export declare const getReportOptions: <T>() => Readonly<T>;
|
package/dist/data.js
CHANGED
|
@@ -28,7 +28,7 @@ export const reportDataUrl = async (path, contentType = "application/octet-strea
|
|
|
28
28
|
const baseEl = globalThis.document.head.querySelector("base")?.href ?? "https://localhost";
|
|
29
29
|
const url = new URL(path, baseEl);
|
|
30
30
|
const liveReloadHash = globalThis.localStorage.getItem(ALLURE_LIVE_RELOAD_HASH_STORAGE_KEY);
|
|
31
|
-
const cacheKey =
|
|
31
|
+
const cacheKey = getReportOptions()?.cacheKey;
|
|
32
32
|
if (liveReloadHash) {
|
|
33
33
|
url.searchParams.set("live_reload_hash", liveReloadHash);
|
|
34
34
|
}
|
|
@@ -37,11 +37,17 @@ export const reportDataUrl = async (path, contentType = "application/octet-strea
|
|
|
37
37
|
}
|
|
38
38
|
return url.toString();
|
|
39
39
|
};
|
|
40
|
+
export class ReportFetchError extends Error {
|
|
41
|
+
constructor(message, response) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.response = response;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
40
46
|
export const fetchReportJsonData = async (path, params) => {
|
|
41
47
|
const url = await reportDataUrl(path, undefined, params);
|
|
42
48
|
const res = await globalThis.fetch(url);
|
|
43
49
|
if (!res.ok) {
|
|
44
|
-
throw new
|
|
50
|
+
throw new ReportFetchError(`Failed to fetch ${url}, response status: ${res.status}`, res);
|
|
45
51
|
}
|
|
46
52
|
const data = res.json();
|
|
47
53
|
return data;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AqlExpression } from "@allurereport/aql";
|
|
2
|
+
import { type Filter } from "./model.js";
|
|
3
|
+
export declare const buildFieldFilters: <T extends string = string>(filters: Filter<T>[]) => AqlExpression;
|
|
4
|
+
export declare const buildFilterPredicate: <Keys extends string = string>(filters: Filter<Keys>[]) => (item: Record<Keys, any>) => boolean;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { aqlArrayConditionExpression, aqlBinaryExpression, aqlConditionExpression, aqlParenExpression, createAqlPredicate, } from "@allurereport/aql";
|
|
2
|
+
import { MAX_ARRAY_FIELD_VALUES, } from "./model.js";
|
|
3
|
+
const buildAqlFromFieldFilter = (field) => {
|
|
4
|
+
const { value: fieldValue } = field;
|
|
5
|
+
const { key, type, strict, value } = fieldValue;
|
|
6
|
+
if (type === "array") {
|
|
7
|
+
if (value.length === 0) {
|
|
8
|
+
throw new Error("ArrayField value cannot be empty");
|
|
9
|
+
}
|
|
10
|
+
if (strict === false) {
|
|
11
|
+
return buildArrayIntersectionFilter(key, value, MAX_ARRAY_FIELD_VALUES);
|
|
12
|
+
}
|
|
13
|
+
return aqlArrayConditionExpression({
|
|
14
|
+
type: "arrayCondition",
|
|
15
|
+
left: {
|
|
16
|
+
identifier: key,
|
|
17
|
+
},
|
|
18
|
+
operator: "IN",
|
|
19
|
+
right: value.map((item) => ({
|
|
20
|
+
value: item,
|
|
21
|
+
type: "STRING",
|
|
22
|
+
})),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
let expressionValue;
|
|
26
|
+
let valueType;
|
|
27
|
+
let operator = "EQ";
|
|
28
|
+
switch (type) {
|
|
29
|
+
case "number": {
|
|
30
|
+
expressionValue = String(value);
|
|
31
|
+
operator = (strict ?? true) ? "EQ" : "CONTAINS";
|
|
32
|
+
valueType = "NUMBER";
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case "boolean": {
|
|
36
|
+
expressionValue = value ? "true" : "false";
|
|
37
|
+
valueType = "BOOLEAN";
|
|
38
|
+
operator = "EQ";
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "string": {
|
|
42
|
+
expressionValue = String(value);
|
|
43
|
+
operator = (strict ?? true) ? "EQ" : "CONTAINS";
|
|
44
|
+
valueType = "STRING";
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
default: {
|
|
48
|
+
const exhaustiveCheck = type;
|
|
49
|
+
throw new Error(`Unsupported field type: ${String(exhaustiveCheck)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return aqlConditionExpression({
|
|
53
|
+
type: "condition",
|
|
54
|
+
left: {
|
|
55
|
+
identifier: key,
|
|
56
|
+
},
|
|
57
|
+
operator,
|
|
58
|
+
right: {
|
|
59
|
+
value: expressionValue,
|
|
60
|
+
type: valueType,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
export const buildFieldFilters = (filters) => {
|
|
65
|
+
if (filters.length === 0) {
|
|
66
|
+
throw new Error("chainFieldFilters: filters array cannot be empty");
|
|
67
|
+
}
|
|
68
|
+
const buildAqlFromFilterGroup = (group) => {
|
|
69
|
+
const { value } = group;
|
|
70
|
+
if (value.length === 0) {
|
|
71
|
+
throw new Error("buildFieldFilters: value array cannot be empty");
|
|
72
|
+
}
|
|
73
|
+
if (value.length === 1) {
|
|
74
|
+
return buildFieldFilters([value[0]]);
|
|
75
|
+
}
|
|
76
|
+
return aqlParenExpression({
|
|
77
|
+
type: "paren",
|
|
78
|
+
expression: buildFieldFilters(value),
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
if (filters.length === 1) {
|
|
82
|
+
const [filter] = filters;
|
|
83
|
+
if (filter.type === "field") {
|
|
84
|
+
return buildAqlFromFieldFilter(filter);
|
|
85
|
+
}
|
|
86
|
+
return buildAqlFromFilterGroup(filter);
|
|
87
|
+
}
|
|
88
|
+
const [first, second, ...rest] = filters;
|
|
89
|
+
if (rest.length === 0) {
|
|
90
|
+
return aqlBinaryExpression({
|
|
91
|
+
type: "binary",
|
|
92
|
+
operator: first.logicalOperator ?? "AND",
|
|
93
|
+
left: buildFieldFilters([first]),
|
|
94
|
+
right: buildFieldFilters([second]),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return rest.reduce((acc, filter) => {
|
|
98
|
+
return aqlBinaryExpression({
|
|
99
|
+
type: "binary",
|
|
100
|
+
operator: filter.logicalOperator ?? "AND",
|
|
101
|
+
left: acc,
|
|
102
|
+
right: buildFieldFilters([filter]),
|
|
103
|
+
});
|
|
104
|
+
}, buildFieldFilters([first, second]));
|
|
105
|
+
};
|
|
106
|
+
const buildArrayIntersectionFilter = (key, values, maxIndex = 20) => {
|
|
107
|
+
if (values.length === 0) {
|
|
108
|
+
throw new Error("buildArrayIntersectionFilter: values array cannot be empty");
|
|
109
|
+
}
|
|
110
|
+
const conditionsPerValue = values.map((value) => {
|
|
111
|
+
const indexConditions = Array.from({ length: maxIndex + 1 }, (_, index) => {
|
|
112
|
+
return aqlConditionExpression({
|
|
113
|
+
type: "condition",
|
|
114
|
+
left: {
|
|
115
|
+
identifier: key,
|
|
116
|
+
param: {
|
|
117
|
+
value: index,
|
|
118
|
+
type: "number",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
operator: "EQ",
|
|
122
|
+
right: {
|
|
123
|
+
value,
|
|
124
|
+
type: "STRING",
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
if (indexConditions.length === 1) {
|
|
129
|
+
return indexConditions[0];
|
|
130
|
+
}
|
|
131
|
+
return indexConditions.reduce((acc, condition, index) => {
|
|
132
|
+
if (index === 0) {
|
|
133
|
+
return condition;
|
|
134
|
+
}
|
|
135
|
+
return aqlBinaryExpression({
|
|
136
|
+
type: "binary",
|
|
137
|
+
operator: "OR",
|
|
138
|
+
left: acc,
|
|
139
|
+
right: condition,
|
|
140
|
+
});
|
|
141
|
+
}, indexConditions[0]);
|
|
142
|
+
});
|
|
143
|
+
if (conditionsPerValue.length === 1) {
|
|
144
|
+
return conditionsPerValue[0];
|
|
145
|
+
}
|
|
146
|
+
return conditionsPerValue.reduce((acc, condition, index) => {
|
|
147
|
+
if (index === 0) {
|
|
148
|
+
return condition;
|
|
149
|
+
}
|
|
150
|
+
return aqlBinaryExpression({
|
|
151
|
+
type: "binary",
|
|
152
|
+
operator: "OR",
|
|
153
|
+
left: acc,
|
|
154
|
+
right: condition,
|
|
155
|
+
});
|
|
156
|
+
}, conditionsPerValue[0]);
|
|
157
|
+
};
|
|
158
|
+
export const buildFilterPredicate = (filters) => {
|
|
159
|
+
return createAqlPredicate(buildFieldFilters(filters));
|
|
160
|
+
};
|