@allurereport/web-awesome 3.0.0 → 3.1.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.
Files changed (146) hide show
  1. package/dist/multi/173.app-79c65c7bff941abcbc51.js +1 -0
  2. package/dist/multi/174.app-79c65c7bff941abcbc51.js +1 -0
  3. package/dist/multi/252.app-79c65c7bff941abcbc51.js +1 -0
  4. package/dist/multi/282.app-79c65c7bff941abcbc51.js +1 -0
  5. package/dist/multi/29.app-79c65c7bff941abcbc51.js +1 -0
  6. package/dist/multi/416.app-79c65c7bff941abcbc51.js +1 -0
  7. package/dist/multi/527.app-79c65c7bff941abcbc51.js +1 -0
  8. package/dist/multi/600.app-79c65c7bff941abcbc51.js +1 -0
  9. package/dist/multi/605.app-79c65c7bff941abcbc51.js +1 -0
  10. package/dist/multi/638.app-79c65c7bff941abcbc51.js +1 -0
  11. package/dist/multi/672.app-79c65c7bff941abcbc51.js +1 -0
  12. package/dist/multi/686.app-79c65c7bff941abcbc51.js +1 -0
  13. package/dist/multi/725.app-79c65c7bff941abcbc51.js +1 -0
  14. package/dist/multi/741.app-79c65c7bff941abcbc51.js +1 -0
  15. package/dist/multi/755.app-79c65c7bff941abcbc51.js +1 -0
  16. package/dist/multi/894.app-79c65c7bff941abcbc51.js +1 -0
  17. package/dist/multi/91.app-79c65c7bff941abcbc51.js +1 -0
  18. package/dist/multi/943.app-79c65c7bff941abcbc51.js +1 -0
  19. package/dist/multi/980.app-79c65c7bff941abcbc51.js +1 -0
  20. package/dist/multi/app-79c65c7bff941abcbc51.js +2 -0
  21. package/dist/multi/{app-9931797d1602fc52db5b.js.LICENSE.txt → app-79c65c7bff941abcbc51.js.LICENSE.txt} +7 -0
  22. package/dist/multi/manifest.json +21 -21
  23. package/dist/multi/styles-9e390bad7ce54a807a8e.css +49 -0
  24. package/dist/single/app-3ca67f29d0f1166c08ca.js +2 -0
  25. package/dist/single/{app-6199dc1c2fd3bddc2526.js.LICENSE.txt → app-3ca67f29d0f1166c08ca.js.LICENSE.txt} +7 -0
  26. package/dist/single/manifest.json +1 -1
  27. package/package.json +8 -8
  28. package/src/assets/scss/palette.scss +102 -102
  29. package/src/assets/scss/vars.scss +3 -0
  30. package/src/components/BaseLayout/index.tsx +25 -21
  31. package/src/components/BaseLayout/styles.scss +1 -0
  32. package/src/components/Charts/index.tsx +5 -2
  33. package/src/components/Footer/FooterVersion.tsx +14 -8
  34. package/src/components/Header/index.tsx +9 -7
  35. package/src/components/HeaderControls/index.tsx +5 -2
  36. package/src/components/MainReport/styles.scss +1 -0
  37. package/src/components/Metadata/index.tsx +24 -7
  38. package/src/components/ReportBody/HeaderActions.tsx +4 -13
  39. package/src/components/ReportBody/SortBy.tsx +28 -17
  40. package/src/components/ReportBody/index.tsx +12 -17
  41. package/src/components/ReportBody/styles.scss +4 -1
  42. package/src/components/ReportFilters/BaseFilters.tsx +345 -0
  43. package/src/components/ReportFilters/RetryFlaky.tsx +29 -0
  44. package/src/components/ReportFilters/TagsFilter.tsx +41 -0
  45. package/src/components/ReportFilters/TransitionFilter.tsx +49 -0
  46. package/src/components/ReportFilters/index.tsx +44 -0
  47. package/src/components/ReportFilters/styles.scss +55 -0
  48. package/src/components/ReportSearch/index.tsx +29 -0
  49. package/src/components/ReportTabs/index.tsx +37 -0
  50. package/src/components/SectionPicker/index.tsx +1 -1
  51. package/src/components/SplitLayout/index.tsx +7 -5
  52. package/src/components/TestResult/TestStepsEmpty/index.tsx +1 -7
  53. package/src/components/TestResult/TrEmpty/index.tsx +1 -7
  54. package/src/components/TestResult/TrEnvironmentItem/index.tsx +2 -2
  55. package/src/components/TestResult/TrError/index.tsx +9 -2
  56. package/src/components/TestResult/TrHeader/TrBreadcrumbs.tsx +2 -2
  57. package/src/components/TestResult/TrHistory/TrHistoryItem.tsx +38 -7
  58. package/src/components/TestResult/TrHistory/index.tsx +18 -8
  59. package/src/components/TestResult/TrHistory/styles.scss +4 -7
  60. package/src/components/TestResult/TrInfo/styles.scss +1 -0
  61. package/src/components/TestResult/TrNavigation/index.tsx +109 -68
  62. package/src/components/TestResult/TrNavigation/styles.scss +15 -25
  63. package/src/components/TestResult/TrPwTraces/PwTraceButton.tsx +1 -8
  64. package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +2 -3
  65. package/src/components/TestResult/TrRetriesView/index.tsx +4 -3
  66. package/src/components/TestResult/TrSteps/TrAttachment.tsx +5 -3
  67. package/src/components/TestResult/TrSteps/TrAttachmentInfo.tsx +10 -3
  68. package/src/components/TestResult/TrSteps/TrStep.tsx +3 -3
  69. package/src/components/TestResult/TrTabs/index.tsx +7 -23
  70. package/src/components/TestResult/index.tsx +9 -4
  71. package/src/components/TestResult/styles.scss +1 -0
  72. package/src/components/Tree/index.tsx +22 -25
  73. package/src/index.html +19 -18
  74. package/src/index.tsx +20 -28
  75. package/src/locales/az.json +42 -12
  76. package/src/locales/de.json +42 -12
  77. package/src/locales/en.json +42 -12
  78. package/src/locales/es.json +42 -12
  79. package/src/locales/fr.json +42 -12
  80. package/src/locales/he.json +42 -12
  81. package/src/locales/hy.json +42 -12
  82. package/src/locales/it.json +42 -12
  83. package/src/locales/ja.json +42 -12
  84. package/src/locales/ka.json +42 -12
  85. package/src/locales/kr.json +42 -12
  86. package/src/locales/nl.json +42 -12
  87. package/src/locales/pl.json +42 -12
  88. package/src/locales/pt.json +42 -12
  89. package/src/locales/ru.json +42 -12
  90. package/src/locales/sv.json +42 -12
  91. package/src/locales/tr.json +42 -12
  92. package/src/locales/ua.json +42 -12
  93. package/src/locales/zh.json +42 -12
  94. package/src/stores/chart.ts +2 -2
  95. package/src/stores/env.ts +6 -6
  96. package/src/stores/envInfo.ts +2 -2
  97. package/src/stores/globals.ts +1 -1
  98. package/src/stores/index.ts +0 -1
  99. package/src/stores/layout.ts +20 -11
  100. package/src/stores/locale.ts +2 -1
  101. package/src/stores/qualityGate.ts +2 -2
  102. package/src/stores/router.ts +25 -91
  103. package/src/stores/sections.ts +32 -45
  104. package/src/stores/stats.ts +4 -4
  105. package/src/stores/testResult.ts +5 -0
  106. package/src/stores/testResults.ts +7 -5
  107. package/src/stores/tree.ts +49 -126
  108. package/src/stores/treeFilters/actions.ts +63 -0
  109. package/src/stores/treeFilters/constants.ts +13 -0
  110. package/src/stores/treeFilters/model.ts +51 -0
  111. package/src/stores/treeFilters/store.ts +273 -0
  112. package/src/stores/treeFilters/utils.ts +132 -0
  113. package/src/stores/treeSort.ts +71 -0
  114. package/src/stores/variables.ts +3 -3
  115. package/src/utils/persist.ts +23 -0
  116. package/src/utils/tree.ts +12 -5
  117. package/src/utils/treeFilters.ts +48 -54
  118. package/test/components/Header.test.tsx +49 -58
  119. package/test/utils/treeFilters.test.ts +18 -176
  120. package/types.d.ts +4 -1
  121. package/dist/multi/173.app-9931797d1602fc52db5b.js +0 -1
  122. package/dist/multi/174.app-9931797d1602fc52db5b.js +0 -1
  123. package/dist/multi/252.app-9931797d1602fc52db5b.js +0 -1
  124. package/dist/multi/282.app-9931797d1602fc52db5b.js +0 -1
  125. package/dist/multi/29.app-9931797d1602fc52db5b.js +0 -1
  126. package/dist/multi/416.app-9931797d1602fc52db5b.js +0 -1
  127. package/dist/multi/527.app-9931797d1602fc52db5b.js +0 -1
  128. package/dist/multi/600.app-9931797d1602fc52db5b.js +0 -1
  129. package/dist/multi/605.app-9931797d1602fc52db5b.js +0 -1
  130. package/dist/multi/638.app-9931797d1602fc52db5b.js +0 -1
  131. package/dist/multi/672.app-9931797d1602fc52db5b.js +0 -1
  132. package/dist/multi/686.app-9931797d1602fc52db5b.js +0 -1
  133. package/dist/multi/725.app-9931797d1602fc52db5b.js +0 -1
  134. package/dist/multi/741.app-9931797d1602fc52db5b.js +0 -1
  135. package/dist/multi/755.app-9931797d1602fc52db5b.js +0 -1
  136. package/dist/multi/894.app-9931797d1602fc52db5b.js +0 -1
  137. package/dist/multi/91.app-9931797d1602fc52db5b.js +0 -1
  138. package/dist/multi/943.app-9931797d1602fc52db5b.js +0 -1
  139. package/dist/multi/980.app-9931797d1602fc52db5b.js +0 -1
  140. package/dist/multi/app-9931797d1602fc52db5b.js +0 -2
  141. package/dist/multi/styles-8fe37354d1c2270c691e.css +0 -48
  142. package/dist/single/app-6199dc1c2fd3bddc2526.js +0 -2
  143. package/src/components/ReportBody/Filters.tsx +0 -71
  144. package/src/components/Tabs/index.tsx +0 -62
  145. package/src/stores/theme.ts +0 -30
  146. /package/src/components/{Tabs → ReportTabs}/styles.scss +0 -0
