@allurereport/web-awesome 3.0.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist/multi/173.app-d0210ed2e64d38a2ee8e.js +1 -0
  2. package/dist/multi/174.app-d0210ed2e64d38a2ee8e.js +1 -0
  3. package/dist/multi/252.app-d0210ed2e64d38a2ee8e.js +1 -0
  4. package/dist/multi/282.app-d0210ed2e64d38a2ee8e.js +1 -0
  5. package/dist/multi/29.app-d0210ed2e64d38a2ee8e.js +1 -0
  6. package/dist/multi/416.app-d0210ed2e64d38a2ee8e.js +1 -0
  7. package/dist/multi/527.app-d0210ed2e64d38a2ee8e.js +1 -0
  8. package/dist/multi/600.app-d0210ed2e64d38a2ee8e.js +1 -0
  9. package/dist/multi/605.app-d0210ed2e64d38a2ee8e.js +1 -0
  10. package/dist/multi/638.app-d0210ed2e64d38a2ee8e.js +1 -0
  11. package/dist/multi/672.app-d0210ed2e64d38a2ee8e.js +1 -0
  12. package/dist/multi/686.app-d0210ed2e64d38a2ee8e.js +1 -0
  13. package/dist/multi/725.app-d0210ed2e64d38a2ee8e.js +1 -0
  14. package/dist/multi/741.app-d0210ed2e64d38a2ee8e.js +1 -0
  15. package/dist/multi/749.app-d0210ed2e64d38a2ee8e.js +1 -0
  16. package/dist/multi/755.app-d0210ed2e64d38a2ee8e.js +1 -0
  17. package/dist/multi/894.app-d0210ed2e64d38a2ee8e.js +1 -0
  18. package/dist/multi/943.app-d0210ed2e64d38a2ee8e.js +1 -0
  19. package/dist/multi/980.app-d0210ed2e64d38a2ee8e.js +1 -0
  20. package/dist/multi/app-d0210ed2e64d38a2ee8e.js +2 -0
  21. package/dist/multi/{app-bae2a0fe5738d77cd976.js.LICENSE.txt → app-d0210ed2e64d38a2ee8e.js.LICENSE.txt} +7 -0
  22. package/dist/multi/manifest.json +21 -21
  23. package/dist/multi/styles-13107bbe6906beabc50f.css +49 -0
  24. package/dist/single/app-01fed10ad5f9083fd39c.js +2 -0
  25. package/dist/single/{app-996d3b5869f8fc942b66.js.LICENSE.txt → app-01fed10ad5f9083fd39c.js.LICENSE.txt} +7 -0
  26. package/dist/single/manifest.json +1 -1
  27. package/package.json +9 -11
  28. package/src/assets/scss/vars.scss +3 -0
  29. package/src/components/BaseLayout/index.tsx +25 -21
  30. package/src/components/BaseLayout/styles.scss +1 -0
  31. package/src/components/Charts/index.tsx +10 -7
  32. package/src/components/Footer/FooterVersion.tsx +14 -8
  33. package/src/components/Header/index.tsx +9 -7
  34. package/src/components/HeaderControls/index.tsx +5 -2
  35. package/src/components/MainReport/index.tsx +52 -47
  36. package/src/components/MainReport/styles.scss +1 -0
  37. package/src/components/Metadata/index.tsx +98 -7
  38. package/src/components/Metadata/styles.scss +10 -0
  39. package/src/components/ReportBody/HeaderActions.tsx +4 -13
  40. package/src/components/ReportBody/SortBy.tsx +27 -13
  41. package/src/components/ReportBody/index.tsx +1 -1
  42. package/src/components/ReportBody/styles.scss +4 -1
  43. package/src/components/ReportFilters/BaseFilters.tsx +345 -0
  44. package/src/components/ReportFilters/RetryFlaky.tsx +29 -0
  45. package/src/components/ReportFilters/TagsFilter.tsx +41 -0
  46. package/src/components/ReportFilters/TransitionFilter.tsx +49 -0
  47. package/src/components/ReportFilters/index.tsx +44 -0
  48. package/src/components/ReportFilters/styles.scss +55 -0
  49. package/src/components/ReportQualityGateResults/index.tsx +77 -19
  50. package/src/components/ReportQualityGateResults/styles.scss +13 -0
  51. package/src/components/ReportSearch/index.tsx +29 -0
  52. package/src/components/ReportTabs/index.tsx +1 -1
  53. package/src/components/SectionPicker/index.tsx +1 -1
  54. package/src/components/SplitLayout/index.tsx +7 -3
  55. package/src/components/TestResult/TrDescription/index.tsx +60 -10
  56. package/src/components/TestResult/TrDescription/styles.scss +4 -0
  57. package/src/components/TestResult/TrEnvironmentItem/index.tsx +2 -2
  58. package/src/components/TestResult/TrError/TrDiff.tsx +2 -6
  59. package/src/components/TestResult/TrError/styles.scss +4 -0
  60. package/src/components/TestResult/TrHeader/TrBreadcrumbs.tsx +2 -2
  61. package/src/components/TestResult/TrHistory/TrHistoryItem.tsx +38 -7
  62. package/src/components/TestResult/TrHistory/index.tsx +18 -8
  63. package/src/components/TestResult/TrHistory/styles.scss +4 -7
  64. package/src/components/TestResult/TrInfo/styles.scss +1 -0
  65. package/src/components/TestResult/TrLinks/index.tsx +4 -4
  66. package/src/components/TestResult/TrNavigation/index.tsx +109 -68
  67. package/src/components/TestResult/TrNavigation/styles.scss +15 -25
  68. package/src/components/TestResult/TrOverview.tsx +3 -2
  69. package/src/components/TestResult/TrPwTraces/PwTraceButton.tsx +1 -8
  70. package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +2 -3
  71. package/src/components/TestResult/TrRetriesView/index.tsx +4 -3
  72. package/src/components/TestResult/TrSteps/TrAttachment.tsx +5 -3
  73. package/src/components/TestResult/TrSteps/TrAttachmentInfo.tsx +10 -3
  74. package/src/components/TestResult/TrTabs/index.tsx +7 -23
  75. package/src/components/TestResult/index.tsx +9 -4
  76. package/src/components/TestResult/styles.scss +1 -0
  77. package/src/components/Timeline/index.tsx +2 -5
  78. package/src/components/Tree/index.tsx +14 -9
  79. package/src/index.html +19 -18
  80. package/src/index.tsx +20 -27
  81. package/src/locales/az.json +42 -13
  82. package/src/locales/de.json +42 -13
  83. package/src/locales/en.json +42 -13
  84. package/src/locales/es.json +42 -13
  85. package/src/locales/fr.json +42 -13
  86. package/src/locales/he.json +42 -13
  87. package/src/locales/hy.json +42 -13
  88. package/src/locales/it.json +42 -13
  89. package/src/locales/ja.json +42 -13
  90. package/src/locales/ka.json +42 -13
  91. package/src/locales/kr.json +42 -13
  92. package/src/locales/nl.json +42 -13
  93. package/src/locales/pl.json +42 -13
  94. package/src/locales/pt.json +42 -13
  95. package/src/locales/ru.json +42 -13
  96. package/src/locales/sv.json +42 -13
  97. package/src/locales/tr.json +42 -13
  98. package/src/locales/{ua.json → uk.json} +42 -13
  99. package/src/locales/zh.json +42 -13
  100. package/src/stores/chart.ts +2 -2
  101. package/src/stores/env.ts +6 -6
  102. package/src/stores/envInfo.ts +2 -2
  103. package/src/stores/globals.ts +1 -1
  104. package/src/stores/index.ts +0 -1
  105. package/src/stores/layout.ts +20 -11
  106. package/src/stores/locale.ts +71 -38
  107. package/src/stores/qualityGate.ts +4 -4
  108. package/src/stores/router.ts +25 -91
  109. package/src/stores/sections.ts +32 -45
  110. package/src/stores/stats.ts +4 -4
  111. package/src/stores/testResult.ts +5 -0
  112. package/src/stores/testResults.ts +7 -5
  113. package/src/stores/timeline.ts +5 -2
  114. package/src/stores/tree.ts +20 -13
  115. package/src/stores/treeFilters/actions.ts +48 -52
  116. package/src/stores/treeFilters/constants.ts +11 -5
  117. package/src/stores/treeFilters/model.ts +51 -0
  118. package/src/stores/treeFilters/store.ts +260 -60
  119. package/src/stores/treeFilters/utils.ts +132 -0
  120. package/src/stores/treeSort.ts +71 -0
  121. package/src/stores/variables.ts +3 -3
  122. package/src/utils/ownerAddress.ts +92 -0
  123. package/src/utils/time.ts +16 -2
  124. package/src/utils/treeFilters.ts +48 -66
  125. package/test/components/Header.test.tsx +49 -58
  126. package/test/utils/ownerAddress.test.ts +89 -0
  127. package/test/utils/treeFilters.test.ts +18 -321
  128. package/types.d.ts +3 -2
  129. package/dist/multi/173.app-bae2a0fe5738d77cd976.js +0 -1
  130. package/dist/multi/174.app-bae2a0fe5738d77cd976.js +0 -1
  131. package/dist/multi/252.app-bae2a0fe5738d77cd976.js +0 -1
  132. package/dist/multi/282.app-bae2a0fe5738d77cd976.js +0 -1
  133. package/dist/multi/29.app-bae2a0fe5738d77cd976.js +0 -1
  134. package/dist/multi/416.app-bae2a0fe5738d77cd976.js +0 -1
  135. package/dist/multi/527.app-bae2a0fe5738d77cd976.js +0 -1
  136. package/dist/multi/600.app-bae2a0fe5738d77cd976.js +0 -1
  137. package/dist/multi/605.app-bae2a0fe5738d77cd976.js +0 -1
  138. package/dist/multi/638.app-bae2a0fe5738d77cd976.js +0 -1
  139. package/dist/multi/672.app-bae2a0fe5738d77cd976.js +0 -1
  140. package/dist/multi/686.app-bae2a0fe5738d77cd976.js +0 -1
  141. package/dist/multi/725.app-bae2a0fe5738d77cd976.js +0 -1
  142. package/dist/multi/741.app-bae2a0fe5738d77cd976.js +0 -1
  143. package/dist/multi/755.app-bae2a0fe5738d77cd976.js +0 -1
  144. package/dist/multi/894.app-bae2a0fe5738d77cd976.js +0 -1
  145. package/dist/multi/91.app-bae2a0fe5738d77cd976.js +0 -1
  146. package/dist/multi/943.app-bae2a0fe5738d77cd976.js +0 -1
  147. package/dist/multi/980.app-bae2a0fe5738d77cd976.js +0 -1
  148. package/dist/multi/app-bae2a0fe5738d77cd976.js +0 -2
  149. package/dist/multi/styles-bbf68b2ba63c38b53c38.css +0 -48
  150. package/dist/single/app-996d3b5869f8fc942b66.js +0 -2
  151. package/src/components/ReportBody/Filters.tsx +0 -122
  152. package/src/stores/theme.ts +0 -30
  153. package/src/stores/treeFilters/index.ts +0 -3
  154. package/src/stores/treeFilters/types.ts +0 -12
  155. package/test/stores/treeFilters.test.ts +0 -302
