@allurereport/web-awesome 3.8.2 → 3.10.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 (227) hide show
  1. package/README.md +112 -0
  2. package/allurerc-dev.mjs +10 -0
  3. package/dist/multi/121.app-b18cce138691927e8759.js +1 -0
  4. package/dist/multi/173.app-b18cce138691927e8759.js +1 -0
  5. package/dist/multi/174.app-b18cce138691927e8759.js +1 -0
  6. package/dist/multi/252.app-b18cce138691927e8759.js +1 -0
  7. package/dist/multi/282.app-b18cce138691927e8759.js +1 -0
  8. package/dist/multi/29.app-b18cce138691927e8759.js +1 -0
  9. package/dist/multi/310.app-b18cce138691927e8759.js +1 -0
  10. package/dist/multi/416.app-b18cce138691927e8759.js +1 -0
  11. package/dist/multi/507.app-b18cce138691927e8759.js +1 -0
  12. package/dist/multi/527.app-b18cce138691927e8759.js +1 -0
  13. package/dist/multi/600.app-b18cce138691927e8759.js +1 -0
  14. package/dist/multi/605.app-b18cce138691927e8759.js +1 -0
  15. package/dist/multi/638.app-b18cce138691927e8759.js +1 -0
  16. package/dist/multi/672.app-b18cce138691927e8759.js +1 -0
  17. package/dist/multi/686.app-b18cce138691927e8759.js +1 -0
  18. package/dist/multi/725.app-b18cce138691927e8759.js +1 -0
  19. package/dist/multi/741.app-b18cce138691927e8759.js +1 -0
  20. package/dist/multi/749.app-b18cce138691927e8759.js +1 -0
  21. package/dist/multi/755.app-b18cce138691927e8759.js +1 -0
  22. package/dist/multi/779.app-b18cce138691927e8759.js +1 -0
  23. package/dist/multi/894.app-b18cce138691927e8759.js +1 -0
  24. package/dist/multi/943.app-b18cce138691927e8759.js +1 -0
  25. package/dist/multi/980.app-b18cce138691927e8759.js +1 -0
  26. package/dist/multi/app-b18cce138691927e8759.js +2 -0
  27. package/dist/multi/manifest.json +26 -23
  28. package/dist/multi/styles-212da6c68fa0beb4c6c5.css +1 -0
  29. package/dist/multi/styles-5c882b14b6f3112e40c4.css +1 -0
  30. package/dist/multi/styles-a4f65de86208f79dd2be.css +58 -0
  31. package/dist/single/app-733f473da7b51f98876d.js +2 -0
  32. package/dist/single/manifest.json +1 -1
  33. package/package.json +19 -14
  34. package/src/assets/scss/_common.scss +2 -2
  35. package/src/assets/scss/index.scss +8 -6
  36. package/src/components/BaseLayout/index.tsx +14 -2
  37. package/src/components/BaseLayout/styles.scss +5 -5
  38. package/src/components/Categories/CategoryHeaderItem/styles.scss +2 -2
  39. package/src/components/Categories/CategoryTreeItem/styles.scss +2 -2
  40. package/src/components/Categories/GroupTreeItem/styles.scss +4 -5
  41. package/src/components/Categories/HistoryTreeItem/styles.scss +2 -2
  42. package/src/components/Categories/LabelTreeItem/styles.scss +2 -2
  43. package/src/components/Categories/MessageTreeItem/index.tsx +1 -1
  44. package/src/components/Categories/MessageTreeItem/styles.scss +18 -18
  45. package/src/components/Categories/sticky.ts +1 -1
  46. package/src/components/Footer/FooterVersion.tsx +5 -10
  47. package/src/components/Footer/index.tsx +7 -1
  48. package/src/components/Footer/styles.scss +8 -2
  49. package/src/components/Header/CiInfo/index.tsx +17 -13
  50. package/src/components/Header/CiInfo/styles.scss +1 -1
  51. package/src/components/Header/styles.scss +2 -2
  52. package/src/components/HeaderControls/index.tsx +1 -3
  53. package/src/components/HotkeysProvider/index.tsx +556 -0
  54. package/src/components/KeyboardShortcuts/index.tsx +73 -0
  55. package/src/components/KeyboardShortcuts/shortcutsConfig.ts +91 -0
  56. package/src/components/KeyboardShortcuts/styles.scss +69 -0
  57. package/src/components/MainReport/index.tsx +89 -72
  58. package/src/components/MainReport/styles.scss +20 -5
  59. package/src/components/Metadata/index.tsx +27 -6
  60. package/src/components/Metadata/styles.scss +21 -9
  61. package/src/components/MetadataButton/index.tsx +2 -0
  62. package/src/components/MetadataButton/styles.scss +1 -1
  63. package/src/components/NavTabs/styles.scss +8 -8
  64. package/src/components/ReportBody/styles.scss +3 -4
  65. package/src/components/ReportCategories/styles.scss +1 -1
  66. package/src/components/ReportFilters/styles.scss +1 -1
  67. package/src/components/ReportGlobalAttachments/styles.scss +1 -1
  68. package/src/components/ReportGlobalErrors/styles.scss +1 -1
  69. package/src/components/ReportHeader/index.tsx +25 -13
  70. package/src/components/ReportHeader/styles.scss +2 -2
  71. package/src/components/ReportMetadata/index.tsx +44 -15
  72. package/src/components/ReportMetadata/styles.scss +6 -6
  73. package/src/components/ReportQualityGateResults/styles.scss +2 -2
  74. package/src/components/ReportSearch/index.tsx +1 -5
  75. package/src/components/ReportTabs/styles.scss +9 -9
  76. package/src/components/SectionSwitcher/index.tsx +87 -10
  77. package/src/components/SideBySide/index.tsx +20 -2
  78. package/src/components/SideBySide/styles.scss +9 -1
  79. package/src/components/SplitLayout/index.tsx +11 -2
  80. package/src/components/SplitLayout/styles.scss +23 -4
  81. package/src/components/TestResult/TestStepsEmpty/styles.scss +1 -1
  82. package/src/components/TestResult/TrDescription/styles.scss +1 -1
  83. package/src/components/TestResult/TrDropdown/index.tsx +2 -2
  84. package/src/components/TestResult/TrDropdown/styles.scss +1 -1
  85. package/src/components/TestResult/TrEmpty/styles.scss +1 -1
  86. package/src/components/TestResult/TrEnvironmentItem/styles.scss +4 -4
  87. package/src/components/TestResult/TrError/index.tsx +32 -7
  88. package/src/components/TestResult/TrError/styles.scss +23 -23
  89. package/src/components/TestResult/TrHeader/styles.scss +2 -2
  90. package/src/components/TestResult/TrHistory/styles.scss +6 -6
  91. package/src/components/TestResult/TrInfo/styles.scss +8 -8
  92. package/src/components/TestResult/TrLinks/index.tsx +2 -2
  93. package/src/components/TestResult/TrLinks/styles.scss +2 -2
  94. package/src/components/TestResult/TrMetadata/index.tsx +1 -1
  95. package/src/components/TestResult/TrMetadata/styles.scss +1 -1
  96. package/src/components/TestResult/TrNavigation/index.tsx +1 -1
  97. package/src/components/TestResult/TrNavigation/styles.scss +2 -2
  98. package/src/components/TestResult/TrOverview.tsx +2 -0
  99. package/src/components/TestResult/TrParameters/index.tsx +1 -1
  100. package/src/components/TestResult/TrParameters/styles.scss +1 -1
  101. package/src/components/TestResult/TrPrevStatuses/styles.scss +8 -8
  102. package/src/components/TestResult/TrPwTraces/styles.scss +1 -1
  103. package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +27 -1
  104. package/src/components/TestResult/TrRetriesView/styles.scss +20 -10
  105. package/src/components/TestResult/TrSetup/index.tsx +10 -4
  106. package/src/components/TestResult/TrSeverity/styles.scss +7 -7
  107. package/src/components/TestResult/TrStatus/styles.scss +2 -35
  108. package/src/components/TestResult/TrSteps/TrAttachment.tsx +79 -43
  109. package/src/components/TestResult/TrSteps/TrAttachmentInfo.tsx +44 -17
  110. package/src/components/TestResult/TrSteps/TrBodyItems.tsx +5 -2
  111. package/src/components/TestResult/TrSteps/TrErrorStep.tsx +3 -0
  112. package/src/components/TestResult/TrSteps/TrStep.tsx +15 -6
  113. package/src/components/TestResult/TrSteps/TrStepHeader.tsx +8 -5
  114. package/src/components/TestResult/TrSteps/index.tsx +7 -5
  115. package/src/components/TestResult/TrSteps/stepTreeExpansion.ts +27 -9
  116. package/src/components/TestResult/TrSteps/styles.scss +80 -20
  117. package/src/components/TestResult/TrTeardown/index.tsx +10 -4
  118. package/src/components/TestResult/bodyItems.ts +1 -1
  119. package/src/components/TestResult/index.tsx +8 -2
  120. package/src/components/TestResult/styles.scss +10 -1
  121. package/src/components/TestResult/trOverviewFocus.scss +4 -0
  122. package/src/components/Timeline/styles.scss +6 -6
  123. package/src/components/Tree/index.tsx +79 -5
  124. package/src/components/Tree/styles.scss +55 -35
  125. package/src/hooks/useTestResultOverviewFocusScroll.ts +23 -0
  126. package/src/index.html +30 -33
  127. package/src/index.tsx +12 -6
  128. package/src/locales/ar.json +62 -1
  129. package/src/locales/az.json +62 -1
  130. package/src/locales/de.json +62 -1
  131. package/src/locales/en.json +62 -1
  132. package/src/locales/es.json +62 -1
  133. package/src/locales/fr.json +62 -1
  134. package/src/locales/he.json +62 -1
  135. package/src/locales/hy.json +62 -1
  136. package/src/locales/it.json +62 -1
  137. package/src/locales/ja.json +62 -1
  138. package/src/locales/ka.json +62 -1
  139. package/src/locales/kr.json +62 -1
  140. package/src/locales/nl.json +62 -1
  141. package/src/locales/pl.json +62 -1
  142. package/src/locales/pt.json +62 -1
  143. package/src/locales/ru.json +62 -1
  144. package/src/locales/sv.json +62 -1
  145. package/src/locales/tr.json +62 -1
  146. package/src/locales/uk.json +62 -1
  147. package/src/locales/zh-TW.json +62 -1
  148. package/src/locales/zh.json +62 -1
  149. package/src/stores/keyboard.ts +371 -0
  150. package/src/stores/keyboardActions.ts +769 -0
  151. package/src/stores/locale.ts +5 -2
  152. package/src/stores/reportEnvSections.ts +6 -0
  153. package/src/stores/reportRootTabs.ts +95 -0
  154. package/src/stores/search.ts +147 -0
  155. package/src/stores/testResultOverviewNav.ts +119 -0
  156. package/src/stores/testResultTabs.ts +62 -0
  157. package/src/stores/timeline.ts +1 -1
  158. package/src/stores/tree.ts +42 -4
  159. package/src/stores/treeFilters/store.ts +3 -36
  160. package/src/stores/treeSort.ts +7 -1
  161. package/src/styles/_pane-active.scss +8 -0
  162. package/src/styles.scss +1 -1
  163. package/src/utils/atSeparator.ts +4 -0
  164. package/src/utils/flattenTestResultOverview.ts +182 -0
  165. package/src/utils/time.ts +2 -1
  166. package/src/utils/trOverviewFocus.ts +18 -0
  167. package/src/utils/treeFilters.ts +15 -4
  168. package/test/components/EnvironmentPicker.test.tsx +21 -3
  169. package/test/components/Footer.test.tsx +26 -0
  170. package/test/components/Header/CiInfo.test.tsx +56 -0
  171. package/test/components/Header.test.tsx +8 -0
  172. package/test/components/HeaderControls.test.tsx +28 -0
  173. package/test/components/ReportGlobals.test.tsx +9 -1
  174. package/test/components/ReportHeader.test.tsx +77 -0
  175. package/test/components/ReportMetadata.test.tsx +131 -0
  176. package/test/components/TestResult/PwTraceButton.test.tsx +8 -0
  177. package/test/components/TestResult/TrErrorStep.test.tsx +8 -0
  178. package/test/components/TestResult/TrOverview.test.tsx +30 -10
  179. package/test/components/TestResult/TrRetriesItem.test.tsx +163 -0
  180. package/test/components/TestResult/TrSteps.test.tsx +108 -0
  181. package/test/components/TestResult/bodyItems.test.ts +9 -1
  182. package/test/components/TestResult/openPwTraceInNewTab.test.ts +8 -0
  183. package/test/components/TestResult/stepTreeExpansion.test.ts +10 -2
  184. package/test/components/Timeline.test.tsx +15 -7
  185. package/test/stores/keyboard/keyboardActions.test.ts +615 -0
  186. package/test/stores/search.test.ts +143 -0
  187. package/test/stores/treeFilters/actions.test.ts +8 -0
  188. package/test/stores/treeSort.test.ts +58 -0
  189. package/test/utils/flattenTestResultOverview.test.ts +57 -0
  190. package/test/utils/ownerAddress.test.ts +9 -1
  191. package/test/utils/time.test.ts +52 -0
  192. package/test/utils/treeFilters.test.ts +113 -1
  193. package/types.d.ts +39 -0
  194. package/webpack.config.js +12 -7
  195. package/CONTRIBUTING.md +0 -34
  196. package/dist/multi/173.app-f008fb8342025f2b1ace.js +0 -1
  197. package/dist/multi/174.app-f008fb8342025f2b1ace.js +0 -1
  198. package/dist/multi/252.app-f008fb8342025f2b1ace.js +0 -1
  199. package/dist/multi/282.app-f008fb8342025f2b1ace.js +0 -1
  200. package/dist/multi/29.app-f008fb8342025f2b1ace.js +0 -1
  201. package/dist/multi/310.app-f008fb8342025f2b1ace.js +0 -1
  202. package/dist/multi/416.app-f008fb8342025f2b1ace.js +0 -1
  203. package/dist/multi/507.app-f008fb8342025f2b1ace.js +0 -1
  204. package/dist/multi/527.app-f008fb8342025f2b1ace.js +0 -1
  205. package/dist/multi/600.app-f008fb8342025f2b1ace.js +0 -1
  206. package/dist/multi/605.app-f008fb8342025f2b1ace.js +0 -1
  207. package/dist/multi/638.app-f008fb8342025f2b1ace.js +0 -1
  208. package/dist/multi/672.app-f008fb8342025f2b1ace.js +0 -1
  209. package/dist/multi/686.app-f008fb8342025f2b1ace.js +0 -1
  210. package/dist/multi/725.app-f008fb8342025f2b1ace.js +0 -1
  211. package/dist/multi/741.app-f008fb8342025f2b1ace.js +0 -1
  212. package/dist/multi/749.app-f008fb8342025f2b1ace.js +0 -1
  213. package/dist/multi/755.app-f008fb8342025f2b1ace.js +0 -1
  214. package/dist/multi/894.app-f008fb8342025f2b1ace.js +0 -1
  215. package/dist/multi/943.app-f008fb8342025f2b1ace.js +0 -1
  216. package/dist/multi/980.app-f008fb8342025f2b1ace.js +0 -1
  217. package/dist/multi/app-f008fb8342025f2b1ace.js +0 -2
  218. package/dist/multi/styles-9f7a23a0c8b79fa76981.css +0 -58
  219. package/dist/single/app-07332238da9897064301.js +0 -2
  220. package/src/assets/scss/day.scss +0 -53
  221. package/src/assets/scss/fonts.scss +0 -3
  222. package/src/assets/scss/night.scss +0 -63
  223. package/src/assets/scss/palette.scss +0 -393
  224. package/src/assets/scss/theme.scss +0 -330
  225. package/src/assets/scss/vars.scss +0 -11
  226. /package/dist/multi/{app-f008fb8342025f2b1ace.js.LICENSE.txt → app-b18cce138691927e8759.js.LICENSE.txt} +0 -0
  227. /package/dist/single/{app-07332238da9897064301.js.LICENSE.txt → app-733f473da7b51f98876d.js.LICENSE.txt} +0 -0
