@allurereport/web-awesome 3.0.0-beta.4 → 3.0.0-beta.6
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/.eslintrc.cjs +1 -1
- package/CONTRIBUTING.md +34 -0
- package/dist/multi/141.app-07a6d2ea.js +1 -0
- package/dist/multi/222.app-07a6d2ea.js +1 -0
- package/dist/multi/335.app-07a6d2ea.js +1 -0
- package/dist/multi/34.app-07a6d2ea.js +1 -0
- package/dist/multi/349.app-07a6d2ea.js +1 -0
- package/dist/multi/378.app-07a6d2ea.js +1 -0
- package/dist/multi/406.app-07a6d2ea.js +1 -0
- package/dist/multi/476.app-07a6d2ea.js +1 -0
- package/dist/multi/53.app-07a6d2ea.js +1 -0
- package/dist/multi/584.app-07a6d2ea.js +1 -0
- package/dist/multi/690.app-07a6d2ea.js +1 -0
- package/dist/multi/747.app-07a6d2ea.js +1 -0
- package/dist/multi/767.app-07a6d2ea.js +1 -0
- package/dist/multi/{816.app-13f840d5.js → 816.app-07a6d2ea.js} +1 -1
- package/dist/multi/83.app-07a6d2ea.js +1 -0
- package/dist/multi/873.app-07a6d2ea.js +1 -0
- package/dist/multi/920.app-07a6d2ea.js +1 -0
- package/dist/multi/991.app-07a6d2ea.js +1 -0
- package/dist/multi/app-07a6d2ea.js +2 -0
- package/dist/multi/manifest.json +20 -20
- package/dist/multi/{styles-13f840d5.css → styles-07a6d2ea.css} +7 -7
- package/dist/single/app-c2627b69.js +2 -0
- package/dist/single/manifest.json +1 -1
- package/package.json +11 -4
- package/src/assets/svg/line-alerts-alert-circle.svg +12 -0
- package/src/assets/svg/line-general-eye.svg +7 -0
- package/src/assets/svg/line-icon-bomb-2.svg +12 -0
- package/src/components/app/ArrowButton/index.tsx +3 -2
- package/src/components/app/ArrowButton/styles.scss +3 -0
- package/src/components/app/BaseLayout/index.tsx +5 -5
- package/src/components/app/ReportBody/Filters.tsx +12 -10
- package/src/components/app/ReportBody/HeaderActions.tsx +3 -3
- package/src/components/app/ReportBody/SortBy.tsx +8 -8
- package/src/components/app/ReportBody/context.tsx +0 -1
- package/src/components/app/ReportMetadata/MetadataSummary.tsx +22 -18
- package/src/components/app/ReportMetadata/MetadataWithIcon.tsx +2 -2
- package/src/components/app/Tabs/index.tsx +2 -3
- package/src/components/app/TestResult/TestResultDescription/index.tsx +3 -3
- package/src/components/app/TestResult/TestResultError/index.tsx +18 -16
- package/src/components/app/TestResult/TestResultError/styles.scss +16 -5
- package/src/components/app/TestResult/TestResultHeader/index.tsx +2 -2
- package/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx +21 -10
- package/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx +31 -0
- package/src/components/app/TestResult/TestResultInfo/index.tsx +6 -4
- package/src/components/app/TestResult/TestResultInfo/styles.scss +17 -0
- package/src/components/app/TestResult/TestResultNavigation/index.tsx +34 -38
- package/src/components/app/TestResult/TestResultNavigation/styles.scss +1 -1
- package/src/components/app/TestResult/TestResultPrevStatuses/index.tsx +6 -6
- package/src/components/app/TestResult/TestResultSteps/attachment.tsx +4 -6
- package/src/components/app/TestResult/TestResultTeardown/index.tsx +2 -2
- package/src/components/app/TestResult/index.tsx +2 -2
- package/src/components/app/Tree/Tree.tsx +54 -101
- package/src/components/app/Tree/TreeHeader.tsx +13 -12
- package/src/components/app/Tree/TreeItem.tsx +5 -1
- package/src/components/app/Tree/index.tsx +31 -7
- package/src/components/app/Tree/styles.scss +16 -4
- package/src/components/commons/Button/styles.scss +6 -4
- package/src/components/commons/Menu/index.tsx +4 -4
- package/src/components/commons/SearchBox/index.tsx +8 -6
- package/src/components/commons/Toggle/index.tsx +2 -1
- package/src/components/commons/Tooltip/index.tsx +3 -3
- package/src/i18n/constants.ts +23 -4
- package/src/i18n/locales/am.json +5 -2
- package/src/i18n/locales/az.json +5 -2
- package/src/i18n/locales/de.json +5 -2
- package/src/i18n/locales/en.json +6 -3
- package/src/i18n/locales/es.json +5 -1
- package/src/i18n/locales/fr.json +5 -2
- package/src/i18n/locales/he.json +5 -2
- package/src/i18n/locales/it.json +5 -2
- package/src/i18n/locales/ja.json +5 -2
- package/src/i18n/locales/ka.json +5 -2
- package/src/i18n/locales/kr.json +5 -2
- package/src/i18n/locales/nl.json +5 -2
- package/src/i18n/locales/pl.json +5 -2
- package/src/i18n/locales/pt.json +5 -2
- package/src/i18n/locales/ru.json +5 -2
- package/src/i18n/locales/sv.json +5 -2
- package/src/i18n/locales/tr.json +5 -2
- package/src/i18n/locales/zh.json +6 -3
- package/src/index.html +1 -0
- package/src/index.tsx +4 -0
- package/src/stores/chart.ts +2 -2
- package/src/stores/stats.ts +1 -1
- package/src/stores/testResults.ts +26 -4
- package/src/stores/tree.ts +98 -4
- package/src/types/globals.d.ts +6 -1
- package/src/utils/copyToClipboard.ts +14 -1
- package/src/utils/treeFilters.ts +73 -120
- package/test/utils/treeFilters.test.ts +424 -0
- package/types.d.ts +25 -4
- package/vitest.config.ts +12 -0
- package/dist/multi/141.app-13f840d5.js +0 -1
- package/dist/multi/222.app-13f840d5.js +0 -1
- package/dist/multi/335.app-13f840d5.js +0 -1
- package/dist/multi/34.app-13f840d5.js +0 -1
- package/dist/multi/349.app-13f840d5.js +0 -1
- package/dist/multi/378.app-13f840d5.js +0 -1
- package/dist/multi/406.app-13f840d5.js +0 -1
- package/dist/multi/476.app-13f840d5.js +0 -1
- package/dist/multi/53.app-13f840d5.js +0 -1
- package/dist/multi/584.app-13f840d5.js +0 -1
- package/dist/multi/690.app-13f840d5.js +0 -1
- package/dist/multi/747.app-13f840d5.js +0 -1
- package/dist/multi/767.app-13f840d5.js +0 -1
- package/dist/multi/83.app-13f840d5.js +0 -1
- package/dist/multi/873.app-13f840d5.js +0 -1
- package/dist/multi/920.app-13f840d5.js +0 -1
- package/dist/multi/991.app-13f840d5.js +0 -1
- package/dist/multi/app-13f840d5.js +0 -2
- package/dist/single/app-d31bd53e.js +0 -2
- /package/dist/multi/{app-13f840d5.js.LICENSE.txt → app-07a6d2ea.js.LICENSE.txt} +0 -0
- /package/dist/single/{app-d31bd53e.js.LICENSE.txt → app-c2627b69.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import lineAlertsAlertCircle from "@/assets/svg/line-alerts-alert-circle.svg";
|
|
2
|
+
import lineGeneralEye from "@/assets/svg/line-general-eye.svg";
|
|
3
|
+
import lineIconBomb2 from "@/assets/svg/line-icon-bomb-2.svg";
|
|
4
|
+
import { SvgIcon } from "@/components/commons/SvgIcon";
|
|
5
|
+
import { useI18n } from "@/stores";
|
|
6
|
+
import { capitalize } from "@/utils/capitalize";
|
|
7
|
+
import * as styles from "./styles.scss";
|
|
8
|
+
|
|
9
|
+
const icons = {
|
|
10
|
+
flaky: lineIconBomb2.id,
|
|
11
|
+
known: lineAlertsAlertCircle.id,
|
|
12
|
+
muted: lineGeneralEye.id,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const TestResultInfoStatuses = ({ statuses }) => {
|
|
16
|
+
const { t } = useI18n("filters");
|
|
17
|
+
return (
|
|
18
|
+
<div className={styles["test-result-info-statuses"]}>
|
|
19
|
+
{statuses.map(([status], key: number) => {
|
|
20
|
+
const title = t(status);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div key={key} className={styles["test-result-info-status"]}>
|
|
24
|
+
<SvgIcon className={styles["metadata-icon"]} id={icons[status]} size={"s"} />
|
|
25
|
+
{capitalize(title)}
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
})}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatDuration } from "@allurereport/core-api";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import type { FunctionalComponent } from "preact";
|
|
3
|
+
import type { AllureAwesomeTestResult } from "types";
|
|
4
|
+
import { TestResultInfoStatuses } from "@/components/app/TestResult/TestResultInfo/TestResultInfoStatuses";
|
|
5
5
|
import { TestResultNavigation } from "@/components/app/TestResult/TestResultNavigation";
|
|
6
6
|
import { TestResultPrevStatuses } from "@/components/app/TestResult/TestResultPrevStatuses";
|
|
7
7
|
import { TestResultSeverity } from "@/components/app/TestResult/TestResultSeverity";
|
|
@@ -19,11 +19,12 @@ export type TestResultInfoProps = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export const TestResultInfo: FunctionalComponent<TestResultInfoProps> = ({ testResult }) => {
|
|
22
|
-
const { name, status, duration, labels, history, retries, attachments, stop } = testResult ?? {};
|
|
22
|
+
const { name, status, muted, flaky, known, duration, labels, history, retries, attachments, stop } = testResult ?? {};
|
|
23
23
|
const formattedDuration = formatDuration(duration);
|
|
24
24
|
const fullDate = stop && timestampToDate(stop);
|
|
25
25
|
const severity = labels?.find((label) => label.name === "severity")?.value ?? "normal";
|
|
26
26
|
const { t } = useI18n("ui");
|
|
27
|
+
const statuses = Object.entries({ flaky, muted, known }).filter(([, value]) => value);
|
|
27
28
|
|
|
28
29
|
const Content = () => {
|
|
29
30
|
return (
|
|
@@ -37,6 +38,7 @@ export const TestResultInfo: FunctionalComponent<TestResultInfoProps> = ({ testR
|
|
|
37
38
|
{Boolean(status) && <TestResultStatus status={status} />}
|
|
38
39
|
{Boolean(history?.length) && <TestResultPrevStatuses history={history} />}
|
|
39
40
|
<TestResultSeverity severity={severity} />
|
|
41
|
+
{Boolean(statuses.length) && <TestResultInfoStatuses statuses={statuses} />}
|
|
40
42
|
<TooltipWrapper tooltipText={fullDate}>
|
|
41
43
|
<Text tag={"div"} size={"s"} bold className={styles["test-result-duration"]}>
|
|
42
44
|
{formattedDuration}
|
|
@@ -31,3 +31,20 @@
|
|
|
31
31
|
gap: 4px;
|
|
32
32
|
align-items: center;
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
.test-result-info-statuses {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.test-result-info-status {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 2px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.metadata-icon {
|
|
48
|
+
margin-right: 8px;
|
|
49
|
+
color: var(--on-icon-secondary);
|
|
50
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FunctionalComponent } from "preact";
|
|
2
|
-
import { AllureAwesomeTestResult } from "types";
|
|
1
|
+
import type { FunctionalComponent } from "preact";
|
|
2
|
+
import type { AllureAwesomeTestResult } from "types";
|
|
3
3
|
import LineArrowsChevronDown from "@/assets/svg/line-arrows-chevron-down.svg";
|
|
4
4
|
import LineGeneralCopy3 from "@/assets/svg/line-general-copy-3.svg";
|
|
5
5
|
import { IconButton } from "@/components/commons/Button";
|
|
@@ -8,7 +8,7 @@ import { TooltipWrapper } from "@/components/commons/Tooltip";
|
|
|
8
8
|
import { Code } from "@/components/commons/Typography";
|
|
9
9
|
import { navigateTo } from "@/index";
|
|
10
10
|
import { useI18n } from "@/stores";
|
|
11
|
-
import {
|
|
11
|
+
import { testResultNavStore } from "@/stores/testResults";
|
|
12
12
|
import { copyToClipboard } from "@/utils/copyToClipboard";
|
|
13
13
|
import * as styles from "./styles.scss";
|
|
14
14
|
|
|
@@ -20,47 +20,43 @@ export const TestResultNavigation: FunctionalComponent<TestResultNavigationProps
|
|
|
20
20
|
const { fullName, id: testResultId } = testResult ?? {};
|
|
21
21
|
const id = testResultId || "";
|
|
22
22
|
const { t: tooltip } = useI18n("controls");
|
|
23
|
+
const FullName = () => {
|
|
24
|
+
return (
|
|
25
|
+
<div data-testid="test-result-fullname" className={styles["test-result-fullName"]}>
|
|
26
|
+
<TooltipWrapper tooltipText={tooltip("clipboard")} tooltipTextAfterClick={tooltip("clipboardSuccess")}>
|
|
27
|
+
<IconButton
|
|
28
|
+
data-testid="test-result-fullname-copy"
|
|
29
|
+
style={"ghost"}
|
|
30
|
+
size={"s"}
|
|
31
|
+
icon={LineGeneralCopy3.id}
|
|
32
|
+
onClick={() => copyToClipboard(fullName)}
|
|
33
|
+
/>
|
|
34
|
+
</TooltipWrapper>
|
|
35
|
+
<Code tag={"div"} size={"s"} className={styles["test-result-fullName-text"]}>
|
|
36
|
+
{fullName && fullName}
|
|
37
|
+
</Code>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
23
41
|
|
|
24
42
|
return (
|
|
25
43
|
<Loadable
|
|
26
|
-
source={
|
|
27
|
-
renderData={(
|
|
28
|
-
const
|
|
29
|
-
const currentIndex = id && leaves?.findIndex((leave) => leave === id) + 1;
|
|
30
|
-
const allTestResults = leaves?.length;
|
|
31
|
-
|
|
32
|
-
const FullName = () => {
|
|
33
|
-
return (
|
|
34
|
-
<div data-testid="test-result-fullname" className={styles["test-result-fullName"]}>
|
|
35
|
-
<TooltipWrapper tooltipText={tooltip("clipboard")} tooltipTextAfterClick={tooltip("clipboardSuccess")}>
|
|
36
|
-
<IconButton
|
|
37
|
-
data-testid="test-result-fullname-copy"
|
|
38
|
-
style={"ghost"}
|
|
39
|
-
size={"s"}
|
|
40
|
-
icon={LineGeneralCopy3.id}
|
|
41
|
-
onClick={() => copyToClipboard(fullName)}
|
|
42
|
-
/>
|
|
43
|
-
</TooltipWrapper>
|
|
44
|
-
<Code tag={"div"} size={"s"} className={styles["test-result-fullName-text"]}>
|
|
45
|
-
{fullName && fullName}
|
|
46
|
-
</Code>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
44
|
+
source={testResultNavStore}
|
|
45
|
+
renderData={(data) => {
|
|
46
|
+
const currentIndex = data.indexOf(id) + 1;
|
|
51
47
|
return (
|
|
52
48
|
<div className={styles["test-result-nav"]}>
|
|
53
49
|
{fullName && <FullName />}
|
|
54
|
-
{
|
|
50
|
+
{data && !testResult?.hidden && (
|
|
55
51
|
<div className={styles["test-result-navigator"]}>
|
|
56
|
-
<TooltipWrapper tooltipText={tooltip("
|
|
52
|
+
<TooltipWrapper tooltipText={tooltip("prevTR")} isTriggerActive={currentIndex > 1}>
|
|
57
53
|
<IconButton
|
|
58
54
|
icon={LineArrowsChevronDown.id}
|
|
59
55
|
style={"ghost"}
|
|
60
|
-
isDisabled={currentIndex ===
|
|
61
|
-
data-testid="test-result-nav-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
isDisabled={currentIndex === data.length}
|
|
57
|
+
data-testid="test-result-nav-prev"
|
|
58
|
+
className={styles["test-result-nav-prev"]}
|
|
59
|
+
onClick={() => navigateTo(data[currentIndex])}
|
|
64
60
|
/>
|
|
65
61
|
</TooltipWrapper>
|
|
66
62
|
<Code
|
|
@@ -68,15 +64,15 @@ export const TestResultNavigation: FunctionalComponent<TestResultNavigationProps
|
|
|
68
64
|
size={"s"}
|
|
69
65
|
className={styles["test-result-navigator-numbers"]}
|
|
70
66
|
>
|
|
71
|
-
{currentIndex}/{
|
|
67
|
+
{currentIndex}/{data.length}
|
|
72
68
|
</Code>
|
|
73
|
-
<TooltipWrapper tooltipText={tooltip("
|
|
69
|
+
<TooltipWrapper tooltipText={tooltip("nextTR")}>
|
|
74
70
|
<IconButton
|
|
75
71
|
icon={LineArrowsChevronDown.id}
|
|
76
72
|
style={"ghost"}
|
|
77
73
|
isDisabled={currentIndex <= 1}
|
|
78
|
-
data-testid="test-result-nav-
|
|
79
|
-
onClick={() => navigateTo(
|
|
74
|
+
data-testid="test-result-nav-next"
|
|
75
|
+
onClick={() => navigateTo(data[currentIndex - 2])}
|
|
80
76
|
/>
|
|
81
77
|
</TooltipWrapper>
|
|
82
78
|
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
4
|
-
import { AllureAwesomeTestResult } from "types";
|
|
1
|
+
import type { FunctionalComponent } from "preact";
|
|
2
|
+
import type { AllureAwesomeTestResult } from "types";
|
|
5
3
|
import LineShapesDotCircle from "@/assets/svg/line-shapes-dot-circle.svg";
|
|
4
|
+
import { useTestResultTabsContext } from "@/components/app/TestResult/TestResultTabs";
|
|
6
5
|
import { SvgIcon } from "@/components/commons/SvgIcon";
|
|
7
6
|
import { TooltipWrapper } from "@/components/commons/Tooltip";
|
|
8
7
|
import { Text } from "@/components/commons/Typography";
|
|
8
|
+
import { navigateTo, openInNewTab } from "@/index";
|
|
9
9
|
import { useI18n } from "@/stores";
|
|
10
10
|
import { capitalize } from "@/utils/capitalize";
|
|
11
11
|
import { timestampToDate } from "@/utils/time";
|
|
@@ -13,7 +13,7 @@ import * as styles from "./styles.scss";
|
|
|
13
13
|
|
|
14
14
|
const TestResultPrevStatus = ({ item }) => {
|
|
15
15
|
return (
|
|
16
|
-
<div className={styles["test-result-prev-status"]}>
|
|
16
|
+
<div className={styles["test-result-prev-status"]} onClick={() => navigateTo(`testresult/${item.id}`)}>
|
|
17
17
|
<SvgIcon id={LineShapesDotCircle.id} className={styles[`status-${item?.status}`]} />
|
|
18
18
|
</div>
|
|
19
19
|
);
|
|
@@ -41,7 +41,7 @@ export const TestResultPrevStatuses: FunctionalComponent<TestResultPrevStatusesP
|
|
|
41
41
|
return (
|
|
42
42
|
<div className={styles["test-result-prev-statuses"]}>
|
|
43
43
|
{history?.slice(0, 6).map((item, key) => (
|
|
44
|
-
<div className={styles["test-result-prev-status"]}>
|
|
44
|
+
<div key={key} className={styles["test-result-prev-status"]}>
|
|
45
45
|
<TooltipWrapper key={key} tooltipComponent={<TestResultPrevStatusTooltip item={item} />}>
|
|
46
46
|
<TestResultPrevStatus item={item} />
|
|
47
47
|
</TooltipWrapper>
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { AttachmentTestStepResult } from "@allurereport/core-api";
|
|
2
2
|
import type { FunctionalComponent } from "preact";
|
|
3
3
|
import { useEffect, useState } from "preact/hooks";
|
|
4
|
+
import { modalData } from "@/components/app/Modal";
|
|
5
|
+
import { HtmlAttachmentPreview } from "@/components/app/TestResult/TestResultSteps/HtmlAttachmentPreview";
|
|
4
6
|
import { AttachmentCode } from "@/components/app/TestResult/TestResultSteps/attachmentCode";
|
|
5
7
|
import { AttachmentImage } from "@/components/app/TestResult/TestResultSteps/attachmentImage";
|
|
6
8
|
import { AttachmentVideo } from "@/components/app/TestResult/TestResultSteps/attachmentVideo";
|
|
7
9
|
import { EmptyComponent } from "@/components/app/TestResult/TestResultSteps/wrongAttachment";
|
|
8
10
|
import { Spinner } from "@/components/commons/Spinner";
|
|
9
11
|
import { type Attachments, attachmentType, fetchAttachment } from "@/utils/attachments";
|
|
10
|
-
import {
|
|
11
|
-
HtmlAttachmentPreview
|
|
12
|
-
} from "@/components/app/TestResult/TestResultSteps/HtmlAttachmentPreview";
|
|
13
|
-
import { modalData } from "@/components/app/Modal";
|
|
14
12
|
import * as styles from "./styles.scss";
|
|
15
13
|
|
|
16
14
|
const componentsByAttachmentType = {
|
|
@@ -63,8 +61,8 @@ export const Attachment: FunctionalComponent<AttachmentTestStepResultProps> = ({
|
|
|
63
61
|
|
|
64
62
|
// temp solution before modal component refactoring
|
|
65
63
|
if (CurrentPreviewComponent && previewable && modalData.value.preview) {
|
|
66
|
-
return <CurrentPreviewComponent attachment={attachment} item={item}
|
|
64
|
+
return <CurrentPreviewComponent attachment={attachment} item={item} />;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
return CurrentComponent ? <CurrentComponent attachment={attachment} item={item} /> : <EmptyComponent
|
|
67
|
+
return CurrentComponent ? <CurrentComponent attachment={attachment} item={item} /> : <EmptyComponent />;
|
|
70
68
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FunctionalComponent } from "preact";
|
|
1
|
+
import type { FunctionalComponent } from "preact";
|
|
2
2
|
import { useState } from "preact/hooks";
|
|
3
|
-
import { AllureAwesomeTestResult } from "types";
|
|
3
|
+
import type { AllureAwesomeTestResult } from "types";
|
|
4
4
|
import lineHelpersFlag from "@/assets/svg/line-helpers-flag.svg";
|
|
5
5
|
import { TestResultDropdown } from "@/components/app/TestResult/TestResultDropdown";
|
|
6
6
|
import * as styles from "@/components/app/TestResult/TestResultSteps/styles.scss";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FunctionComponent, FunctionalComponent } from "preact";
|
|
2
|
-
import { AllureAwesomeTestResult } from "types";
|
|
1
|
+
import type { FunctionComponent, FunctionalComponent } from "preact";
|
|
2
|
+
import type { AllureAwesomeTestResult } from "types";
|
|
3
3
|
import * as styles from "@/components/app/BaseLayout/styles.scss";
|
|
4
4
|
import { TestResultAttachmentView } from "@/components/app/TestResult/TestResultAttachmentsView";
|
|
5
5
|
import TestResultEmpty from "@/components/app/TestResult/TestResultEmpty";
|
|
@@ -1,121 +1,74 @@
|
|
|
1
|
-
import type { Statistic
|
|
1
|
+
import type { Statistic } from "@allurereport/core-api";
|
|
2
2
|
import cx from "clsx";
|
|
3
3
|
import type { FunctionComponent } from "preact";
|
|
4
4
|
import { useState } from "preact/hooks";
|
|
5
|
-
import { useReportContentContext } from "@/components/app/ReportBody/context";
|
|
6
5
|
import TreeItem from "@/components/app/Tree/TreeItem";
|
|
7
|
-
import {
|
|
8
|
-
import { PageLoader } from "@/components/commons/PageLoader";
|
|
9
|
-
import { Text } from "@/components/commons/Typography";
|
|
10
|
-
import { useI18n } from "@/stores";
|
|
11
|
-
import { treeStore } from "@/stores/tree";
|
|
12
|
-
import { filterGroups, filterLeaves } from "@/utils/treeFilters";
|
|
6
|
+
import type { AllureAwesomeRecursiveTree, AllureAwesomeStatus } from "../../../../types";
|
|
13
7
|
import TreeHeader from "./TreeHeader";
|
|
14
8
|
import * as styles from "./styles.scss";
|
|
15
9
|
|
|
16
10
|
interface TreeProps {
|
|
17
11
|
statistic?: Statistic;
|
|
18
|
-
|
|
19
|
-
groups?: WithChildren["groups"];
|
|
12
|
+
tree: AllureAwesomeRecursiveTree;
|
|
20
13
|
name?: string;
|
|
21
14
|
root?: boolean;
|
|
22
|
-
statusFilter?:
|
|
15
|
+
statusFilter?: AllureAwesomeStatus;
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
const Tree: FunctionComponent<TreeProps> = ({ statusFilter, root, name,
|
|
18
|
+
const Tree: FunctionComponent<TreeProps> = ({ tree, statusFilter, root, name, statistic }) => {
|
|
26
19
|
const [isOpened, setIsOpen] = useState(statistic === undefined || !!statistic.failed || !!statistic.broken);
|
|
27
|
-
const
|
|
20
|
+
const toggleTree = () => {
|
|
21
|
+
setIsOpen(!isOpened);
|
|
22
|
+
};
|
|
23
|
+
const emptyTree = !tree?.trees?.length && !tree?.leaves?.length;
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
renderLoader={() => <PageLoader />}
|
|
33
|
-
renderData={(treeData) => {
|
|
34
|
-
const reportContext = useReportContentContext();
|
|
35
|
-
const toggleTree = () => {
|
|
36
|
-
setIsOpen(!isOpened);
|
|
37
|
-
};
|
|
38
|
-
const leavesToRender = filterLeaves(leaves, treeData?.leavesById, statusFilter, reportContext);
|
|
39
|
-
const groupsToRender = filterGroups(
|
|
40
|
-
groups,
|
|
41
|
-
treeData?.groupsById,
|
|
42
|
-
treeData?.leavesById,
|
|
43
|
-
statusFilter,
|
|
44
|
-
reportContext,
|
|
45
|
-
);
|
|
46
|
-
if (!groupsToRender.length && !leavesToRender.length) {
|
|
47
|
-
return (
|
|
48
|
-
<div className={styles["tree-list"]}>
|
|
49
|
-
<div className={styles["tree-empty-results"]}>
|
|
50
|
-
<Text className={styles["tree-empty-results-title"]}>{t("no-results")}</Text>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const treeContent = isOpened && (
|
|
57
|
-
<div
|
|
58
|
-
className={cx({
|
|
59
|
-
[styles["tree-content"]]: true,
|
|
60
|
-
[styles.root]: root,
|
|
61
|
-
})}
|
|
62
|
-
>
|
|
63
|
-
{groupsToRender.map((groupId) => {
|
|
64
|
-
const group = treeData?.groupsById?.[groupId];
|
|
65
|
-
|
|
66
|
-
if (!group) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<Tree
|
|
72
|
-
key={group.nodeId}
|
|
73
|
-
name={group.name}
|
|
74
|
-
leaves={group.leaves}
|
|
75
|
-
groups={group.groups}
|
|
76
|
-
statistic={group.statistic}
|
|
77
|
-
statusFilter={statusFilter}
|
|
78
|
-
/>
|
|
79
|
-
);
|
|
80
|
-
})}
|
|
81
|
-
{leavesToRender.map((leafId) => {
|
|
82
|
-
const leaf = treeData?.leavesById?.[leafId];
|
|
25
|
+
if (emptyTree) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
83
28
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
29
|
+
const treeContent = isOpened && (
|
|
30
|
+
<div
|
|
31
|
+
data-testid="tree-content"
|
|
32
|
+
className={cx({
|
|
33
|
+
[styles["tree-content"]]: true,
|
|
34
|
+
[styles.root]: root,
|
|
35
|
+
})}
|
|
36
|
+
>
|
|
37
|
+
{tree?.trees?.map?.((subTree) => (
|
|
38
|
+
<Tree
|
|
39
|
+
key={subTree.nodeId}
|
|
40
|
+
name={subTree.name}
|
|
41
|
+
tree={subTree}
|
|
42
|
+
statistic={subTree.statistic}
|
|
43
|
+
statusFilter={statusFilter}
|
|
44
|
+
/>
|
|
45
|
+
))}
|
|
46
|
+
{tree?.leaves?.map?.((leaf) => (
|
|
47
|
+
<TreeItem
|
|
48
|
+
data-testid="tree-leaf"
|
|
49
|
+
key={leaf.nodeId}
|
|
50
|
+
id={leaf.nodeId}
|
|
51
|
+
name={leaf.name}
|
|
52
|
+
status={leaf.status}
|
|
53
|
+
groupOrder={leaf.groupOrder}
|
|
54
|
+
duration={leaf.duration}
|
|
55
|
+
/>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
101
59
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{treeContent}
|
|
115
|
-
</div>
|
|
116
|
-
);
|
|
117
|
-
}}
|
|
118
|
-
/>
|
|
60
|
+
return (
|
|
61
|
+
<div className={styles.tree}>
|
|
62
|
+
{name && (
|
|
63
|
+
<TreeHeader
|
|
64
|
+
categoryTitle={name}
|
|
65
|
+
isOpened={isOpened}
|
|
66
|
+
toggleTree={toggleTree}
|
|
67
|
+
statistic={statistic}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
{treeContent}
|
|
71
|
+
</div>
|
|
119
72
|
);
|
|
120
73
|
};
|
|
121
74
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Statistic, statusesList } from "@allurereport/core-api";
|
|
1
|
+
import { type Statistic, statusesList } from "@allurereport/core-api";
|
|
2
2
|
import { clsx } from "clsx";
|
|
3
|
-
import { FunctionComponent } from "preact";
|
|
3
|
+
import { type FunctionComponent } from "preact";
|
|
4
4
|
import { ArrowButton } from "@/components/app/ArrowButton";
|
|
5
5
|
import { Loadable } from "@/components/commons/Loadable";
|
|
6
6
|
import { Text } from "@/components/commons/Typography";
|
|
7
7
|
import { statsStore } from "@/stores";
|
|
8
|
+
import { treeFiltersStore } from "@/stores/tree";
|
|
8
9
|
import * as styles from "./styles.scss";
|
|
9
10
|
|
|
10
11
|
interface TreeHeaderProps {
|
|
@@ -12,15 +13,14 @@ interface TreeHeaderProps {
|
|
|
12
13
|
categoryTitle: string;
|
|
13
14
|
isOpened: boolean;
|
|
14
15
|
toggleTree: () => void;
|
|
15
|
-
statusFilter?: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const maxWidthTab
|
|
19
|
-
const minWidthTab
|
|
18
|
+
const maxWidthTab = 140;
|
|
19
|
+
const minWidthTab = 46;
|
|
20
20
|
// to make the progress bar more visually responsive for smaller values,
|
|
21
21
|
// we can adjust the formula by adding an offset to stretch the lower part
|
|
22
22
|
// of the logarithmic scale
|
|
23
|
-
const offset
|
|
23
|
+
const offset = 10;
|
|
24
24
|
|
|
25
25
|
const progress = (current: number, total: number) => {
|
|
26
26
|
const logOffset = Math.log(offset);
|
|
@@ -31,10 +31,11 @@ const TreeHeader: FunctionComponent<TreeHeaderProps> = ({
|
|
|
31
31
|
categoryTitle,
|
|
32
32
|
isOpened,
|
|
33
33
|
toggleTree,
|
|
34
|
-
statusFilter = "total",
|
|
35
34
|
statistic,
|
|
36
35
|
...rest
|
|
37
36
|
}) => {
|
|
37
|
+
const { status: statusFilter } = treeFiltersStore.value;
|
|
38
|
+
|
|
38
39
|
return (
|
|
39
40
|
<Loadable
|
|
40
41
|
source={statsStore}
|
|
@@ -49,8 +50,8 @@ const TreeHeader: FunctionComponent<TreeHeaderProps> = ({
|
|
|
49
50
|
value !== undefined && (statusFilter === "total" || (statusFilter === status && value > 0)),
|
|
50
51
|
)
|
|
51
52
|
.map(({ status, value }) => {
|
|
52
|
-
const className = clsx(styles[
|
|
53
|
-
const style = { flexGrow:
|
|
53
|
+
const className = clsx(styles["tree-header-bar-item"], styles[status]);
|
|
54
|
+
const style = { flexGrow: value };
|
|
54
55
|
|
|
55
56
|
return (
|
|
56
57
|
<div key={status} className={className} style={style}>
|
|
@@ -61,9 +62,9 @@ const TreeHeader: FunctionComponent<TreeHeaderProps> = ({
|
|
|
61
62
|
: null;
|
|
62
63
|
|
|
63
64
|
return (
|
|
64
|
-
<div {...rest} className={styles["tree-header"]} onClick={toggleTree}>
|
|
65
|
-
<ArrowButton isOpened={isOpened} />
|
|
66
|
-
<Text size="m" bold className={styles["tree-header-title"]}>
|
|
65
|
+
<div data-testid="tree-header" {...rest} className={styles["tree-header"]} onClick={toggleTree}>
|
|
66
|
+
<ArrowButton data-testid="tree-arrow" isOpened={isOpened} />
|
|
67
|
+
<Text data-testid="tree-header-title" size="m" bold className={styles["tree-header-title"]}>
|
|
67
68
|
{categoryTitle}
|
|
68
69
|
</Text>
|
|
69
70
|
{treeHeaderBar && (
|
|
@@ -10,14 +10,18 @@ interface TreeItemProps {
|
|
|
10
10
|
status: TestStatus;
|
|
11
11
|
duration?: number;
|
|
12
12
|
id: string;
|
|
13
|
+
groupOrder: number;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export const TreeItem: FunctionComponent<TreeItemProps> = ({ name, status, duration, id, ...rest }) => {
|
|
16
|
+
export const TreeItem: FunctionComponent<TreeItemProps> = ({ name, groupOrder, status, duration, id, ...rest }) => {
|
|
16
17
|
const formattedDuration = formatDuration(duration);
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<div {...rest} className={styles["tree-item"]} onClick={() => navigateTo(id)}>
|
|
20
21
|
<TreeItemIcon status={status} />
|
|
22
|
+
<span data-testid="tree-leaf-order" class={styles.order}>
|
|
23
|
+
{groupOrder}
|
|
24
|
+
</span>
|
|
21
25
|
<Text data-testid="tree-leaf-title" className={styles["item-title"]}>
|
|
22
26
|
{name}
|
|
23
27
|
</Text>
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect } from "preact/hooks";
|
|
2
2
|
import { useTabsContext } from "@/components/app/Tabs";
|
|
3
3
|
import Tree from "@/components/app/Tree/Tree";
|
|
4
|
+
import { Button } from "@/components/commons/Button";
|
|
4
5
|
import { Loadable } from "@/components/commons/Loadable";
|
|
5
6
|
import { PageLoader } from "@/components/commons/PageLoader";
|
|
6
7
|
import { Text } from "@/components/commons/Typography";
|
|
7
8
|
import { useI18n } from "@/stores/locale";
|
|
8
|
-
import { treeStore } from "@/stores/tree";
|
|
9
|
+
import { clearTreeFilters, filteredTree, noTests, noTestsFound, setTreeStatus, treeStore } from "@/stores/tree";
|
|
10
|
+
import type { AllureAwesomeStatus } from "../../../../types";
|
|
9
11
|
import * as styles from "./styles.scss";
|
|
10
12
|
|
|
11
13
|
export const TreeList = () => {
|
|
12
14
|
const { t } = useI18n("empty");
|
|
13
15
|
const { currentTab } = useTabsContext();
|
|
14
16
|
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setTreeStatus(currentTab as AllureAwesomeStatus);
|
|
19
|
+
}, [currentTab]);
|
|
20
|
+
|
|
15
21
|
return (
|
|
16
22
|
<Loadable
|
|
17
23
|
source={treeStore}
|
|
18
24
|
renderLoader={() => <PageLoader />}
|
|
19
|
-
renderData={(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!groups && !leaves) {
|
|
25
|
+
renderData={() => {
|
|
26
|
+
if (noTests.value) {
|
|
23
27
|
return (
|
|
24
28
|
<div className={styles["tree-list"]}>
|
|
25
29
|
<div className={styles["tree-empty-results"]}>
|
|
@@ -29,9 +33,29 @@ export const TreeList = () => {
|
|
|
29
33
|
);
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
if (noTestsFound.value) {
|
|
37
|
+
return (
|
|
38
|
+
<div className={styles["tree-list"]}>
|
|
39
|
+
<div className={styles["tree-empty-results"]}>
|
|
40
|
+
<Text tag="p" className={styles["tree-empty-results-title"]}>
|
|
41
|
+
{t("no-tests-found")}
|
|
42
|
+
</Text>
|
|
43
|
+
<Button
|
|
44
|
+
className={styles["tree-empty-results-clear-button"]}
|
|
45
|
+
type="button"
|
|
46
|
+
text={t("clear-filters")}
|
|
47
|
+
size={"s"}
|
|
48
|
+
style={"outline"}
|
|
49
|
+
onClick={() => clearTreeFilters()}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
32
56
|
return (
|
|
33
57
|
<div className={styles["tree-list"]}>
|
|
34
|
-
<Tree
|
|
58
|
+
<Tree tree={filteredTree.value} statusFilter={currentTab as AllureAwesomeStatus} root />
|
|
35
59
|
</div>
|
|
36
60
|
);
|
|
37
61
|
}}
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
padding: 6px 8px 6px 6px;
|
|
50
50
|
transition: background-color 300ms;
|
|
51
51
|
gap: 4px;
|
|
52
|
-
align-items: center;
|
|
53
52
|
cursor: pointer;
|
|
54
53
|
position: relative;
|
|
55
54
|
|
|
@@ -159,12 +158,25 @@
|
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
.tree-empty-results {
|
|
162
|
-
display: flex;
|
|
163
161
|
padding: 44px 24px;
|
|
164
|
-
align
|
|
165
|
-
justify-content: center;
|
|
162
|
+
text-align: center;
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
.tree-empty-results-title {
|
|
169
166
|
color: var(--on-text-secondary);
|
|
170
167
|
}
|
|
168
|
+
|
|
169
|
+
.tree-empty-results-clear-button {
|
|
170
|
+
margin-top: 4px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.order {
|
|
174
|
+
user-select: none;
|
|
175
|
+
color: var(--on-text-hint);
|
|
176
|
+
min-width: 16px;
|
|
177
|
+
text-align: center;
|
|
178
|
+
box-sizing: content-box;
|
|
179
|
+
padding-top: 2px;
|
|
180
|
+
line-height: 16px;
|
|
181
|
+
width: 24px;
|
|
182
|
+
}
|