@@ -1,40 +1,98 @@
1
+ import { DEFAULT_ENVIRONMENT } from "@allurereport/core-api";
2
+ import type { QualityGateValidationResult } from "@allurereport/plugin-api";
1
3
  import { Loadable, SvgIcon, Text, allureIcons } from "@allurereport/web-components";
4
+ import { useState } from "preact/hooks";
5
+ import { MetadataButton } from "@/components/MetadataButton";
2
6
  import { TrError } from "@/components/TestResult/TrError";
3
7
  import { useI18n } from "@/stores";
8
+ import { currentEnvironment } from "@/stores/env";
4
9
  import { qualityGateStore } from "@/stores/qualityGate";
5
10
  import * as styles from "./styles.scss";
6
11
 
12
+ const QualityGateResultsList = ({ results }: { results: QualityGateValidationResult[] }) => (
13
+ <ul className={styles["report-quality-gate-results-list"]} data-testid={"quality-gate-results-section-env-content"}>
14
+ {results.map((result) => (
15
+ <li key={result.rule} data-testid="quality-gate-result">
16
+ <div className={styles["report-quality-gate-result"]}>
17
+ <SvgIcon id={allureIcons.solidXCircle} className={styles["report-quality-gate-result-icon"]} />
18
+ <div className={styles["report-quality-gate-result-content"]}>
19
+ <Text tag="p" size="l" type="ui" bold data-testid="quality-gate-result-rule">
20
+ {result.rule}
21
+ </Text>
22
+ <TrError
23
+ className={styles["report-quality-gate-result-error"]}
24
+ message={result.message}
25
+ data-testid="quality-gate-result-message"
26
+ />
27
+ </div>
28
+ </div>
29
+ </li>
30
+ ))}
31
+ </ul>
32
+ );
33
+
7
34
  export const ReportQualityGateResults = () => {
8
35
  const { t } = useI18n("empty");
36
+ const { t: tEnvironments } = useI18n("environments");
37
+ const [collapsedEnvs, setCollapsedEnvs] = useState<string[]>([]);
9
38
 
10
39
  return (
11
40
  <Loadable
12
41
  source={qualityGateStore}
13
42
  renderData={(results) => {
14
- if (!results.length) {
43
+ if (currentEnvironment.value) {
44
+ const currentEnvResults = results[currentEnvironment.value] ?? [];
45
+
46
+ if (!currentEnvResults.length) {
47
+ return <div className={styles["report-quality-gate-results-empty"]}>{t("no-quality-gate-results")}</div>;
48
+ }
49
+
50
+ return <QualityGateResultsList results={currentEnvResults} />;
51
+ }
52
+
53
+ const entries = Object.entries(results).filter(([, envResults]) => envResults.length > 0);
54
+
55
+ if (!entries.length) {
15
56
  return <div className={styles["report-quality-gate-results-empty"]}>{t("no-quality-gate-results")}</div>;
16
57
  }
17
58
 
59
+ // single default environment
60
+ if (entries.length === 1 && entries[0][0] === DEFAULT_ENVIRONMENT) {
61
+ const currentEnvResults = entries[0][1] ?? [];
62
+
63
+ if (!currentEnvResults.length) {
64
+ return <div className={styles["report-quality-gate-results-empty"]}>{t("no-quality-gate-results")}</div>;
65
+ }
66
+
67
+ return <QualityGateResultsList results={currentEnvResults} />;
68
+ }
69
+
18
70
  return (
19
- <ul className={styles["report-quality-gate-results"]}>
20
- {results.map((result) => (
21
- <li key={result.rule} data-testid="report-quality-gate-result">
22
- <div className={styles["report-quality-gate-result"]}>
23
- <SvgIcon id={allureIcons.solidXCircle} className={styles["report-quality-gate-result-icon"]} />
24
- <div className={styles["report-quality-gate-result-content"]}>
25
- <Text tag="p" size="l" type="ui" bold data-testid="report-quality-gate-result-rule">
26
- {result.rule}
27
- </Text>
28
- <TrError
29
- className={styles["report-quality-gate-result-error"]}
30
- message={result.message}
31
- data-testid="report-quality-gate-result-message"
32
- />
33
- </div>
71
+ <div className={styles["report-quality-gate-results"]}>
72
+ {entries.map(([env, envResults]) => {
73
+ const isOpened = !collapsedEnvs.includes(env);
74
+ const toggleEnv = () => {
75
+ setCollapsedEnvs((prev) => (isOpened ? prev.concat(env) : prev.filter((e) => e !== env)));
76
+ };
77
+
78
+ return (
79
+ <div
80
+ key={env}
81
+ className={styles["report-quality-gate-section"]}
82
+ data-testid={"quality-gate-results-section"}
83
+ >
84
+ <MetadataButton
85
+ isOpened={isOpened}
86
+ setIsOpen={toggleEnv}
87
+ title={`${tEnvironments("environment", { count: 1 })}: "${env}"`}
88
+ counter={envResults.length}
89
+ data-testid={"quality-gate-results-section-env-button"}
90
+ />
91
+ {isOpened && <QualityGateResultsList results={envResults} />}
34
92
  </div>
35
- </li>
36
- ))}
37
- </ul>
93
+ );
94
+ })}
95
+ </div>
38
96
  );
39
97
  }}
40
98
  />
@@ -1,6 +1,11 @@
1
1
  @import "~@allurereport/web-components/mixins.scss";
2
2
 
3
3
  .report-quality-gate-results {
4
+ padding: 12px 0 32px 8px;
5
+ min-height: 320px;
6
+ }
7
+
8
+ .report-quality-gate-results-list {
4
9
  padding: 20px 0;
5
10
 
6
11
  & > li + li {
@@ -42,3 +47,11 @@
42
47
  .report-quality-gate-result-error {
43
48
  margin-top: 8px;
44
49
  }
50
+
51
+ .report-quality-gate-section {
52
+ &:not(:last-child) {
53
+ padding-bottom: 8px;
54
+ margin-bottom: 14px;
55
+ border-bottom: 1px solid var(--on-border-muted);
56
+ }
57
+ }
@@ -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
+ };
@@ -2,7 +2,7 @@ import { Text } from "@allurereport/web-components";
2
2
  import { useComputed } from "@preact/signals";
3
3
  import { type ComponentChildren } from "preact";
4
4
  import { useCallback } from "preact/hooks";
5
- import { setTreeStatus, treeStatus } from "@/stores/treeFilters";
5
+ import { setTreeStatus, treeStatus } from "@/stores/treeFilters/store";
6
6
  import type { AwesomeStatus } from "../../../types.js";
7
7
  import * as styles from "./styles.scss";
8
8
 
@@ -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,11 +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
5
  import MainReport from "@/components/MainReport";
5
6
  import SideBySide from "@/components/SideBySide";
6
7
  import TestResult from "@/components/TestResult";
7
8
  import { useI18n } from "@/stores";
8
- import { route } from "@/stores/router";
9
+ import { testResultRoute } from "@/stores/router";
10
+ import { currentTrId } from "@/stores/testResult";
9
11
  import { testResultStore } from "@/stores/testResults";
10
12
  import { treeStore } from "@/stores/tree";
11
13
  import * as styles from "./styles.scss";
@@ -28,8 +30,10 @@ const Loader = () => {
28
30
  );
29
31
  };
30
32
 
33
+ const isTestResultRoute = computed(() => testResultRoute.value.matches);
34
+
31
35
  export const SplitLayout = () => {
32
- const testResultId = route.value.params?.testResultId ?? null;
36
+ const testResultId = currentTrId.value;
33
37
  const [cachedMain, setCachedMain] = useState<JSX.Element | null>(null);
34
38
  const { t } = useI18n("controls");
35
39
  const leftSide = (
@@ -37,7 +41,7 @@ export const SplitLayout = () => {
37
41
  );
38
42
 
39
43
  const TrView = () => {
40
- return testResultId ? (
44
+ return isTestResultRoute.value ? (
41
45
  <Loadable
42
46
  source={testResultStore}
43
47
  renderLoader={() => <Loader />}
@@ -1,25 +1,75 @@
1
- import { Text } from "@allurereport/web-components";
1
+ import { proseStyles, resolveCssVarDeclarations, sanitizeIframeHtml, themeStore } from "@allurereport/web-commons";
2
2
  import type { FunctionalComponent } from "preact";
3
- import { useState } from "preact/hooks";
3
+ import { useEffect, useMemo, useState } from "preact/hooks";
4
4
  import type { AwesomeTestResult } from "types";
5
5
  import { MetadataButton } from "@/components/MetadataButton";
6
6
  import * as styles from "./styles.scss";
7
7
 
8
8
  export type TrDescriptionProps = {
9
- description: AwesomeTestResult["description"];
9
+ descriptionHtml: AwesomeTestResult["descriptionHtml"];
10
10
  };
11
11
 
12
- export const TrDescription: FunctionalComponent<TrDescriptionProps> = ({ description }) => {
13
- const [isOpen, setIsOpen] = useState<boolean>(true);
12
+ const MIN_HEIGHT = 120;
13
+
14
+ export const TrDescription: FunctionalComponent<TrDescriptionProps> = ({ descriptionHtml }) => {
15
+ const [isOpen, setIsOpen] = useState(true);
16
+ const [blobUrl, setBlobUrl] = useState("");
17
+ const [height, setHeight] = useState(MIN_HEIGHT);
18
+ const currentTheme = themeStore.value.current;
19
+
20
+ const sanitized = useMemo(() => (descriptionHtml ? sanitizeIframeHtml(descriptionHtml) : ""), [descriptionHtml]);
21
+
22
+ useEffect(() => {
23
+ if (!sanitized) {
24
+ setBlobUrl("");
25
+ return;
26
+ }
27
+
28
+ const iframeThemeVars = resolveCssVarDeclarations(proseStyles);
29
+
30
+ const html = `<!DOCTYPE html>
31
+ <html data-theme="${currentTheme}">
32
+ <head>
33
+ <meta charset="utf-8">
34
+ <style>:root {${iframeThemeVars}}</style>
35
+ <style>${proseStyles}</style>
36
+ </head>
37
+ <body>${sanitized}</body>
38
+ </html>`;
39
+
40
+ const blob = new Blob([html], { type: "text/html" });
41
+ const url = URL.createObjectURL(blob);
42
+ setBlobUrl(url);
43
+
44
+ return () => URL.revokeObjectURL(url);
45
+ }, [currentTheme, sanitized]);
46
+
47
+ const handleLoad = (e: Event) => {
48
+ const iframe = e.currentTarget as HTMLIFrameElement;
49
+ const documentElement = iframe.contentDocument?.documentElement;
50
+ const body = iframe.contentDocument?.body;
51
+ const scrollHeight = Math.max(documentElement?.scrollHeight ?? 0, body?.scrollHeight ?? 0);
52
+ setHeight(Math.max(scrollHeight, MIN_HEIGHT));
53
+ };
14
54
 
15
55
  return (
16
- <div className={styles["test-result-description"]}>
56
+ <div className={styles["test-result-description"]} data-testid="test-result-description">
17
57
  <div className={styles["test-result-description-wrapper"]}>
18
- <MetadataButton title={"Description"} setIsOpen={setIsOpen} isOpened={isOpen} />
58
+ <MetadataButton title="Description" setIsOpen={setIsOpen} isOpened={isOpen} />
19
59
  {isOpen && (
20
- <Text tag={"p"} className={styles["test-result-description-text"]}>
21
- {description}
22
- </Text>
60
+ <div className={styles["test-result-description-text"]}>
61
+ {blobUrl && (
62
+ <iframe
63
+ data-testid="test-result-description-frame"
64
+ src={blobUrl}
65
+ width="100%"
66
+ height={String(height)}
67
+ style={{ border: 0 }}
68
+ sandbox="allow-same-origin"
69
+ onLoad={handleLoad}
70
+ />
71
+ )}
72
+ </div>
23
73
  )}
24
74
  </div>
25
75
  </div>
@@ -9,4 +9,8 @@
9
9
 
10
10
  .test-result-description-text {
11
11
  padding-top: 10px;
12
+
13
+ iframe {
14
+ display: block;
15
+ }
12
16
  }
@@ -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"]} />
@@ -1,5 +1,5 @@
1
1
  import { Button, Code, CodeViewer } from "@allurereport/web-components";
2
- import type { BaseOptions, Change } from "diff";
2
+ import type { Change } from "diff";
3
3
  import { diffChars, diffLines, diffWords } from "diff";
4
4
  import { useState } from "preact/hooks";
5
5
  import * as styles from "@/components/TestResult/TrError/styles.scss";
@@ -33,11 +33,7 @@ export const TrDiff = ({ expected, actual }: { expected: string; actual: string
33
33
  };
34
34
  const changeTypeDiff = (type: DiffType = "chars") => {
35
35
  const diffFn = diffFunctions[type];
36
- const result = (diffFn as (oldStr: string, newStr: string, options?: BaseOptions) => Change[])(
37
- expected,
38
- actual,
39
- {},
40
- );
36
+ const result: Change[] = diffFn(expected, actual, {});
41
37
 
42
38
  setDiffType(type);
43
39
  setDiff(result);
@@ -128,6 +128,10 @@
128
128
  &:hover {
129
129
  background: inherit;
130
130
  }
131
+
132
+ pre {
133
+ white-space: pre-wrap;
134
+ }
131
135
  }
132
136
 
133
137
  .diff {
@@ -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 {
@@ -22,13 +22,13 @@ const linksIconMap: Record<string, string> = {
22
22
  const TrLink: FunctionalComponent<{
23
23
  link: TrLinkProps;
24
24
  }> = ({ link }) => {
25
- const { url, type } = link;
25
+ const { url, name, type } = link;
26
26
 
27
27
  return (
28
- <div className={styles["test-result-link"]}>
28
+ <div className={styles["test-result-link"]} data-testid="test-result-meta-link">
29
29
  <SvgIcon id={linksIconMap[type] ?? allureIcons.lineGeneralLink1} />
30
30
  <Text tag={"a"} href={url} target={"_blank"} size={"m"} className={styles["test-result-link-text"]}>
31
- {url}
31
+ {name || url}
32
32
  </Text>
33
33
  </div>
34
34
  );
@@ -46,7 +46,7 @@ export const TrLinks: FunctionalComponent<TrLinksProps> = ({ links }) => {
46
46
  });
47
47
 
48
48
  return (
49
- <div className={styles["test-result-links"]}>
49
+ <div className={styles["test-result-links"]} data-testid="test-result-meta-links">
50
50
  <div className={styles["test-result-links-wrapper"]}>
51
51
  <MetadataButton isOpened={isOpened} setIsOpen={setIsOpen} counter={links.length} title={t("links")} />
52
52
  {isOpened && <div className={styles["test-result-links-list"]}>{linkMap}</div>}