@allurereport/web-awesome 3.1.0 → 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/multi/173.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/174.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/252.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/282.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/29.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/416.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/527.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/600.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/605.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/638.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/672.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/686.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/725.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/741.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/749.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/755.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/894.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/943.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/980.app-d0210ed2e64d38a2ee8e.js +1 -0
- package/dist/multi/app-d0210ed2e64d38a2ee8e.js +2 -0
- package/dist/multi/manifest.json +21 -21
- package/dist/multi/{styles-9e390bad7ce54a807a8e.css → styles-13107bbe6906beabc50f.css} +5 -5
- package/dist/single/app-01fed10ad5f9083fd39c.js +2 -0
- package/dist/single/manifest.json +1 -1
- package/package.json +8 -10
- package/src/components/Charts/index.tsx +5 -5
- package/src/components/MainReport/index.tsx +52 -47
- package/src/components/Metadata/index.tsx +75 -1
- package/src/components/Metadata/styles.scss +10 -0
- package/src/components/ReportQualityGateResults/index.tsx +77 -19
- package/src/components/ReportQualityGateResults/styles.scss +13 -0
- package/src/components/TestResult/TrDescription/index.tsx +60 -10
- package/src/components/TestResult/TrDescription/styles.scss +4 -0
- package/src/components/TestResult/TrError/TrDiff.tsx +2 -6
- package/src/components/TestResult/TrError/styles.scss +4 -0
- package/src/components/TestResult/TrLinks/index.tsx +4 -4
- package/src/components/TestResult/TrOverview.tsx +3 -2
- package/src/components/Timeline/index.tsx +2 -5
- package/src/locales/az.json +3 -2
- package/src/locales/de.json +3 -2
- package/src/locales/en.json +3 -2
- package/src/locales/es.json +3 -2
- package/src/locales/fr.json +3 -2
- package/src/locales/he.json +3 -2
- package/src/locales/hy.json +3 -2
- package/src/locales/it.json +3 -2
- package/src/locales/ja.json +3 -2
- package/src/locales/ka.json +3 -2
- package/src/locales/kr.json +3 -2
- package/src/locales/nl.json +3 -2
- package/src/locales/pl.json +3 -2
- package/src/locales/pt.json +3 -2
- package/src/locales/ru.json +3 -2
- package/src/locales/sv.json +3 -2
- package/src/locales/tr.json +3 -2
- package/src/locales/{ua.json → uk.json} +3 -2
- package/src/locales/zh.json +3 -2
- package/src/stores/locale.ts +69 -37
- package/src/stores/qualityGate.ts +2 -2
- package/src/stores/timeline.ts +5 -2
- package/src/utils/ownerAddress.ts +92 -0
- package/src/utils/time.ts +16 -2
- package/test/utils/ownerAddress.test.ts +89 -0
- package/types.d.ts +1 -1
- package/dist/multi/173.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/174.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/252.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/282.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/29.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/416.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/527.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/600.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/605.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/638.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/672.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/686.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/725.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/741.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/755.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/894.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/91.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/943.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/980.app-79c65c7bff941abcbc51.js +0 -1
- package/dist/multi/app-79c65c7bff941abcbc51.js +0 -2
- package/dist/single/app-3ca67f29d0f1166c08ca.js +0 -2
- /package/dist/multi/{app-79c65c7bff941abcbc51.js.LICENSE.txt → app-d0210ed2e64d38a2ee8e.js.LICENSE.txt} +0 -0
- /package/dist/single/{app-3ca67f29d0f1166c08ca.js.LICENSE.txt → app-01fed10ad5f9083fd39c.js.LICENSE.txt} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/web-awesome",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "The static files for Allure Awesome Report",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -31,19 +31,18 @@
|
|
|
31
31
|
"IE 11"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@allurereport/charts-api": "3.
|
|
35
|
-
"@allurereport/core-api": "3.
|
|
36
|
-
"@allurereport/plugin-api": "3.
|
|
37
|
-
"@allurereport/web-commons": "3.
|
|
38
|
-
"@allurereport/web-components": "3.
|
|
34
|
+
"@allurereport/charts-api": "3.2.0",
|
|
35
|
+
"@allurereport/core-api": "3.2.0",
|
|
36
|
+
"@allurereport/plugin-api": "3.2.0",
|
|
37
|
+
"@allurereport/web-commons": "3.2.0",
|
|
38
|
+
"@allurereport/web-components": "3.2.0",
|
|
39
39
|
"@preact/signals": "^2.6.1",
|
|
40
40
|
"clsx": "^2.1.1",
|
|
41
41
|
"d3-shape": "^3.2.0",
|
|
42
42
|
"i18next": "^24.0.2",
|
|
43
43
|
"md5": "^2.3.0",
|
|
44
44
|
"preact": "^10.28.2",
|
|
45
|
-
"prismjs": "^1.30.0"
|
|
46
|
-
"reset.css": "^2.0.2"
|
|
45
|
+
"prismjs": "^1.30.0"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
48
|
"@babel/core": "^7.27.4",
|
|
@@ -60,7 +59,6 @@
|
|
|
60
59
|
"@testing-library/user-event": "^14.5.1",
|
|
61
60
|
"@types/babel__core": "^7.20.5",
|
|
62
61
|
"@types/d3-shape": "^3.1.6",
|
|
63
|
-
"@types/diff": "^7",
|
|
64
62
|
"@types/eslint": "^8.56.11",
|
|
65
63
|
"@types/md5": "^2.3.5",
|
|
66
64
|
"@types/node": "^20.17.9",
|
|
@@ -74,7 +72,7 @@
|
|
|
74
72
|
"babel-loader": "^9.2.1",
|
|
75
73
|
"babel-plugin-prismjs": "^2.1.0",
|
|
76
74
|
"css-loader": "^7.1.2",
|
|
77
|
-
"diff": "^
|
|
75
|
+
"diff": "^8.0.3",
|
|
78
76
|
"eslint": "^8.57.0",
|
|
79
77
|
"eslint-config-preact": "^1.5.0",
|
|
80
78
|
"eslint-config-prettier": "^9.1.0",
|
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
CurrentStatusChartWidget,
|
|
7
7
|
DurationDynamicsChartWidget,
|
|
8
8
|
DurationsChartWidget,
|
|
9
|
-
FBSUAgePyramidChartWidget,
|
|
10
9
|
Grid,
|
|
11
10
|
GridItem,
|
|
12
11
|
HeatMapWidget,
|
|
13
12
|
Loadable,
|
|
14
13
|
PageLoader,
|
|
15
14
|
StabilityDistributionWidget,
|
|
15
|
+
StatusAgePyramidChartWidget,
|
|
16
16
|
StatusDynamicsChartWidget,
|
|
17
17
|
StatusTransitionsChartWidget,
|
|
18
18
|
TestBaseGrowthDynamicsChartWidget,
|
|
@@ -113,15 +113,15 @@ const getChartWidgetByType = (
|
|
|
113
113
|
/>
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
|
-
case ChartType.
|
|
117
|
-
const title = chartData.title ?? t("
|
|
116
|
+
case ChartType.StatusAgePyramid: {
|
|
117
|
+
const title = chartData.title ?? t("statusAgePyramid.title");
|
|
118
118
|
|
|
119
119
|
return (
|
|
120
|
-
<
|
|
120
|
+
<StatusAgePyramidChartWidget
|
|
121
121
|
title={title}
|
|
122
122
|
data={chartData.data}
|
|
123
123
|
statuses={chartData.statuses}
|
|
124
|
-
i18n={(key, props = {}) => t(`
|
|
124
|
+
i18n={(key, props = {}) => t(`statusAgePyramid.${key}`, props)}
|
|
125
125
|
/>
|
|
126
126
|
);
|
|
127
127
|
}
|
|
@@ -6,8 +6,8 @@ import { ReportGlobalAttachments } from "@/components/ReportGlobalAttachments";
|
|
|
6
6
|
import { ReportGlobalErrors } from "@/components/ReportGlobalErrors";
|
|
7
7
|
import { ReportHeader } from "@/components/ReportHeader";
|
|
8
8
|
import { ReportMetadata } from "@/components/ReportMetadata";
|
|
9
|
-
import { reportStatsStore } from "@/stores";
|
|
10
|
-
import {
|
|
9
|
+
import { reportStatsStore, useI18n } from "@/stores";
|
|
10
|
+
import { currentEnvironment } from "@/stores/env";
|
|
11
11
|
import { globalsStore } from "@/stores/globals";
|
|
12
12
|
import { isSplitMode } from "@/stores/layout";
|
|
13
13
|
import { qualityGateStore } from "@/stores/qualityGate";
|
|
@@ -45,53 +45,58 @@ const MainReport = () => {
|
|
|
45
45
|
const { t } = useI18n("tabs");
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
48
|
+
<div className={clsx(styles.content, isSplitMode.value ? styles["scroll-inside"] : "")}>
|
|
49
|
+
<ReportHeader />
|
|
50
|
+
<div className={styles["main-report-tabs"]}>
|
|
51
|
+
<NavTabs initialTab={ReportRootTab.Results}>
|
|
52
|
+
<NavTabsList>
|
|
53
|
+
<Loadable
|
|
54
|
+
source={reportStatsStore}
|
|
55
|
+
renderData={(stats) => (
|
|
56
|
+
<NavTab id={ReportRootTab.Results}>
|
|
57
|
+
{t("results")} <Counter count={stats?.total ?? 0} />
|
|
58
|
+
</NavTab>
|
|
59
|
+
)}
|
|
60
|
+
/>
|
|
61
|
+
<Loadable
|
|
62
|
+
source={qualityGateStore}
|
|
63
|
+
renderData={(results) => {
|
|
64
|
+
const currentEnvResults = currentEnvironment.value
|
|
65
|
+
? (results[currentEnvironment.value] ?? [])
|
|
66
|
+
: Object.values(results).flatMap((envResults) => envResults);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<NavTab id={ReportRootTab.QualityGate}>
|
|
70
|
+
{t("qualityGates")}{" "}
|
|
71
|
+
<Counter
|
|
72
|
+
status={currentEnvResults.length > 0 ? "failed" : undefined}
|
|
73
|
+
count={currentEnvResults.length}
|
|
74
|
+
/>
|
|
75
|
+
</NavTab>
|
|
76
|
+
);
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
<Loadable
|
|
80
|
+
source={globalsStore}
|
|
81
|
+
renderData={({ attachments = [], errors = [] }) => (
|
|
82
|
+
<>
|
|
83
|
+
<NavTab id={ReportRootTab.GlobalAttachments}>
|
|
84
|
+
{t("globalAttachments")} <Counter count={attachments.length} />
|
|
59
85
|
</NavTab>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/>
|
|
73
|
-
<Loadable
|
|
74
|
-
source={globalsStore}
|
|
75
|
-
renderData={({ attachments = [], errors = [] }) => (
|
|
76
|
-
<>
|
|
77
|
-
<NavTab id={ReportRootTab.GlobalAttachments}>
|
|
78
|
-
{t("globalAttachments")} <Counter count={attachments.length} />
|
|
79
|
-
</NavTab>
|
|
80
|
-
<NavTab id={ReportRootTab.GlobalErrors}>
|
|
81
|
-
{t("globalErrors")}{" "}
|
|
82
|
-
<Counter status={errors.length > 0 ? "failed" : undefined} count={errors.length} />
|
|
83
|
-
</NavTab>
|
|
84
|
-
</>
|
|
85
|
-
)}
|
|
86
|
-
/>
|
|
87
|
-
</NavTabsList>
|
|
88
|
-
<div className={styles["main-report-tabs-content"]}>
|
|
89
|
-
<MainReportContent />
|
|
90
|
-
</div>
|
|
91
|
-
</NavTabs>
|
|
92
|
-
</div>
|
|
86
|
+
<NavTab id={ReportRootTab.GlobalErrors}>
|
|
87
|
+
{t("globalErrors")}{" "}
|
|
88
|
+
<Counter status={errors.length > 0 ? "failed" : undefined} count={errors.length} />
|
|
89
|
+
</NavTab>
|
|
90
|
+
</>
|
|
91
|
+
)}
|
|
92
|
+
/>
|
|
93
|
+
</NavTabsList>
|
|
94
|
+
<div className={styles["main-report-tabs-content"]}>
|
|
95
|
+
<MainReportContent />
|
|
96
|
+
</div>
|
|
97
|
+
</NavTabs>
|
|
93
98
|
</div>
|
|
94
|
-
|
|
99
|
+
</div>
|
|
95
100
|
);
|
|
96
101
|
};
|
|
97
102
|
export default MainReport;
|
|
@@ -7,6 +7,7 @@ import type { MetadataProps } from "@/components/ReportMetadata";
|
|
|
7
7
|
import { useI18n } from "@/stores/locale";
|
|
8
8
|
import { getTagsFilterUrl } from "@/stores/treeFilters/utils";
|
|
9
9
|
import { copyToClipboard } from "@/utils/copyToClipboard";
|
|
10
|
+
import { parseOwnerAddress } from "@/utils/ownerAddress";
|
|
10
11
|
import * as styles from "./styles.scss";
|
|
11
12
|
|
|
12
13
|
export const MetadataList: FunctionalComponent<MetadataProps & { columns?: number }> = ({
|
|
@@ -65,6 +66,49 @@ const OpenFilterUrlButton: FunctionalComponent<{ url: string }> = ({ url }) => {
|
|
|
65
66
|
);
|
|
66
67
|
};
|
|
67
68
|
|
|
69
|
+
const MAX_URL_LENGTH = 25;
|
|
70
|
+
|
|
71
|
+
const OwnerAction = (props: { ownerValue: string }) => {
|
|
72
|
+
const { t } = useI18n("ui");
|
|
73
|
+
const { ownerValue } = props;
|
|
74
|
+
const ownerAddress = parseOwnerAddress(ownerValue);
|
|
75
|
+
|
|
76
|
+
if (ownerAddress.type === "none") {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Don't need to show copy email button here because
|
|
81
|
+
// we already have a button to copy the whole owner value
|
|
82
|
+
if (ownerAddress.type === "email" && ownerAddress.email === ownerValue) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (ownerAddress.type === "email") {
|
|
87
|
+
return (
|
|
88
|
+
<Button
|
|
89
|
+
icon={allureIcons.lineGeneralCopy3}
|
|
90
|
+
style="outline"
|
|
91
|
+
text={t("copy-email")}
|
|
92
|
+
onClick={() => copyToClipboard(ownerAddress.email)}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (ownerAddress.type === "url") {
|
|
98
|
+
const truncatedUrl =
|
|
99
|
+
ownerAddress.url.length > MAX_URL_LENGTH ? `${ownerAddress.url.slice(0, MAX_URL_LENGTH)}...` : ownerAddress.url;
|
|
100
|
+
return (
|
|
101
|
+
<ButtonLink
|
|
102
|
+
href={ownerAddress.url}
|
|
103
|
+
target="_blank"
|
|
104
|
+
style="ghost"
|
|
105
|
+
icon={allureIcons.lineGeneralLinkExternal}
|
|
106
|
+
text={truncatedUrl}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
68
112
|
const MetadataTooltip = (props: { value: string; name: string }) => {
|
|
69
113
|
const { value, name } = props;
|
|
70
114
|
const { t } = useI18n("ui");
|
|
@@ -75,6 +119,7 @@ const MetadataTooltip = (props: { value: string; name: string }) => {
|
|
|
75
119
|
<Text>{value}</Text>
|
|
76
120
|
</div>
|
|
77
121
|
{name === "tag" && <OpenFilterUrlButton url={getTagsFilterUrl([value])} />}
|
|
122
|
+
{name === "owner" && <OwnerAction ownerValue={value} />}
|
|
78
123
|
<Button
|
|
79
124
|
style={"outline"}
|
|
80
125
|
icon={allureIcons.lineGeneralCopy3}
|
|
@@ -85,11 +130,40 @@ const MetadataTooltip = (props: { value: string; name: string }) => {
|
|
|
85
130
|
);
|
|
86
131
|
};
|
|
87
132
|
|
|
133
|
+
const MetaDataOwnerLabel: FunctionalComponent<{
|
|
134
|
+
value: string;
|
|
135
|
+
size?: "s" | "m";
|
|
136
|
+
}> = ({ value, size = "s" }) => {
|
|
137
|
+
const ownerAddress = parseOwnerAddress(value);
|
|
138
|
+
const displayName = ownerAddress.displayName ?? value;
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Menu
|
|
142
|
+
size="xl"
|
|
143
|
+
menuTrigger={({ onClick }) => (
|
|
144
|
+
<div className={styles["report-metadata-keyvalue-wrapper"]}>
|
|
145
|
+
<Text type={"ui"} size={size} onClick={onClick} bold className={styles["report-metadata-keyvalue-value"]}>
|
|
146
|
+
{displayName}
|
|
147
|
+
</Text>
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
>
|
|
151
|
+
<Menu.Section>
|
|
152
|
+
<MetadataTooltip value={value} name={"owner"} />
|
|
153
|
+
</Menu.Section>
|
|
154
|
+
</Menu>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
88
158
|
const MetaDataKeyLabel: FunctionalComponent<{
|
|
89
159
|
name: string;
|
|
90
160
|
size?: "s" | "m";
|
|
91
161
|
value: string;
|
|
92
162
|
}> = ({ name, size = "s", value }) => {
|
|
163
|
+
if (name === "owner") {
|
|
164
|
+
return <MetaDataOwnerLabel value={value} size={size} />;
|
|
165
|
+
}
|
|
166
|
+
|
|
93
167
|
return (
|
|
94
168
|
<Menu
|
|
95
169
|
size="xl"
|
|
@@ -132,7 +206,7 @@ const MetadataKeyValue: FunctionalComponent<{
|
|
|
132
206
|
</div>
|
|
133
207
|
) : (
|
|
134
208
|
<div className={styles["report-metadata-values"]} data-testid={"metadata-item-value"}>
|
|
135
|
-
<MetaDataKeyLabel value={value} name={title} />
|
|
209
|
+
<MetaDataKeyLabel value={value ?? ""} name={title} />
|
|
136
210
|
</div>
|
|
137
211
|
)}
|
|
138
212
|
</div>
|
|
@@ -92,6 +92,16 @@
|
|
|
92
92
|
white-space: nowrap;
|
|
93
93
|
text-overflow: ellipsis;
|
|
94
94
|
cursor: pointer;
|
|
95
|
+
|
|
96
|
+
&:is(a) {
|
|
97
|
+
color: inherit;
|
|
98
|
+
font-weight: bold;
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
|
|
101
|
+
&:hover {
|
|
102
|
+
text-decoration: underline;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
.report-metadata-list {
|
|
@@ -1,40 +1,98 @@
|
|
|
1
|
+
import { DEFAULT_ENVIRONMENT } from "@allurereport/core-api";
|
|
2
|
+
import type { QualityGateValidationResult } from "@allurereport/plugin-api";
|
|
1
3
|
import { Loadable, SvgIcon, Text, allureIcons } from "@allurereport/web-components";
|
|
4
|
+
import { useState } from "preact/hooks";
|
|
5
|
+
import { MetadataButton } from "@/components/MetadataButton";
|
|
2
6
|
import { TrError } from "@/components/TestResult/TrError";
|
|
3
7
|
import { useI18n } from "@/stores";
|
|
8
|
+
import { currentEnvironment } from "@/stores/env";
|
|
4
9
|
import { qualityGateStore } from "@/stores/qualityGate";
|
|
5
10
|
import * as styles from "./styles.scss";
|
|
6
11
|
|
|
12
|
+
const QualityGateResultsList = ({ results }: { results: QualityGateValidationResult[] }) => (
|
|
13
|
+
<ul className={styles["report-quality-gate-results-list"]} data-testid={"quality-gate-results-section-env-content"}>
|
|
14
|
+
{results.map((result) => (
|
|
15
|
+
<li key={result.rule} data-testid="quality-gate-result">
|
|
16
|
+
<div className={styles["report-quality-gate-result"]}>
|
|
17
|
+
<SvgIcon id={allureIcons.solidXCircle} className={styles["report-quality-gate-result-icon"]} />
|
|
18
|
+
<div className={styles["report-quality-gate-result-content"]}>
|
|
19
|
+
<Text tag="p" size="l" type="ui" bold data-testid="quality-gate-result-rule">
|
|
20
|
+
{result.rule}
|
|
21
|
+
</Text>
|
|
22
|
+
<TrError
|
|
23
|
+
className={styles["report-quality-gate-result-error"]}
|
|
24
|
+
message={result.message}
|
|
25
|
+
data-testid="quality-gate-result-message"
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</li>
|
|
30
|
+
))}
|
|
31
|
+
</ul>
|
|
32
|
+
);
|
|
33
|
+
|
|
7
34
|
export const ReportQualityGateResults = () => {
|
|
8
35
|
const { t } = useI18n("empty");
|
|
36
|
+
const { t: tEnvironments } = useI18n("environments");
|
|
37
|
+
const [collapsedEnvs, setCollapsedEnvs] = useState<string[]>([]);
|
|
9
38
|
|
|
10
39
|
return (
|
|
11
40
|
<Loadable
|
|
12
41
|
source={qualityGateStore}
|
|
13
42
|
renderData={(results) => {
|
|
14
|
-
if (
|
|
43
|
+
if (currentEnvironment.value) {
|
|
44
|
+
const currentEnvResults = results[currentEnvironment.value] ?? [];
|
|
45
|
+
|
|
46
|
+
if (!currentEnvResults.length) {
|
|
47
|
+
return <div className={styles["report-quality-gate-results-empty"]}>{t("no-quality-gate-results")}</div>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return <QualityGateResultsList results={currentEnvResults} />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const entries = Object.entries(results).filter(([, envResults]) => envResults.length > 0);
|
|
54
|
+
|
|
55
|
+
if (!entries.length) {
|
|
15
56
|
return <div className={styles["report-quality-gate-results-empty"]}>{t("no-quality-gate-results")}</div>;
|
|
16
57
|
}
|
|
17
58
|
|
|
59
|
+
// single default environment
|
|
60
|
+
if (entries.length === 1 && entries[0][0] === DEFAULT_ENVIRONMENT) {
|
|
61
|
+
const currentEnvResults = entries[0][1] ?? [];
|
|
62
|
+
|
|
63
|
+
if (!currentEnvResults.length) {
|
|
64
|
+
return <div className={styles["report-quality-gate-results-empty"]}>{t("no-quality-gate-results")}</div>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return <QualityGateResultsList results={currentEnvResults} />;
|
|
68
|
+
}
|
|
69
|
+
|
|
18
70
|
return (
|
|
19
|
-
<
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
<div className={styles["report-quality-gate-results"]}>
|
|
72
|
+
{entries.map(([env, envResults]) => {
|
|
73
|
+
const isOpened = !collapsedEnvs.includes(env);
|
|
74
|
+
const toggleEnv = () => {
|
|
75
|
+
setCollapsedEnvs((prev) => (isOpened ? prev.concat(env) : prev.filter((e) => e !== env)));
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div
|
|
80
|
+
key={env}
|
|
81
|
+
className={styles["report-quality-gate-section"]}
|
|
82
|
+
data-testid={"quality-gate-results-section"}
|
|
83
|
+
>
|
|
84
|
+
<MetadataButton
|
|
85
|
+
isOpened={isOpened}
|
|
86
|
+
setIsOpen={toggleEnv}
|
|
87
|
+
title={`${tEnvironments("environment", { count: 1 })}: "${env}"`}
|
|
88
|
+
counter={envResults.length}
|
|
89
|
+
data-testid={"quality-gate-results-section-env-button"}
|
|
90
|
+
/>
|
|
91
|
+
{isOpened && <QualityGateResultsList results={envResults} />}
|
|
34
92
|
</div>
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
</
|
|
93
|
+
);
|
|
94
|
+
})}
|
|
95
|
+
</div>
|
|
38
96
|
);
|
|
39
97
|
}}
|
|
40
98
|
/>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
@import "~@allurereport/web-components/mixins.scss";
|
|
2
2
|
|
|
3
3
|
.report-quality-gate-results {
|
|
4
|
+
padding: 12px 0 32px 8px;
|
|
5
|
+
min-height: 320px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.report-quality-gate-results-list {
|
|
4
9
|
padding: 20px 0;
|
|
5
10
|
|
|
6
11
|
& > li + li {
|
|
@@ -42,3 +47,11 @@
|
|
|
42
47
|
.report-quality-gate-result-error {
|
|
43
48
|
margin-top: 8px;
|
|
44
49
|
}
|
|
50
|
+
|
|
51
|
+
.report-quality-gate-section {
|
|
52
|
+
&:not(:last-child) {
|
|
53
|
+
padding-bottom: 8px;
|
|
54
|
+
margin-bottom: 14px;
|
|
55
|
+
border-bottom: 1px solid var(--on-border-muted);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,25 +1,75 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { proseStyles, resolveCssVarDeclarations, sanitizeIframeHtml, themeStore } from "@allurereport/web-commons";
|
|
2
2
|
import type { FunctionalComponent } from "preact";
|
|
3
|
-
import { useState } from "preact/hooks";
|
|
3
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
4
4
|
import type { AwesomeTestResult } from "types";
|
|
5
5
|
import { MetadataButton } from "@/components/MetadataButton";
|
|
6
6
|
import * as styles from "./styles.scss";
|
|
7
7
|
|
|
8
8
|
export type TrDescriptionProps = {
|
|
9
|
-
|
|
9
|
+
descriptionHtml: AwesomeTestResult["descriptionHtml"];
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const MIN_HEIGHT = 120;
|
|
13
|
+
|
|
14
|
+
export const TrDescription: FunctionalComponent<TrDescriptionProps> = ({ descriptionHtml }) => {
|
|
15
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
16
|
+
const [blobUrl, setBlobUrl] = useState("");
|
|
17
|
+
const [height, setHeight] = useState(MIN_HEIGHT);
|
|
18
|
+
const currentTheme = themeStore.value.current;
|
|
19
|
+
|
|
20
|
+
const sanitized = useMemo(() => (descriptionHtml ? sanitizeIframeHtml(descriptionHtml) : ""), [descriptionHtml]);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!sanitized) {
|
|
24
|
+
setBlobUrl("");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const iframeThemeVars = resolveCssVarDeclarations(proseStyles);
|
|
29
|
+
|
|
30
|
+
const html = `<!DOCTYPE html>
|
|
31
|
+
<html data-theme="${currentTheme}">
|
|
32
|
+
<head>
|
|
33
|
+
<meta charset="utf-8">
|
|
34
|
+
<style>:root {${iframeThemeVars}}</style>
|
|
35
|
+
<style>${proseStyles}</style>
|
|
36
|
+
</head>
|
|
37
|
+
<body>${sanitized}</body>
|
|
38
|
+
</html>`;
|
|
39
|
+
|
|
40
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
41
|
+
const url = URL.createObjectURL(blob);
|
|
42
|
+
setBlobUrl(url);
|
|
43
|
+
|
|
44
|
+
return () => URL.revokeObjectURL(url);
|
|
45
|
+
}, [currentTheme, sanitized]);
|
|
46
|
+
|
|
47
|
+
const handleLoad = (e: Event) => {
|
|
48
|
+
const iframe = e.currentTarget as HTMLIFrameElement;
|
|
49
|
+
const documentElement = iframe.contentDocument?.documentElement;
|
|
50
|
+
const body = iframe.contentDocument?.body;
|
|
51
|
+
const scrollHeight = Math.max(documentElement?.scrollHeight ?? 0, body?.scrollHeight ?? 0);
|
|
52
|
+
setHeight(Math.max(scrollHeight, MIN_HEIGHT));
|
|
53
|
+
};
|
|
14
54
|
|
|
15
55
|
return (
|
|
16
|
-
<div className={styles["test-result-description"]}>
|
|
56
|
+
<div className={styles["test-result-description"]} data-testid="test-result-description">
|
|
17
57
|
<div className={styles["test-result-description-wrapper"]}>
|
|
18
|
-
<MetadataButton title=
|
|
58
|
+
<MetadataButton title="Description" setIsOpen={setIsOpen} isOpened={isOpen} />
|
|
19
59
|
{isOpen && (
|
|
20
|
-
<
|
|
21
|
-
{
|
|
22
|
-
|
|
60
|
+
<div className={styles["test-result-description-text"]}>
|
|
61
|
+
{blobUrl && (
|
|
62
|
+
<iframe
|
|
63
|
+
data-testid="test-result-description-frame"
|
|
64
|
+
src={blobUrl}
|
|
65
|
+
width="100%"
|
|
66
|
+
height={String(height)}
|
|
67
|
+
style={{ border: 0 }}
|
|
68
|
+
sandbox="allow-same-origin"
|
|
69
|
+
onLoad={handleLoad}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
23
73
|
)}
|
|
24
74
|
</div>
|
|
25
75
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button, Code, CodeViewer } from "@allurereport/web-components";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Change } from "diff";
|
|
3
3
|
import { diffChars, diffLines, diffWords } from "diff";
|
|
4
4
|
import { useState } from "preact/hooks";
|
|
5
5
|
import * as styles from "@/components/TestResult/TrError/styles.scss";
|
|
@@ -33,11 +33,7 @@ export const TrDiff = ({ expected, actual }: { expected: string; actual: string
|
|
|
33
33
|
};
|
|
34
34
|
const changeTypeDiff = (type: DiffType = "chars") => {
|
|
35
35
|
const diffFn = diffFunctions[type];
|
|
36
|
-
const result =
|
|
37
|
-
expected,
|
|
38
|
-
actual,
|
|
39
|
-
{},
|
|
40
|
-
);
|
|
36
|
+
const result: Change[] = diffFn(expected, actual, {});
|
|
41
37
|
|
|
42
38
|
setDiffType(type);
|
|
43
39
|
setDiff(result);
|
|
@@ -22,13 +22,13 @@ const linksIconMap: Record<string, string> = {
|
|
|
22
22
|
const TrLink: FunctionalComponent<{
|
|
23
23
|
link: TrLinkProps;
|
|
24
24
|
}> = ({ link }) => {
|
|
25
|
-
const { url, type } = link;
|
|
25
|
+
const { url, name, type } = link;
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<div className={styles["test-result-link"]}>
|
|
28
|
+
<div className={styles["test-result-link"]} data-testid="test-result-meta-link">
|
|
29
29
|
<SvgIcon id={linksIconMap[type] ?? allureIcons.lineGeneralLink1} />
|
|
30
30
|
<Text tag={"a"} href={url} target={"_blank"} size={"m"} className={styles["test-result-link-text"]}>
|
|
31
|
-
{url}
|
|
31
|
+
{name || url}
|
|
32
32
|
</Text>
|
|
33
33
|
</div>
|
|
34
34
|
);
|
|
@@ -46,7 +46,7 @@ export const TrLinks: FunctionalComponent<TrLinksProps> = ({ links }) => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<div className={styles["test-result-links"]}>
|
|
49
|
+
<div className={styles["test-result-links"]} data-testid="test-result-meta-links">
|
|
50
50
|
<div className={styles["test-result-links-wrapper"]}>
|
|
51
51
|
<MetadataButton isOpened={isOpened} setIsOpen={setIsOpen} counter={links.length} title={t("links")} />
|
|
52
52
|
{isOpened && <div className={styles["test-result-links-list"]}>{linkMap}</div>}
|
|
@@ -17,7 +17,8 @@ export type TrOverviewProps = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export const TrOverview: FunctionalComponent<TrOverviewProps> = ({ testResult }) => {
|
|
20
|
-
const { error, parameters, groupedLabels, links,
|
|
20
|
+
const { error, parameters, groupedLabels, links, descriptionHtml, setup, steps, teardown, id, status } =
|
|
21
|
+
testResult || {};
|
|
21
22
|
const isNoSteps = !setup?.length && !steps.length && !teardown.length;
|
|
22
23
|
const pwTraces = testResult?.attachments?.filter(
|
|
23
24
|
(attachment) => attachment.link.contentType === "application/vnd.allure.playwright-trace",
|
|
@@ -34,7 +35,7 @@ export const TrOverview: FunctionalComponent<TrOverviewProps> = ({ testResult })
|
|
|
34
35
|
{Boolean(parameters?.length) && <TrParameters parameters={parameters} />}
|
|
35
36
|
{Boolean(groupedLabels && Object.keys(groupedLabels || {})?.length) && <TrMetadata testResult={testResult} />}
|
|
36
37
|
{Boolean(links?.length) && <TrLinks links={links} />}
|
|
37
|
-
{Boolean(
|
|
38
|
+
{Boolean(descriptionHtml) && <TrDescription descriptionHtml={descriptionHtml} />}
|
|
38
39
|
<div className={styles["test-results"]}>
|
|
39
40
|
{isNoSteps && <TestStepsEmpty />}
|
|
40
41
|
{Boolean(setup?.length) && <TrSetup id={id} setup={setup} />}
|