@@ -0,0 +1,131 @@
1
+ import { getReportOptions } from "@allurereport/web-commons";
2
+ import { cleanup, render, screen } from "@testing-library/preact";
3
+ import { type Mock, beforeEach, describe, expect, it, vi } from "vitest";
4
+
5
+ import { ReportMetadata } from "@/components/ReportMetadata";
6
+
7
+ vi.mock("@allurereport/web-commons", async (importOriginal) => ({
8
+ ...(await importOriginal()),
9
+ getReportOptions: vi.fn(),
10
+ }));
11
+
12
+ vi.mock("@allurereport/web-components", () => {
13
+ const Menu = Object.assign(
14
+ (props: { children: unknown; menuTrigger: (props: { onClick: () => void }) => unknown }) => (
15
+ <>
16
+ {props.menuTrigger({ onClick: vi.fn() })}
17
+ {props.children}
18
+ </>
19
+ ),
20
+ {
21
+ Section: (props: { children: unknown }) => <>{props.children}</>,
22
+ },
23
+ );
24
+
25
+ return {
26
+ ArrowButton: () => <span />,
27
+ Button: (props: { text?: string; onClick?: () => void }) => <button onClick={props.onClick}>{props.text}</button>,
28
+ ButtonLink: (props: { href: string; text?: string }) => <a href={props.href}>{props.text}</a>,
29
+ Counter: (props: { count: number }) => <span>{props.count}</span>,
30
+ Loadable: (props: {
31
+ source: { value?: { data?: unknown } };
32
+ transformData?: (data: unknown) => unknown;
33
+ renderData: (data: unknown) => unknown;
34
+ }) =>
35
+ props.renderData(props.transformData ? props.transformData(props.source.value?.data) : props.source.value?.data),
36
+ Menu,
37
+ SvgIcon: () => <span />,
38
+ Text: (props: { children: unknown }) => <span>{props.children}</span>,
39
+ TooltipWrapper: (props: { children: unknown }) => props.children,
40
+ allureIcons: {
41
+ lineGeneralCopy3: "copy",
42
+ lineGeneralLinkExternal: "external",
43
+ },
44
+ useElementTruncation: () => ({ ref: { current: null }, isTruncated: false }),
45
+ };
46
+ });
47
+
48
+ vi.mock("@/stores", () => ({
49
+ reportStatsStore: { value: { data: undefined } },
50
+ statsByEnvStore: { value: { data: {} } },
51
+ useI18n: () => ({
52
+ t: (key: string) => key,
53
+ }),
54
+ }));
55
+
56
+ vi.mock("@/stores/env", () => ({
57
+ currentEnvironment: { value: undefined },
58
+ }));
59
+
60
+ vi.mock("@/stores/envInfo", () => ({
61
+ envInfoStore: { value: { data: [] } },
62
+ }));
63
+
64
+ vi.mock("@/stores/tree", () => ({
65
+ collapsedTrees: { value: new Set() },
66
+ toggleTree: vi.fn(),
67
+ }));
68
+
69
+ vi.mock("@/stores/variables", () => ({
70
+ fetchVariables: vi.fn(),
71
+ variables: { value: { data: { default: {} } } },
72
+ }));
73
+
74
+ beforeEach(() => {
75
+ vi.clearAllMocks();
76
+ cleanup();
77
+ });
78
+
79
+ describe("components > ReportMetadata", () => {
80
+ it("should render executor metadata as a clickable build link", () => {
81
+ (getReportOptions as Mock).mockReturnValue({
82
+ executor: {
83
+ name: "TeamCity",
84
+ buildName: "Wrike #123",
85
+ url: "https://teamcity.example",
86
+ reportUrl: "https://teamcity.example/report/123",
87
+ buildUrl: "https://teamcity.example/build/123",
88
+ },
89
+ });
90
+
91
+ render(<ReportMetadata />);
92
+
93
+ expect(screen.getByText("executor")).toBeInTheDocument();
94
+ expect(screen.getByRole("link", { name: "TeamCity · Wrike #123" })).toHaveAttribute(
95
+ "href",
96
+ "https://teamcity.example/build/123",
97
+ );
98
+ });
99
+
100
+ it("should fall back to report url when executor build url is missing", () => {
101
+ (getReportOptions as Mock).mockReturnValue({
102
+ executor: {
103
+ name: "TeamCity",
104
+ buildName: "Wrike #123",
105
+ reportUrl: "https://teamcity.example/report/123",
106
+ },
107
+ });
108
+
109
+ render(<ReportMetadata />);
110
+
111
+ expect(screen.getByRole("link", { name: "TeamCity · Wrike #123" })).toHaveAttribute(
112
+ "href",
113
+ "https://teamcity.example/report/123",
114
+ );
115
+ });
116
+
117
+ it("should render executor metadata as plain text when url is unsafe", () => {
118
+ (getReportOptions as Mock).mockReturnValue({
119
+ executor: {
120
+ name: "TeamCity",
121
+ buildName: "Wrike #123",
122
+ buildUrl: "javascript:alert(1)",
123
+ },
124
+ });
125
+
126
+ render(<ReportMetadata />);
127
+
128
+ expect(screen.queryByRole("link", { name: "TeamCity · Wrike #123" })).not.toBeInTheDocument();
129
+ expect(screen.getByTestId("metadata-item")).toHaveTextContent("TeamCity · Wrike #123");
130
+ });
131
+ });
@@ -1,6 +1,14 @@
1
1
  import { fireEvent, render, screen, waitFor } from "@testing-library/preact";