@@ -0,0 +1,44 @@
1
+ import { For } from "@preact/signals/utils";
2
+ import type { AwesomeFilter } from "@/stores/treeFilters/model";
3
+ import { setTreeFilter, treeQuickFilters } from "@/stores/treeFilters/store";
4
+ import { isFlakyFilter, isRetryFilter, isTagFilter, isTransitionFilter } from "@/stores/treeFilters/utils";
5
+ import { BooleanFieldFilter } from "./BaseFilters";
6
+ import { RetryFlakyFilter } from "./RetryFlaky";
7
+ import { TagsFilter } from "./TagsFilter";
8
+ import { TransitionFilter } from "./TransitionFilter";
9
+ import * as styles from "./styles.scss";
10
+
11
+ const Filter = (props: { filter: AwesomeFilter; onChange: (filter: AwesomeFilter) => void }) => {
12
+ const { filter, onChange } = props;
13
+ const { value: field, type } = filter;
14
+
15
+ if (isRetryFilter(filter) || isFlakyFilter(filter)) {
16
+ return <RetryFlakyFilter filter={filter} onChange={onChange} />;
17
+ }
18
+
19
+ if (isTransitionFilter(filter)) {
20
+ return <TransitionFilter group={filter} onChange={onChange} />;
21
+ }
22
+
23
+ if (type === "field" && field.type === "boolean") {
24
+ return <BooleanFieldFilter field={field} onChange={(value) => onChange({ ...filter, value })} />;
25
+ }
26
+
27
+ if (isTagFilter(filter)) {
28
+ return <TagsFilter filter={filter} onChange={onChange} />;
29
+ }
30
+
31
+ return null;
32
+ };
33
+
34
+ const QuickFilters = () => {
35
+ return <For each={treeQuickFilters}>{(filter) => <Filter filter={filter} onChange={setTreeFilter} />}</For>;
36
+ };
37
+
38
+ export const ReportFilters = () => {
39
+ return (
40
+ <div className={styles.wrapper}>
41
+ <QuickFilters />
42
+ </div>
43
+ );
44
+ };
@@ -0,0 +1,55 @@
1
+ .wrapper {
2
+ display: flex;
3
+ flex-direction: row;
4
+ column-gap: 8px;
5
+ row-gap: 12px;
6
+ flex-wrap: wrap;
7
+ align-items: center;
8
+ flex-grow: 1;
9
+ width: 100%;
10
+ }
11
+
12
+ .btnWrapper {
13
+ position: relative;
14
+
15
+ &:hover,
16
+ &:focus-within {
17
+ & .clear {
18
+ opacity: 1;
19
+ }
20
+ }
21
+ }
22
+
23
+ .clear {
24
+ transition: opacity var(--color-change-transition-duration) ease-out;
25
+ opacity: 0;
26
+ position: absolute;
27
+ top: -9.5px;
28
+ right: -9.5px;
29
+ }
30
+
31
+ .tooltip {
32
+ opacity: 0;
33
+ pointer-events: none;
34
+ user-select: none;
35
+ transition: opacity 0.2s ease-in-out;
36
+ & > div {
37
+ max-width: 300px;
38
+ min-width: 0;
39
+ text-align: center;
40
+ }
41
+
42
+ &[data-visible="true"] {
43
+ opacity: 1;
44
+ }
45
+ }
46
+
47
+ .description {
48
+ color: var(--on-text-secondary);
49
+ }
50
+
51
+ .itemContent {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 4px;
55
+ }
@@ -0,0 +1,29 @@
1
+ import { SearchBox } from "@allurereport/web-components";
2
+ import { useI18n } from "@/stores/locale";
3
+ import { setTreeQueryFilter, treeQueryFilterValue } from "@/stores/treeFilters/store";
4
+
5
+ const handleQuerySearch = (value: string) => {
6
+ if (!value) {
7
+ setTreeQueryFilter(undefined);
8
+ return;
9
+ }
10
+
11
+ setTreeQueryFilter(value);
12
+ };
13
+
14
+ const QuerySearch = () => {
15
+ const { t } = useI18n("search");
16
+
17
+ return (
18
+ <SearchBox
19
+ placeholder={t("search-placeholder")}
20
+ value={treeQueryFilterValue.value}
21
+ onChange={handleQuerySearch}
22
+ changeDebounce={150}
23
+ />
24
+ );
25
+ };
26
+
27
+ export const ReportSearch = () => {
28
+ return <QuerySearch />;
29
+ };
@@ -0,0 +1,37 @@
1
+ import { Text } from "@allurereport/web-components";
2
+ import { useComputed } from "@preact/signals";
3
+ import { type ComponentChildren } from "preact";
4
+ import { useCallback } from "preact/hooks";
5
+ import { setTreeStatus, treeStatus } from "@/stores/treeFilters/store";
6
+ import type { AwesomeStatus } from "../../../types.js";
7
+ import * as styles from "./styles.scss";
8
+
9
+ export const ReportTabsList = (props: { children: ComponentChildren }) => {
10
+ return <div className={styles.tabsList}>{props.children}</div>;
11
+ };
12
+
13
+ export const ReportTab = (props: { id: AwesomeStatus; children: ComponentChildren }) => {
14
+ const { id, children, ...rest } = props;
15
+ const isCurrentTab = useComputed(() => treeStatus.value === id);
16
+
17
+ const handleCurrentTabClick = useCallback(() => {
18
+ setTreeStatus("total");
19
+ }, []);
20
+
21
+ const handleTabClick = useCallback(() => {
22
+ setTreeStatus(id);
23
+ }, [id]);
24
+
25
+ return (
26
+ <button
27
+ {...rest}
28
+ className={styles.tab}
29
+ onClick={isCurrentTab.value ? handleCurrentTabClick : handleTabClick}
30
+ aria-current={isCurrentTab.value || undefined}
31
+ >
32
+ <Text type="paragraph" size="m" bold>
33
+ {children}
34
+ </Text>
35
+ </button>
36
+ );
37
+ };
@@ -37,7 +37,7 @@ export const SectionPicker = () => {
37
37
  )}
