@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.
Files changed (115) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/CONTRIBUTING.md +34 -0
  3. package/dist/multi/141.app-07a6d2ea.js +1 -0
  4. package/dist/multi/222.app-07a6d2ea.js +1 -0
  5. package/dist/multi/335.app-07a6d2ea.js +1 -0
  6. package/dist/multi/34.app-07a6d2ea.js +1 -0
  7. package/dist/multi/349.app-07a6d2ea.js +1 -0
  8. package/dist/multi/378.app-07a6d2ea.js +1 -0
  9. package/dist/multi/406.app-07a6d2ea.js +1 -0
  10. package/dist/multi/476.app-07a6d2ea.js +1 -0
  11. package/dist/multi/53.app-07a6d2ea.js +1 -0
  12. package/dist/multi/584.app-07a6d2ea.js +1 -0
  13. package/dist/multi/690.app-07a6d2ea.js +1 -0
  14. package/dist/multi/747.app-07a6d2ea.js +1 -0
  15. package/dist/multi/767.app-07a6d2ea.js +1 -0
  16. package/dist/multi/{816.app-13f840d5.js → 816.app-07a6d2ea.js} +1 -1
  17. package/dist/multi/83.app-07a6d2ea.js +1 -0
  18. package/dist/multi/873.app-07a6d2ea.js +1 -0
  19. package/dist/multi/920.app-07a6d2ea.js +1 -0
  20. package/dist/multi/991.app-07a6d2ea.js +1 -0
  21. package/dist/multi/app-07a6d2ea.js +2 -0
  22. package/dist/multi/manifest.json +20 -20
  23. package/dist/multi/{styles-13f840d5.css → styles-07a6d2ea.css} +7 -7
  24. package/dist/single/app-c2627b69.js +2 -0
  25. package/dist/single/manifest.json +1 -1
  26. package/package.json +11 -4
  27. package/src/assets/svg/line-alerts-alert-circle.svg +12 -0
  28. package/src/assets/svg/line-general-eye.svg +7 -0
  29. package/src/assets/svg/line-icon-bomb-2.svg +12 -0
  30. package/src/components/app/ArrowButton/index.tsx +3 -2
  31. package/src/components/app/ArrowButton/styles.scss +3 -0
  32. package/src/components/app/BaseLayout/index.tsx +5 -5
  33. package/src/components/app/ReportBody/Filters.tsx +12 -10
  34. package/src/components/app/ReportBody/HeaderActions.tsx +3 -3
  35. package/src/components/app/ReportBody/SortBy.tsx +8 -8
  36. package/src/components/app/ReportBody/context.tsx +0 -1
  37. package/src/components/app/ReportMetadata/MetadataSummary.tsx +22 -18
  38. package/src/components/app/ReportMetadata/MetadataWithIcon.tsx +2 -2
  39. package/src/components/app/Tabs/index.tsx +2 -3
  40. package/src/components/app/TestResult/TestResultDescription/index.tsx +3 -3
  41. package/src/components/app/TestResult/TestResultError/index.tsx +18 -16
  42. package/src/components/app/TestResult/TestResultError/styles.scss +16 -5
  43. package/src/components/app/TestResult/TestResultHeader/index.tsx +2 -2
  44. package/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx +21 -10
  45. package/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx +31 -0
  46. package/src/components/app/TestResult/TestResultInfo/index.tsx +6 -4
  47. package/src/components/app/TestResult/TestResultInfo/styles.scss +17 -0
  48. package/src/components/app/TestResult/TestResultNavigation/index.tsx +34 -38
  49. package/src/components/app/TestResult/TestResultNavigation/styles.scss +1 -1
  50. package/src/components/app/TestResult/TestResultPrevStatuses/index.tsx +6 -6
  51. package/src/components/app/TestResult/TestResultSteps/attachment.tsx +4 -6
  52. package/src/components/app/TestResult/TestResultTeardown/index.tsx +2 -2
  53. package/src/components/app/TestResult/index.tsx +2 -2
  54. package/src/components/app/Tree/Tree.tsx +54 -101
  55. package/src/components/app/Tree/TreeHeader.tsx +13 -12
  56. package/src/components/app/Tree/TreeItem.tsx +5 -1
  57. package/src/components/app/Tree/index.tsx +31 -7
  58. package/src/components/app/Tree/styles.scss +16 -4
  59. package/src/components/commons/Button/styles.scss +6 -4
  60. package/src/components/commons/Menu/index.tsx +4 -4
  61. package/src/components/commons/SearchBox/index.tsx +8 -6
  62. package/src/components/commons/Toggle/index.tsx +2 -1
  63. package/src/components/commons/Tooltip/index.tsx +3 -3
  64. package/src/i18n/constants.ts +23 -4
  65. package/src/i18n/locales/am.json +5 -2
  66. package/src/i18n/locales/az.json +5 -2
  67. package/src/i18n/locales/de.json +5 -2
  68. package/src/i18n/locales/en.json +6 -3
  69. package/src/i18n/locales/es.json +5 -1
  70. package/src/i18n/locales/fr.json +5 -2
  71. package/src/i18n/locales/he.json +5 -2
  72. package/src/i18n/locales/it.json +5 -2
  73. package/src/i18n/locales/ja.json +5 -2
  74. package/src/i18n/locales/ka.json +5 -2
  75. package/src/i18n/locales/kr.json +5 -2
  76. package/src/i18n/locales/nl.json +5 -2
  77. package/src/i18n/locales/pl.json +5 -2
  78. package/src/i18n/locales/pt.json +5 -2
  79. package/src/i18n/locales/ru.json +5 -2
  80. package/src/i18n/locales/sv.json +5 -2
  81. package/src/i18n/locales/tr.json +5 -2
  82. package/src/i18n/locales/zh.json +6 -3
  83. package/src/index.html +1 -0
  84. package/src/index.tsx +4 -0
  85. package/src/stores/chart.ts +2 -2
  86. package/src/stores/stats.ts +1 -1
  87. package/src/stores/testResults.ts +26 -4
  88. package/src/stores/tree.ts +98 -4
  89. package/src/types/globals.d.ts +6 -1
  90. package/src/utils/copyToClipboard.ts +14 -1
  91. package/src/utils/treeFilters.ts +73 -120
  92. package/test/utils/treeFilters.test.ts +424 -0
  93. package/types.d.ts +25 -4
  94. package/vitest.config.ts +12 -0
  95. package/dist/multi/141.app-13f840d5.js +0 -1
  96. package/dist/multi/222.app-13f840d5.js +0 -1
  97. package/dist/multi/335.app-13f840d5.js +0 -1
  98. package/dist/multi/34.app-13f840d5.js +0 -1
  99. package/dist/multi/349.app-13f840d5.js +0 -1
  100. package/dist/multi/378.app-13f840d5.js +0 -1
  101. package/dist/multi/406.app-13f840d5.js +0 -1
  102. package/dist/multi/476.app-13f840d5.js +0 -1
  103. package/dist/multi/53.app-13f840d5.js +0 -1
  104. package/dist/multi/584.app-13f840d5.js +0 -1
  105. package/dist/multi/690.app-13f840d5.js +0 -1
  106. package/dist/multi/747.app-13f840d5.js +0 -1
  107. package/dist/multi/767.app-13f840d5.js +0 -1
  108. package/dist/multi/83.app-13f840d5.js +0 -1
  109. package/dist/multi/873.app-13f840d5.js +0 -1
  110. package/dist/multi/920.app-13f840d5.js +0 -1
  111. package/dist/multi/991.app-13f840d5.js +0 -1
  112. package/dist/multi/app-13f840d5.js +0 -2
  113. package/dist/single/app-d31bd53e.js +0 -2
  114. /package/dist/multi/{app-13f840d5.js.LICENSE.txt → app-07a6d2ea.js.LICENSE.txt} +0 -0
  115. /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 * as test from "node:test";