2
+ import { epic, feature, label, story } from "allure-js-commons";
2
3
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
4
 
5
+ beforeEach(async () => {
6
+ await epic("coverage");
7
+ await feature("ui-components");
8
+ await story("PwTraceButton");
9
+ await label("coverage", "ui-components");
10
+ });
11
+
4
12
  type SetupOptions = {
5
13
  canOpenInNewTab: boolean;
6
14
  failFetch?: boolean;
@@ -1,6 +1,14 @@
1
1
  import { cleanup, fireEvent, render } from "@testing-library/preact";
2
+ import { epic, feature, label, story } from "allure-js-commons";
2
3
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
4
 
5
+ beforeEach(async () => {
6
+ await epic("coverage");
7
+ await feature("ui-components");
8
+ await story("TrErrorStep");
9
+ await label("coverage", "ui-components");
10
+ });
11
+
4
12
  const { matchMediaMock } = vi.hoisted(() => {
5
13
  const matchMediaMock = vi.fn().mockImplementation(() => ({
6
14
  matches: false,
@@ -1,7 +1,15 @@
1
1
  import { cleanup, render, screen } from "@testing-library/preact";
2
+ import { epic, feature, label, story } from "allure-js-commons";
2
3
  import type { AwesomeTestResult } from "types";
3
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
5
 
6
+ beforeEach(async () => {
7
+ await epic("coverage");
8
+ await feature("ui-components");
9
+ await story("TrOverview");
10
+ await label("coverage", "ui-components");
11
+ });
12
+
5
13
  import { TrOverview } from "@/components/TestResult/TrOverview";
6
14
 
7
15
  vi.mock("@/components/TestResult/TestStepsEmpty", () => ({
@@ -12,32 +20,36 @@ vi.mock("@/components/TestResult/TrSteps", () => ({
12
20
  TrSteps: () => <div data-testid="tr-steps" />,
13
21
  }));
14
22
 
23
+ vi.mock("@/components/TestResult/TrError", () => ({
24
+ TrError: () => <div data-testid="test-result-error" />,
25
+ }));
26
+
15
27
  vi.mock("@/components/TestResult/TrDescription", () => ({
16
- TrDescription: () => null,
28
+ TrDescription: () => null as any,
17
29
  }));
18
30
 
19
31
  vi.mock("@/components/TestResult/TrLinks", () => ({
20
- TrLinks: () => null,
32
+ TrLinks: () => null as any,
21
33
  }));
22
34
 
23
35
  vi.mock("@/components/TestResult/TrMetadata", () => ({
24
- TrMetadata: () => null,
36
+ TrMetadata: () => null as any,
25
37
  }));
26
38
 
27
39
  vi.mock("@/components/TestResult/TrParameters", () => ({
28
- TrParameters: () => null,
40
+ TrParameters: () => null as any,
29
41
  }));
30
42
 
31
43
  vi.mock("@/components/TestResult/TrPwTraces", () => ({
32
- TrPwTraces: () => null,
44
+ TrPwTraces: () => null as any,
33
45
  }));
34
46
 
35
47
  vi.mock("@/components/TestResult/TrSetup", () => ({
36
- TrSetup: () => null,
48
+ TrSetup: () => null as any,
37
49
  }));
38
50
 
39
51
  vi.mock("@/components/TestResult/TrTeardown", () => ({
40
- TrTeardown: () => null,
52
+ TrTeardown: () => null as any,
41
53
  }));
42
54
 
43
55
  vi.mock("@/stores/locale", () => ({
@@ -48,6 +60,7 @@ vi.mock("@/stores/locale", () => ({
48
60
 
49
61
  vi.mock("@/stores/testResult", () => ({
50
62
  currentTrId: { value: "current-test-result-id" },
63
+ trCurrentTab: { value: "overview" },
51
64
  }));
52
65
 
53
66
  const makeTestResult = (overrides: Partial<AwesomeTestResult> = {}): AwesomeTestResult =>
@@ -59,7 +72,7 @@ const makeTestResult = (overrides: Partial<AwesomeTestResult> = {}): AwesomeTest
59
72
  flaky: false,
60
73
  muted: false,
61
74
  known: false,
62
- hidden: false,
75
+ isRetry: false,
63
76
  labels: [],
64
77
  groupedLabels: {},
65
78
  parameters: [],
@@ -98,8 +111,15 @@ describe("components > TestResult > TrOverview", () => {
98
111
  expect(screen.queryByTestId("test-steps-empty")).not.toBeInTheDocument();
99
112
  });
100
113
 
101
- it("should render TrSteps when the test has an error", () => {
102
- render(<TrOverview testResult={makeTestResult({ error: { message: "boom" } })} />);
114
+ it("should render TrSteps when the test has steps and an error", () => {
115
+ render(
116
+ <TrOverview
117
+ testResult={makeTestResult({
118
+ error: { message: "boom" },
119
+ steps: [{ type: "step", name: "step", status: "passed", parameters: [], steps: [] }],
120
+ })}
121
+ />,
122
+ );
103
123
 
104
124
  expect(screen.getByTestId("tr-steps")).toBeInTheDocument();
105
125
  expect(screen.queryByTestId("test-steps-empty")).not.toBeInTheDocument();
@@ -0,0 +1,163 @@
1
+ import { fireEvent, render, screen } from "@testing-library/preact";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ import { TrRetriesItem } from "@/components/TestResult/TrRetriesView/TrRetriesItem";
5
+
6
+ import type { AwesomeTestResult } from "../../../types";
7
+
8
+ vi.mock("@allurereport/web-components", () => ({
9
+ ArrowButton: () => <button data-testid="test-result-retries-item-arrow-button" type="button" />,
10
+ IconButton: (props: { "onClick": () => void; "data-testid"?: string }) => (
11
+ <button data-testid={props["data-testid"]} type="button" onClick={props.onClick} />
12
+ ),
13
+ Text: (props: { "children": unknown; "data-testid"?: string }) => (
14
+ <span data-testid={props["data-testid"]}>{props.children}</span>
15
+ ),
16
+ TreeItemIcon: () => <span data-testid="retry-status" />,
17
+ allureIcons: {
18
+ lineArrowsChevronDown: "chevron",
19
+ lineGeneralLinkExternal: "external",
20
+ },
21
+ }));
22
+
23
+ vi.mock("@/components/TestResult/TrError", () => ({
24
+ TrError: (props: { message?: string; trace?: string; actual?: string; expected?: string }) => (
25
+ <div data-testid="test-result-error">{props.message || props.trace || `${props.actual} ${props.expected}`}</div>
26
+ ),
27
+ }));
28
+
29
+ vi.mock("@/stores/locale", () => ({
30
+ useI18n: (namespace: string) =>
31
+ namespace === "controls"
32
+ ? {
33
+ t: (key: string) => (key === "comparison" ? "Comparison" : key),
34
+ }
35
+ : {
36
+ t: (_key: string, params: { attempt: number; total: number }) =>
37
+ `Attempt ${params.attempt} of ${params.total}`,
38
+ },
39
+ }));
40
+
41
+ vi.mock("@/stores/router", () => ({
42
+ navigateToTestResult: vi.fn(),
43
+ }));
44
+
45
+ vi.mock("@/utils/time", () => ({
46
+ timestampToDate: (value: number) => `date:${value}`,
47
+ }));
48
+
49
+ const makeRetry = (overrides: Partial<AwesomeTestResult> = {}): AwesomeTestResult =>
50
+ ({
51
+ id: "retry-id",
52
+ name: "retry",
53
+ status: "failed",
54
+ fullName: "retry.fullName",
55
+ flaky: false,
56
+ muted: false,
57
+ known: false,
58
+ isRetry: true,
59
+ labels: [],
60
+ groupedLabels: {},
61
+ parameters: [],
62
+ links: [],
63
+ steps: [],
64
+ error: undefined,
65
+ environment: "default",
66
+ setup: [],
67
+ teardown: [],
68
+ history: [],
69
+ retries: [],
70
+ breadcrumbs: [],
71
+ titlePath: [],
72
+ attachments: [],
73
+ ...overrides,
74
+ }) as AwesomeTestResult;
75
+
76
+ beforeEach(() => {
77
+ vi.clearAllMocks();
78
+ });
79
+
80
+ describe("components > TestResult > TrRetriesItem", () => {
81
+ it("should render retry error message preview without expanding details", () => {
82
+ render(
83
+ <TrRetriesItem
84
+ attempt={1}
85
+ totalAttempts={2}
86
+ testResultItem={makeRetry({
87
+ stop: 1000,
88
+ duration: 500,
89
+ error: {
90
+ message: "Expected true to be false",
91
+ trace: "stack trace",
92
+ },
93
+ })}
94
+ />,
95
+ );
96
+
97
+ expect(screen.getByTestId("test-result-retries-item-error-preview")).toHaveTextContent("Expected true to be false");
98
+ expect(screen.queryByTestId("test-result-error")).not.toBeInTheDocument();
99
+
100
+ fireEvent.click(screen.getByTestId("test-result-retries-item-arrow-button"));
101
+
102
+ expect(screen.getByTestId("test-result-error")).toHaveTextContent("Expected true to be false");
103
+ });
104
+
105
+ it("should use first meaningful trace line when message is missing", () => {
106
+ render(
107
+ <TrRetriesItem
108
+ attempt={1}
109
+ totalAttempts={2}
110
+ testResultItem={makeRetry({
111
+ error: {
112
+ trace: "\n\nAssertionError: timeout\n at test.ts:1",
113
+ },
114
+ })}
115
+ />,
116
+ );
117
+
118
+ expect(screen.getByTestId("test-result-retries-item-error-preview")).toHaveTextContent("AssertionError: timeout");
119
+ });
120
+
121
+ it("should use trace preview when message is only whitespace", () => {
122
+ render(
123
+ <TrRetriesItem
124
+ attempt={1}
125
+ totalAttempts={2}
126
+ testResultItem={makeRetry({
127
+ error: {
128
+ message: " ",
129
+ trace: "\nAssertionError: retry failed",
130
+ },
131
+ })}
132
+ />,
133
+ );
134
+
135
+ expect(screen.getByTestId("test-result-retries-item-arrow-button")).toBeInTheDocument();
136
+ expect(screen.getByTestId("test-result-retries-item-error-preview")).toHaveTextContent(
137
+ "AssertionError: retry failed",
138
+ );
139
+ });
140
+
141
+ it("should render diff-only retry preview and keep details expandable", () => {
142
+ render(
143
+ <TrRetriesItem
144
+ attempt={1}
145
+ totalAttempts={2}
146
+ testResultItem={makeRetry({
147
+ error: {
148
+ actual: "actual value",
149
+ expected: "expected value",
150
+ },
151
+ })}
152
+ />,
153
+ );
154
+
155
+ expect(screen.getByTestId("test-result-retries-item-arrow-button")).toBeInTheDocument();
156
+ expect(screen.getByTestId("test-result-retries-item-error-preview")).toHaveTextContent("Comparison");
157
+ expect(screen.queryByTestId("test-result-error")).not.toBeInTheDocument();
158
+
159
+ fireEvent.click(screen.getByTestId("test-result-retries-item-arrow-button"));
160
+
161
+ expect(screen.getByTestId("test-result-error")).toHaveTextContent("actual value expected value");
162
+ });
163
+ });
@@ -0,0 +1,108 @@
1
+ import { cleanup, render } from "@testing-library/preact";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ vi.hoisted(() => {
5
+ vi.stubGlobal(
6
+ "matchMedia",
7
+ vi.fn().mockImplementation(() => ({
8
+ matches: false,
9
+ media: "",
10
+ onchange: null,
11
+ addListener: vi.fn(),
12
+ removeListener: vi.fn(),
13
+ addEventListener: vi.fn(),
14
+ removeEventListener: vi.fn(),
15
+ dispatchEvent: vi.fn(),
16
+ })),
17
+ );
18
+ });
19
+
20
+ import type { TrBodyItem } from "@/components/TestResult/bodyItems";
21
+ import { TrSteps } from "@/components/TestResult/TrSteps";
22
+ import { TrStep } from "@/components/TestResult/TrSteps/TrStep";
23
+ import { collapsedTrees, expandedTrees } from "@/stores/tree";
24
+
25
+ const nestedPassedStep = {
26
+ type: "step",
27
+ item: {
28
+ stepId: "nested-passed-step",
29
+ name: "nested passed step",
30
+ status: "passed",
31
+ parameters: [],
32
+ message: "",
33
+ trace: "",
34
+ hasSimilarErrorInSubSteps: false,
35
+ },
36
+ suppressInlineError: false,
37
+ bodyItems: [],
38
+ } satisfies TrBodyItem;
39
+
40
+ const passedStepWithContent = {
41
+ type: "step",
42
+ item: {
43
+ stepId: "passed-step",
44
+ name: "passed step",
45
+ status: "passed",
46
+ parameters: [],
47
+ message: "",
48
+ trace: "",
49
+ hasSimilarErrorInSubSteps: false,
50
+ },
51
+ suppressInlineError: false,
52
+ bodyItems: [nestedPassedStep],
53
+ } satisfies TrBodyItem;
54
+
55
+ const failedStepWithContent = {
56
+ type: "step",
57
+ item: {
58
+ stepId: "failed-step",
59
+ name: "failed step",
60
+ status: "failed",
61
+ parameters: [],
62
+ message: "failed",
63
+ trace: "trace",
64
+ hasSimilarErrorInSubSteps: false,
65
+ },
66
+ suppressInlineError: false,
67
+ bodyItems: [nestedPassedStep],
68
+ } satisfies TrBodyItem;
69
+
70
+ describe("components > TestResult > TrSteps", () => {
71
+ beforeEach(() => {
72
+ cleanup();
73
+ collapsedTrees.value = new Set();
74
+ expandedTrees.value = new Set();
75
+ globalThis.allureReportOptions = { stepTreeExpansion: "expand_failed_only" } as any;
76
+ });
77
+
78
+ it("always shows the steps root container regardless of step status", () => {
79
+ const view = render(<TrSteps id="test" bodyItems={[passedStepWithContent]} />);
80
+
81
+ expect(view.getByTestId("test-result-steps-root")).toBeInTheDocument();
82
+ });
83
+
84
+ it("opens top-level passed steps by default even with expand_failed_only", () => {
85
+ const view = render(<TrStep item={passedStepWithContent} stepIndex={1} isTopLevel={true} />);
86
+
87
+ expect(view.getByTestId("test-result-step-content")).toBeInTheDocument();
88
+ });
89
+
90
+ it("collapses top-level steps when policy is collapsed", () => {
91
+ globalThis.allureReportOptions = { stepTreeExpansion: "collapsed" } as any;
92
+ const view = render(<TrStep item={passedStepWithContent} stepIndex={1} isTopLevel={true} />);
93
+
94
+ expect(view.queryByTestId("test-result-step-content")).not.toBeInTheDocument();
95
+ });
96
+
97
+ it("opens top-level failed steps by default with expand_failed_only", () => {
98
+ const view = render(<TrSteps id="failed-test" bodyItems={[failedStepWithContent]} />);
99
+
100
+ expect(view.getByTestId("test-result-step-content")).toBeInTheDocument();
101
+ });
102
+
103
+ it("collapses nested passed steps by default with expand_failed_only", () => {
104
+ const view = render(<TrStep item={passedStepWithContent} stepIndex={1} isTopLevel={true} />);
105
+
106
+ expect(view.queryAllByTestId("test-result-step-content")).toHaveLength(1);
107
+ });
108
+ });
@@ -1,6 +1,14 @@
1
1
  import type { AttachmentTestStepResult, DefaultTestStepResult } from "@allurereport/core-api";
2
+ import { epic, feature, label, story } from "allure-js-commons";
2
3
  import type { AwesomeFixtureResult, AwesomeTestResult } from "types";
3
- import { describe, expect, it } from "vitest";
4
+ import { beforeEach, describe, expect, it } from "vitest";
5
+
6
+ beforeEach(async () => {
7
+ await epic("coverage");
8
+ await feature("ui-components");
9
+ await story("bodyItems");
10
+ await label("coverage", "ui-components");
11
+ });
4
12
 
5
13
  import {
6
14
  fixtureResultToTrStepItem,
@@ -1,5 +1,13 @@
1
+ import { epic, feature, label, story } from "allure-js-commons";
1
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
3
 
4
+ beforeEach(async () => {
5
+ await epic("coverage");
6
+ await feature("ui-components");
7
+ await story("openPwTraceInNewTab");
8
+ await label("coverage", "ui-components");
9
+ });
10
+
3
11
  describe("components > TestResult > openPlaywrightTraceInNewTab", () => {
4
12
  beforeEach(() => {
5
13
  vi.resetModules();
@@ -1,14 +1,22 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { getNextSubtreeToggleState } from "@allurereport/web-commons";
2
+ import { epic, feature, label, story } from "allure-js-commons";
3
+ import { beforeEach, describe, expect, it } from "vitest";
2
4
 
3
5
  import type { TrBodyItem } from "@/components/TestResult/bodyItems";
4
6
  import {
5
7
  collectExpandableStepNodes,
6
- getNextSubtreeToggleState,
7
8
  hasFailedStepContext,
8
9
  isOpenByDefaultForPolicy,
9
10
  isStepOpenedByDefault,
10
11
  } from "@/components/TestResult/TrSteps/stepTreeExpansion";
11
12
 
13
+ beforeEach(async () => {
14
+ await epic("coverage");
15
+ await feature("ui-components");
16
+ await story("stepTreeExpansion");
17
+ await label("coverage", "ui-components");
18
+ });
19
+
12
20
  describe("components > TestResult > stepTreeExpansion", () => {
13
21
  it("should detect failed context in nested steps", () => {
14
22
  const bodyItems = [
@@ -1,12 +1,20 @@
1
1
  import { signal } from "@preact/signals";
2
2
  import { cleanup, render, screen } from "@testing-library/preact";
3
+ import { epic, feature, label, story } from "allure-js-commons";
3
4
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
5
 
6
+ beforeEach(async () => {
7
+ await epic("coverage");
8
+ await feature("ui-components");
9
+ await story("Timeline");
10
+ await label("coverage", "ui-components");
11
+ });
12
+
5
13
  type TimelineTestResult = {
6
14
  id: string;
7
15
  name: string;
8
16
  status: "passed" | "failed";
9
- hidden: boolean;
17
+ isRetry: boolean;
10
18
  environment: string;
11
19
  environmentName?: string;
12
20
  host: string;
@@ -30,11 +38,11 @@ const setup = async (timelineData: TimelineTestResult[]) => {
30
38
  Timeline: ({ data, dataId }: { data: TimelineTestResult[]; dataId: string }) => (
31
39
  <div data-testid={`timeline-${dataId}`}>{data.map(({ id }) => id).join(",")}</div>
32
40
  ),
33
- Grid: ({ children }: { children: unknown }) => <div>{children}</div>,
34
- GridItem: ({ children }: { children: unknown }) => <div>{children}</div>,
35
- Loadable: ({ renderData }: { renderData: () => JSX.Element }) => renderData(),
41
+ Grid: ({ children }: { children: unknown }) => <div>{children as any}</div>,
42
+ GridItem: ({ children }: { children: unknown }) => <div>{children as any}</div>,
43
+ Loadable: ({ renderData }: { renderData: () => any }) => renderData(),
36
44
  PageLoader: () => <div>loading</div>,
37
- Widget: ({ children }: { children: unknown }) => <div>{children}</div>,
45
+ Widget: ({ children }: { children: unknown }) => <div>{children as any}</div>,
38
46
  }));
39
47
  vi.doMock("@/stores", () => ({
40
48
  useI18n: () => ({
@@ -76,7 +84,7 @@ describe("components > Timeline", () => {
76
84
  id: "tr-qa-a",
77
85
  name: "qa a test",
78
86
  status: "passed",
79
- hidden: false,
87
+ isRetry: false,
80
88
  environment: "qa_a",
81
89
  environmentName: "QA",
82
90
  host: "shared-host",
@@ -88,7 +96,7 @@ describe("components > Timeline", () => {
88
96
  id: "tr-qa-b",
89
97
  name: "qa b test",
90
98
  status: "failed",
91
- hidden: false,
99
+ isRetry: false,
92
100
  environment: "qa_b",
93
101
  environmentName: "QA",
94
102
  host: "shared-host",