38
38
  >
39
39
  <Menu.Section>
40
- {["default", ...availableSections.value].map((value) => (
40
+ {["default", ...availableSections].map((value) => (
41
41
  <Menu.ItemWithCheckmark
42
42
  onClick={() => setSection(value)}
43
43
  key={value}
@@ -1,13 +1,13 @@
1
1
  import { Loadable, PageLoader, Text } from "@allurereport/web-components";
2
+ import { computed } from "@preact/signals";
2
3
  import type { JSX } from "preact";
3
4
  import { useEffect, useRef, useState } from "preact/hooks";
4
- import { Footer } from "@/components/Footer";
5
- import { Header } from "@/components/Header";
6
5
  import MainReport from "@/components/MainReport";
7
6
  import SideBySide from "@/components/SideBySide";
8
7
  import TestResult from "@/components/TestResult";
9
8
  import { useI18n } from "@/stores";
10
- import { route } from "@/stores/router";
9
+ import { testResultRoute } from "@/stores/router";
10
+ import { currentTrId } from "@/stores/testResult";
11
11
  import { testResultStore } from "@/stores/testResults";
12
12
  import { treeStore } from "@/stores/tree";
13
13
  import * as styles from "./styles.scss";
@@ -30,8 +30,10 @@ const Loader = () => {
30
30
  );
31
31
  };
32
32
 
33
+ const isTestResultRoute = computed(() => testResultRoute.value.matches);
34
+
33
35
  export const SplitLayout = () => {
34
- const testResultId = route.value.params?.testResultId ?? null;
36
+ const testResultId = currentTrId.value;
35
37
  const [cachedMain, setCachedMain] = useState<JSX.Element | null>(null);
36
38
  const { t } = useI18n("controls");
37
39
  const leftSide = (
@@ -39,7 +41,7 @@ export const SplitLayout = () => {
39
41
  );
40
42
 
41
43
  const TrView = () => {
42
- return testResultId ? (
44
+ return isTestResultRoute.value ? (
43
45
  <Loadable
44
46
  source={testResultStore}
45
47
  renderLoader={() => <Loader />}
@@ -7,13 +7,7 @@ const TrStepsEmpty = () => {
7
7
  return (
8
8
  <div className={styles["test-steps-empty"]}>
9
9
  <div className={styles["test-steps-empty-wrapper"]}>
10
- <SvgIcon
11
- size={"m"}
12
- width={"32px"}
13
- height={"32px"}
14
- id={allureIcons.lineDevCodeSquare}
15
- className={styles["test-steps-empty-icon"]}
16
- />
10
+ <SvgIcon size={"xl"} id={allureIcons.lineDevCodeSquare} className={styles["test-steps-empty-icon"]} />
17
11
  <Text className={styles["test-steps-empty-text"]}>{t("no-test-steps-results")}</Text>
18
12
  </div>
19
13
  </div>
@@ -9,13 +9,7 @@ const TrThumb = () => {
9
9
  return (
10
10
  <div className={styles["test-result-thumb"]}>
11
11
  <div className={styles["test-result-thumb-wrapper"]}>
12
- <SvgIcon
13
- size={"m"}
14
- width={"32px"}
15
- height={"32px"}
16
- id={allureIcons.lineDevCodeSquare}
17
- className={styles["test-result-thumb-icon"]}
18
- />
12
+ <SvgIcon size={"xl"} id={allureIcons.lineDevCodeSquare} className={styles["test-result-thumb-icon"]} />
19
13
  <Text className={styles["test-result-thumb-text"]}>{t("no-test-case-results")}</Text>
20
14
  </div>
21
15
  </div>
@@ -5,7 +5,7 @@ import { type FunctionalComponent } from "preact";
5
5
  import { useState } from "preact/hooks";
6
6
  import { TrError } from "@/components/TestResult/TrError";
7
7
  import { useI18n } from "@/stores";
8
- import { navigateTo, openInNewTab } from "@/stores/router";
8
+ import { navigateToTestResult, openInNewTab } from "@/stores/router";
9
9
  import { timestampToDate } from "@/utils/time";
10
10
  import type { AwesomeTestResult } from "../../../../types";
11
11
  import * as styles from "./styles.scss";
@@ -42,7 +42,7 @@ export const TrEnvironmentItem: FunctionalComponent<{
42
42
  }
43
43
 
44
44
  e.stopPropagation();
45
- navigateTo(navigateUrl);
45
+ navigateToTestResult({ testResultId: id });
46
46
  }}
47
47
  >
48
48
  <TreeItemIcon status={status} className={styles["test-result-environment-item-status"]} />
@@ -13,7 +13,15 @@ import * as styles from "./styles.scss";
13
13
  const TrErrorTrace = ({ trace }: { trace: string }) => {
14
14
  const sanitizedTrace = ansiToHTML(trace, {
15
15
  fg: "var(--on-text-primary)",
16
- colors: {},
16
+ bg: "none",
17
+ colors: {
18
+ 0: "none",
19
+ 1: "none",
20
+ 2: "var(--on-support-sirius)",
21
+ 3: "var(--on-support-atlas)",
22
+ 4: "var(--bg-support-skat)",
23
+ 5: "var(--on-support-betelgeuse)",
24
+ },
17
25
  });
18
26
 
19
27
  return (
@@ -88,7 +96,6 @@ export const TrError: FunctionalComponent<TestError & { className?: string; stat
88
96
  ) : (
89
97
  empty("no-message-provided")
90
98
  )}
91
-
92
99
  {Boolean(actual && actual !== "undefined" && expected && expected !== "undefined") && (
93
100
  <Button
94
101
  style={"flat"}
@@ -2,7 +2,7 @@ import { IconButton, SvgIcon, Text, allureIcons } from "@allurereport/web-compon
2
2
  import clsx from "clsx";
3
3
  import type { AwesomeTestResult } from "types";
4
4
  import * as styles from "@/components/TestResult/TrHeader/styles.scss";
5
- import { navigateTo } from "@/stores/router";
5
+ import { navigateToRoot } from "@/stores/router";
6
6
 
7
7
  interface TrBreadcrumbs {
8
8
  testResult?: AwesomeTestResult;
@@ -19,7 +19,7 @@ export const TrBreadcrumbs = ({ testResult }: TrBreadcrumbs) => {
19
19
  size={"s"}
20
20
  style={"ghost"}
21
21
  className={styles["test-result-breadcrumb-link"]}
22
- onClick={() => navigateTo("")}
22
+ onClick={() => navigateToRoot()}
23
23
  />
24
24
  </div>
25
25
  {Boolean(breadcrumbs?.length) &&
@@ -9,15 +9,45 @@ import * as styles from "@/components/TestResult/TrHistory/styles.scss";
9
9
  import { useI18n } from "@/stores";
10
10
  import { timestampToDate } from "@/utils/time";
11
11
 
12
- export const TrHistoryItem: FunctionalComponent<{
13
- testResultItem: HistoryTestResult;
14
- }> = ({ testResultItem }: { testResultItem: HistoryTestResult }) => {
12
+ type Props = {
13
+ historyTr: HistoryTestResult;
14
+ };
15
+
16
+ const getDate = (historyTr: HistoryTestResult) => {
17
+ const { stop, duration, start } = historyTr;
18
+
19
+ if (stop) {
20
+ return timestampToDate(stop);
21
+ }
22
+
23
+ if (start && duration) {
24
+ return timestampToDate(start + duration);
25
+ }
26
+
27
+ return undefined;
28
+ };
29
+
30
+ const HistoryDate = (props: { date: string | undefined }) => {
31
+ const { t } = useI18n("trHistory");
32
+ const { date } = props;
33
+
34
+ return (
35
+ <Text data-unknown={!date || undefined} className={styles["test-result-history-item-text"]}>
36
+ {date ?? t("unknown-date")}
37
+ </Text>
38
+ );
39
+ };
40
+
41
+ export const TrHistoryItem: FunctionalComponent<Props> = (props) => {
15
42
  const reportOptions = getReportOptions<AwesomeReportOptions & { id: string }>();
16
- const { status, error, stop, duration, id, url } = testResultItem;
43
+ const { historyTr } = props;
44
+ const { status, error, duration, id, url } = historyTr;
17
45
  const [isOpened, setIsOpen] = useState(false);
18
- const convertedStop = stop ? timestampToDate(stop) : undefined;
46
+ const historyDate = getDate(historyTr);
19
47
  const formattedDuration = duration ? formatDuration(duration) : undefined;
48
+
20
49
  const { t } = useI18n("controls");
50
+
21
51
  const navigateUrl = useMemo(() => {
22
52
  if (!url) {
23
53
  return undefined;
@@ -29,7 +59,8 @@ export const TrHistoryItem: FunctionalComponent<{
29
59
  navUrl.hash = id;
30
60
 
31
61
  return navUrl.toString();
32
- }, [reportOptions, id, url]);
62
+ }, [id, url]);
63
+
33
64
  const renderExternalLink = () => {
34
65
  if (!navigateUrl) {
35
66
  return null;
@@ -55,7 +86,7 @@ export const TrHistoryItem: FunctionalComponent<{
55
86
  return (
56
87
  <>
57
88
  <TreeItemIcon status={status} className={styles["test-result-history-item-status"]} />
58
- {convertedStop && <Text className={styles["test-result-history-item-text"]}>{convertedStop}</Text>}
89
+ <HistoryDate date={historyDate} />
59
90
  <div className={styles["test-result-history-item-info"]}>
60
91
  {formattedDuration && (
61
92
  <Text type="ui" size={"s"} className={styles["item-time"]}>
@@ -1,24 +1,34 @@
1
+ import { EmptyView } from "@allurereport/web-components";
1
2
  import type { FunctionalComponent } from "preact";
3
+ import { type AwesomeTestResult } from "@/../types";
2
4
  import { TrHistoryItem } from "@/components/TestResult/TrHistory/TrHistoryItem";
3
5
  import { useI18n } from "@/stores";
4
- import { type AwesomeTestResult } from "../../../../types";
5
6
  import * as styles from "./styles.scss";
6
7
 
7
8
  export type TrHistoryViewProps = {
8
9
  testResult?: AwesomeTestResult;
9
10
  };
10
11
 
11
- const TrHistoryView: FunctionalComponent<TrHistoryViewProps> = ({ testResult }) => {
12
- const { history } = testResult ?? {};
12
+ const TrHistoryView: FunctionalComponent<TrHistoryViewProps> = (props) => {
13
+ const { testResult = {} as AwesomeTestResult } = props;
14
+ const { history = [] } = testResult;
13
15
  const { t } = useI18n("empty");
14
16
 
17
+ const hasHistory = history.length > 0;
18
+
19
+ if (!hasHistory) {
20
+ return (
21
+ <div className={styles["test-result-history"]}>
22
+ <EmptyView description={t("no-history-results")} />
23
+ </div>
24
+ );
25
+ }
26
+
15
27
  return (
16
28
  <div className={styles["test-result-history"]}>
17
- {history.length ? (
18
- history?.map((item, key) => <TrHistoryItem testResultItem={item} key={key} />)
19
- ) : (
20
- <div className={styles["test-result-empty"]}>{t("no-history-results")}</div>
21
- )}
29
+ {history.map((item, key) => (
30
+ <TrHistoryItem key={key} historyTr={item} />
31
+ ))}
22
32
  </div>
23
33
  );
24
34
  };
@@ -29,6 +29,10 @@
29
29
 
30
30
  .test-result-history-item-text {
31
31
  padding-top: 2px;
32
+
33
+ &[data-unknown] {
34
+ color: var(--on-text-hint);
35
+ }
32
36
  }
33
37
 
34
38
  .test-result-history-item-info {
@@ -55,10 +59,3 @@
55
59
  .test-result-history-item-status {
56
60
  padding-top: 4px;
57
61
  }
58
-
59
- .test-result-empty {
60
- display: flex;
61
- padding: 48px 0;
62
- width: 100%;
63
- justify-content: center;
64
- }
@@ -34,6 +34,7 @@
34
34
 
35
35
  .test-result-name {
36
36
  margin-bottom: 8px;
37
+ word-break: break-word;
37
38
  }
38
39
 
39
40
  .test-result-duration {
@@ -1,81 +1,122 @@
1
- import { Code, IconButton, Loadable, TooltipWrapper, allureIcons } from "@allurereport/web-components";
2
- import type { FunctionalComponent } from "preact";
1
+ import { Code, IconButton, TooltipWrapper, allureIcons } from "@allurereport/web-components";
2
+ import { computed, useComputed } from "@preact/signals";
3
3
  import type { AwesomeTestResult } from "types";
4
4
  import { useI18n } from "@/stores";
5
- import { navigateTo } from "@/stores/router";
5
+ import { navigateToTestResult } from "@/stores/router";
6
+ import { trCurrentTab } from "@/stores/testResult";
6
7
  import { testResultNavStore } from "@/stores/testResults";
7
8
  import { copyToClipboard } from "@/utils/copyToClipboard";
8
9
  import * as styles from "./styles.scss";
9
10
 
10
- export type TrNavigationProps = {
11
+ type Props = {
11
12
  testResult?: AwesomeTestResult;
12
13
  };
13
14
 
14
- export const TrNavigation: FunctionalComponent<TrNavigationProps> = ({ testResult }) => {
15
- const { fullName, id: testResultId } = testResult ?? {};
16
- const id = testResultId || "";
17
- const { t: tooltip } = useI18n("controls");
18
- const FullName = () => {
19
- return (
20
- <div data-testid="test-result-fullname" className={styles["test-result-fullName"]}>
21
- <TooltipWrapper tooltipText={tooltip("clipboard")} tooltipTextAfterClick={tooltip("clipboardSuccess")}>
22
- <IconButton
23
- data-testid="test-result-fullname-copy"
24
- style={"ghost"}
25
- size={"s"}
26
- icon={allureIcons.lineGeneralCopy3}
27
- onClick={() => copyToClipboard(fullName)}
28
- />
29
- </TooltipWrapper>
30
- <Code tag={"div"} size={"s"} className={styles["test-result-fullName-text"]}>
31
- {fullName && fullName}
32
- </Code>
33
- </div>
34
- );
35
- };
15
+ const NavArrow = (props: { trId: string | undefined; type: "prev" | "next" }) => {
16
+ const { trId, type } = props;
17
+
18
+ const { t: tooltipT } = useI18n("controls");
19
+ const isDisabled = trId === undefined;
20
+ const isPrevArrow = type === "prev";
21
+
22
+ const icon = isPrevArrow ? allureIcons.lineArrowsChevronUp : allureIcons.lineArrowsChevronDown;
23
+
24
+ const prevTooltip = tooltipT("prevTR");
25
+ const nextTooltip = tooltipT("nextTR");
26
+ const testId = `test-result-nav-${type}`;
27
+
28
+ if (isDisabled) {
29
+ return <IconButton icon={icon} style="ghost" isDisabled data-testid={testId} />;
30
+ }
31
+
32
+ return (
33
+ <TooltipWrapper tooltipText={isPrevArrow ? prevTooltip : nextTooltip}>
34
+ <IconButton
35
+ icon={icon}
36
+ style="ghost"
37
+ data-testid={testId}
38
+ onClick={() => navigateToTestResult({ testResultId: trId, tab: trCurrentTab.value })}
39
+ />
40
+ </TooltipWrapper>
41
+ );
42
+ };
43
+
44
+ const FullName = (props: { fullName: string }) => {
45
+ const { fullName } = props;
46
+ const { t: tooltipT } = useI18n("controls");
47
+
48
+ return (
49
+ <div data-testid="test-result-fullname" className={styles.fullName}>
50
+ <TooltipWrapper tooltipText={tooltipT("clipboard")} tooltipTextAfterClick={tooltipT("clipboardSuccess")}>
51
+ <IconButton
52
+ data-testid="test-result-fullname-copy"
53
+ style="ghost"
54
+ size="s"
55
+ iconColor="secondary"
56
+ icon={allureIcons.lineGeneralCopy3}
57
+ onClick={() => copyToClipboard(fullName)}
58
+ />
59
+ </TooltipWrapper>
60
+ <Code tag="div" size="s" className={styles.text}>
61
+ {fullName}
62
+ </Code>
63
+ </div>
64
+ );
65
+ };
66
+
67
+ const Counter = (props: { current: number; total: number }) => {
68
+ const { current, total } = props;
69
+
70
+ return (
71
+ <Code data-testid="test-result-nav-current" size="s" className={styles.counter}>
72
+ {current}&#47;{total}
73
+ </Code>
74
+ );
75
+ };
76
+
77
+ const trData = computed(() => testResultNavStore.value.data ?? []);
78
+ const hasData = computed(() => trData.value.length > 0);
79
+
80
+ const Controls = (props: { currentId: string }) => {
81
+ const { currentId } = props;
82
+
83
+ const nextTrId = useComputed<string | undefined>(() => trData.value[trData.value.indexOf(currentId) + 1]);
84
+ const prevTrId = useComputed<string | undefined>(() => trData.value[trData.value.indexOf(currentId) - 1]);
85
+ const currentIndex = useComputed(() => trData.value.indexOf(currentId) + 1);
86
+ const total = useComputed(() => trData.value.length);
87
+
88
+ if (!hasData.value) {
89
+ return null;
90
+ }
91
+
92
+ return (
93
+ <div className={styles.controls}>
94
+ <NavArrow trId={prevTrId.value} type="prev" />
95
+ <Counter current={currentIndex.value} total={total.value} />
96
+ <NavArrow trId={nextTrId.value} type="next" />
97
+ </div>
98
+ );
99
+ };
100
+
101
+ export const TrNavigation = (props: Props) => {
102
+ const { testResult } = props;
103
+
104
+ if (!testResult?.id) {
105
+ return null;
106
+ }
107
+
108
+ const isHidden = !!testResult?.hidden;
109
+ const hasFullName = !!testResult?.fullName;
110
+
111
+ // Nothing to show
112
+ if ((isHidden || !hasData.value) && !hasFullName) {
113
+ return null;
114
+ }
36
115
 
37
116
  return (
38
- <Loadable
39
- source={testResultNavStore}
40
- renderData={(data) => {
41
- const currentIndex = data.indexOf(id) + 1;
42
-
43
- return (
44
- <div className={styles["test-result-nav"]}>
45
- {fullName && <FullName />}
46
- {data && !testResult?.hidden && (
47
- <div className={styles["test-result-navigator"]}>
48
- <TooltipWrapper tooltipText={tooltip("prevTR")} isTriggerActive={currentIndex > 1}>
49
- <IconButton
50
- icon={allureIcons.lineArrowsChevronDown}
51
- style={"ghost"}
52
- isDisabled={currentIndex === data.length}
53
- data-testid="test-result-nav-prev"
54
- className={styles["test-result-nav-prev"]}
55
- onClick={() => navigateTo(data[currentIndex])}
56
- />
57
- </TooltipWrapper>
58
- <Code
59
- data-testid="test-result-nav-current"
60
- size={"s"}
61
- className={styles["test-result-navigator-numbers"]}
62
- >
63
- {currentIndex}/{data.length}
64
- </Code>
65
- <TooltipWrapper tooltipText={tooltip("nextTR")}>
66
- <IconButton
67
- icon={allureIcons.lineArrowsChevronDown}
68
- style={"ghost"}
69
- isDisabled={currentIndex <= 1}
70
- data-testid="test-result-nav-next"
71
- onClick={() => navigateTo(data[currentIndex - 2])}
72
- />
73
- </TooltipWrapper>
74
- </div>
75
- )}
76
- </div>
77
- );
78
- }}
79
- />
117
+ <div className={styles.nav}>
118
+ {hasFullName && <FullName fullName={testResult.fullName} />}
119
+ {!isHidden && <Controls currentId={testResult.id} />}
120
+ </div>
80
121
  );
81
122
  };