3
- import { FunctionalComponent } from "preact";
4
- import { AllureAwesomeTestResult } from "types";
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 { treeStore } from "@/stores/tree";
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={treeStore}
27
- renderData={(treeData) => {
28
- const leaves = treeData && Object.keys(treeData?.leavesById);
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
- {allTestResults && (
50
+ {data && !testResult?.hidden && (
55
51
  <div className={styles["test-result-navigator"]}>
56
- <TooltipWrapper tooltipText={tooltip("nextTR")}>
52
+ <TooltipWrapper tooltipText={tooltip("prevTR")} isTriggerActive={currentIndex > 1}>
57
53
  <IconButton
58
54
  icon={LineArrowsChevronDown.id}
59
55
  style={"ghost"}
60
- isDisabled={currentIndex === allTestResults}
61
- data-testid="test-result-nav-next"
62
- onClick={() => navigateTo(leaves[currentIndex])}
63
- className={styles["test-result-nav-next"]}
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}/{allTestResults}
67
+ {currentIndex}/{data.length}
72
68
  </Code>
73
- <TooltipWrapper tooltipText={tooltip("prevTR")} isTriggerActive={currentIndex > 1}>
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-prev"
79
- onClick={() => navigateTo(leaves[currentIndex - 2])}
74
+ data-testid="test-result-nav-next"
75
+ onClick={() => navigateTo(data[currentIndex - 2])}
80
76
  />
81
77
  </TooltipWrapper>
82
78
  </div>
@@ -40,7 +40,7 @@
40
40
  font-weight: 400;
41
41
  }
42
42
 
43
- .test-result-nav-next {
43
+ .test-result-nav-prev {
44
44
  transform: rotate(-180deg);
45
45
  &:active {
46
46
  transform: scale(var(--allure-btn-pressed-scale)) rotate(-180deg);
@@ -1,11 +1,11 @@
1
- import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
2
- import { FunctionalComponent } from "preact";
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, TestStatus, WithChildren } from "@allurereport/core-api";
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 { Loadable } from "@/components/commons/Loadable";
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
- leaves?: WithChildren["leaves"];
19
- groups?: WithChildren["groups"];
12
+ tree: AllureAwesomeRecursiveTree;
20
13
  name?: string;
21
14
  root?: boolean;
22
- statusFilter?: TestStatus;
15
+ statusFilter?: AllureAwesomeStatus;
23
16
  }
24
17
 
25
- const Tree: FunctionComponent<TreeProps> = ({ statusFilter, root, name, leaves = [], groups = [], statistic }) => {
18
+ const Tree: FunctionComponent<TreeProps> = ({ tree, statusFilter, root, name, statistic }) => {
26
19
  const [isOpened, setIsOpen] = useState(statistic === undefined || !!statistic.failed || !!statistic.broken);
27
- const { t } = useI18n("empty");
20
+ const toggleTree = () => {
21
+ setIsOpen(!isOpened);
22
+ };
23
+ const emptyTree = !tree?.trees?.length && !tree?.leaves?.length;
28
24
 
29
- return (
30
- <Loadable
31
- source={treeStore}
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
- if (!leaf) {
85
- return null;
86
- }
87
-
88
- return (
89
- <TreeItem
90
- data-testid="tree-leaf"
91
- key={leaf.nodeId}
92
- id={leaf.nodeId}
93
- name={leaf.name}
94
- status={leaf.status}
95
- duration={leaf.duration}
96
- />
97
- );
98
- })}
99
- </div>
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
- return (
103
- <div className={styles.tree}>
104
- {name && (
105
- <TreeHeader
106
- categoryTitle={name}
107
- isOpened={isOpened}
108
- toggleTree={toggleTree}
109
- statusFilter={statusFilter}
110
- statistic={statistic}
111
- data-testid="tree-group-header"
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: number = 140;
19
- const minWidthTab: number = 46;
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: number = 10;
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[`tree-header-bar-item`], styles[status]);
53
- const style = { flexGrow: `${value}` };
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 type { TestStatus } from "@allurereport/core-api";
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={(treeData) => {
20
- const { groups, leaves } = treeData?.root ?? {};
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 groups={groups} leaves={leaves} statusFilter={currentTab as TestStatus} root />
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-items: center;
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
+ }