@allurereport/web-awesome 3.3.0 → 3.4.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 (209) hide show
  1. package/dist/multi/173.app-648d4a8e7d5405e104a1.js +1 -0
  2. package/dist/multi/174.app-648d4a8e7d5405e104a1.js +1 -0
  3. package/dist/multi/252.app-648d4a8e7d5405e104a1.js +1 -0
  4. package/dist/multi/282.app-648d4a8e7d5405e104a1.js +1 -0
  5. package/dist/multi/29.app-648d4a8e7d5405e104a1.js +1 -0
  6. package/dist/multi/310.app-648d4a8e7d5405e104a1.js +1 -0
  7. package/dist/multi/416.app-648d4a8e7d5405e104a1.js +1 -0
  8. package/dist/multi/507.app-648d4a8e7d5405e104a1.js +1 -0
  9. package/dist/multi/527.app-648d4a8e7d5405e104a1.js +1 -0
  10. package/dist/multi/600.app-648d4a8e7d5405e104a1.js +1 -0
  11. package/dist/multi/605.app-648d4a8e7d5405e104a1.js +1 -0
  12. package/dist/multi/638.app-648d4a8e7d5405e104a1.js +1 -0
  13. package/dist/multi/672.app-648d4a8e7d5405e104a1.js +1 -0
  14. package/dist/multi/686.app-648d4a8e7d5405e104a1.js +1 -0
  15. package/dist/multi/725.app-648d4a8e7d5405e104a1.js +1 -0
  16. package/dist/multi/741.app-648d4a8e7d5405e104a1.js +1 -0
  17. package/dist/multi/749.app-648d4a8e7d5405e104a1.js +1 -0
  18. package/dist/multi/755.app-648d4a8e7d5405e104a1.js +1 -0
  19. package/dist/multi/{894.app-8be6acc0a596a2197dbf.js → 894.app-648d4a8e7d5405e104a1.js} +1 -1
  20. package/dist/multi/943.app-648d4a8e7d5405e104a1.js +1 -0
  21. package/dist/multi/980.app-648d4a8e7d5405e104a1.js +1 -0
  22. package/dist/multi/app-648d4a8e7d5405e104a1.js +2 -0
  23. package/dist/multi/{app-8be6acc0a596a2197dbf.js.LICENSE.txt → app-648d4a8e7d5405e104a1.js.LICENSE.txt} +1 -1
  24. package/dist/multi/manifest.json +23 -21
  25. package/dist/multi/{styles-0b84e1ef76554ad2db9a.css → styles-b72a0161ddf41447b31c.css} +10 -10
  26. package/dist/single/app-ae966ad3dc27579d6c39.js +2 -0
  27. package/dist/single/{app-8221eb856e47b4ef50d6.js.LICENSE.txt → app-ae966ad3dc27579d6c39.js.LICENSE.txt} +1 -1
  28. package/dist/single/manifest.json +1 -1
  29. package/package.json +18 -36
  30. package/src/components/BaseLayout/index.tsx +2 -0
  31. package/src/components/Categories/CategoriesTree/index.tsx +2 -0
  32. package/src/components/Categories/CategoryHeaderItem/index.tsx +2 -0
  33. package/src/components/Categories/CategoryTreeItem/index.tsx +2 -0
  34. package/src/components/Categories/GroupTreeItem/index.tsx +2 -0
  35. package/src/components/Categories/HistoryTreeItem/index.tsx +2 -0
  36. package/src/components/Categories/LabelTreeItem/index.tsx +2 -0
  37. package/src/components/Categories/MessageTreeItem/index.tsx +2 -0
  38. package/src/components/Categories/SeverityTreeItem/index.tsx +2 -0
  39. package/src/components/Charts/index.tsx +3 -0
  40. package/src/components/EnvironmentPicker/index.tsx +78 -18
  41. package/src/components/EnvironmentPicker/styles.scss +19 -0
  42. package/src/components/Footer/FooterLogo.tsx +1 -0
  43. package/src/components/Footer/FooterVersion.tsx +2 -0
  44. package/src/components/Footer/index.tsx +3 -1
  45. package/src/components/Header/CiInfo/index.tsx +17 -3
  46. package/src/components/Header/index.tsx +3 -0
  47. package/src/components/Header/styles.scss +1 -0
  48. package/src/components/HeaderControls/index.tsx +1 -0
  49. package/src/components/MainReport/index.tsx +3 -0
  50. package/src/components/Metadata/index.tsx +2 -0
  51. package/src/components/MetadataButton/index.tsx +35 -8
  52. package/src/components/MetadataButton/styles.scss +20 -0
  53. package/src/components/Modal/index.tsx +2 -0
  54. package/src/components/NavTabs/index.tsx +2 -1
  55. package/src/components/ReportBody/HeaderActions.tsx +1 -0
  56. package/src/components/ReportBody/SortBy.tsx +2 -0
  57. package/src/components/ReportBody/index.tsx +4 -1
  58. package/src/components/ReportCategories/index.tsx +2 -0
  59. package/src/components/ReportFilters/BaseFilters.tsx +2 -0
  60. package/src/components/ReportFilters/CategoriesFilter.tsx +2 -0
  61. package/src/components/ReportFilters/RetryFlaky.tsx +2 -0
  62. package/src/components/ReportFilters/TagsFilter.tsx +2 -0
  63. package/src/components/ReportFilters/TransitionFilter.tsx +2 -0
  64. package/src/components/ReportFilters/index.tsx +3 -0
  65. package/src/components/ReportGlobalAttachments/index.tsx +3 -0
  66. package/src/components/ReportGlobalErrors/index.tsx +2 -0
  67. package/src/components/ReportHeader/ReportHeaderLabelList.tsx +1 -0
  68. package/src/components/ReportHeader/ReportHeaderLogo.tsx +3 -1
  69. package/src/components/ReportHeader/ReportHeaderPie.tsx +2 -0
  70. package/src/components/ReportHeader/index.tsx +2 -0
  71. package/src/components/ReportMetadata/MetadataItem.tsx +1 -0
  72. package/src/components/ReportMetadata/MetadataSummary.tsx +3 -1
  73. package/src/components/ReportMetadata/MetadataTestType.tsx +2 -0
  74. package/src/components/ReportMetadata/MetadataWithIcon.tsx +2 -0
  75. package/src/components/ReportMetadata/index.tsx +61 -17
  76. package/src/components/ReportQualityGateResults/index.tsx +6 -2
  77. package/src/components/ReportSearch/index.tsx +1 -0
  78. package/src/components/ReportTabs/index.tsx +3 -0
  79. package/src/components/SectionPicker/index.tsx +2 -0
  80. package/src/components/SectionSwitcher/index.tsx +3 -0
  81. package/src/components/SideBySide/index.tsx +1 -0
  82. package/src/components/SplitLayout/index.tsx +2 -0
  83. package/src/components/TestResult/TestStepsEmpty/index.tsx +2 -0
  84. package/src/components/TestResult/TrAttachmentsView/index.tsx +2 -0
  85. package/src/components/TestResult/TrDescription/index.tsx +16 -3
  86. package/src/components/TestResult/TrDropdown/index.tsx +1 -0
  87. package/src/components/TestResult/TrEmpty/index.tsx +3 -1
  88. package/src/components/TestResult/TrEnvironmentItem/index.tsx +3 -0
  89. package/src/components/TestResult/TrEnvironmentsView/index.tsx +8 -2
  90. package/src/components/TestResult/TrError/TrDiff.tsx +3 -1
  91. package/src/components/TestResult/TrError/index.tsx +20 -18
  92. package/src/components/TestResult/TrError/styles.scss +0 -25
  93. package/src/components/TestResult/TrHeader/TrBreadcrumbs.tsx +3 -1
  94. package/src/components/TestResult/TrHeader/index.tsx +2 -0
  95. package/src/components/TestResult/TrHeader/styles.scss +1 -0
  96. package/src/components/TestResult/TrHistory/TrHistoryItem.tsx +3 -1
  97. package/src/components/TestResult/TrHistory/index.tsx +2 -0
  98. package/src/components/TestResult/TrInfo/TrInfoStatuses.tsx +2 -0
  99. package/src/components/TestResult/TrInfo/index.tsx +2 -0
  100. package/src/components/TestResult/TrLinks/index.tsx +63 -11
  101. package/src/components/TestResult/TrMetadata/index.tsx +44 -5
  102. package/src/components/TestResult/TrNavigation/index.tsx +2 -0
  103. package/src/components/TestResult/TrOverview.tsx +20 -17
  104. package/src/components/TestResult/TrParameters/index.tsx +41 -7
  105. package/src/components/TestResult/TrPrevStatuses/index.tsx +2 -0
  106. package/src/components/TestResult/TrPwTraces/PwTraceButton.tsx +44 -9
  107. package/src/components/TestResult/TrPwTraces/index.tsx +5 -1
  108. package/src/components/TestResult/TrPwTraces/openPwTraceInNewTab.ts +29 -0
  109. package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +3 -1
  110. package/src/components/TestResult/TrRetriesView/index.tsx +2 -0
  111. package/src/components/TestResult/TrSetup/index.tsx +3 -1
  112. package/src/components/TestResult/TrSeverity/index.tsx +2 -0
  113. package/src/components/TestResult/TrStatus/index.tsx +2 -0
  114. package/src/components/TestResult/TrSteps/TrAttachment.tsx +38 -6
  115. package/src/components/TestResult/TrSteps/TrAttachmentInfo.tsx +50 -2
  116. package/src/components/TestResult/TrSteps/TrBodyItems.tsx +42 -0
  117. package/src/components/TestResult/TrSteps/TrErrorStep.tsx +40 -0
  118. package/src/components/TestResult/TrSteps/TrStep.tsx +31 -65
  119. package/src/components/TestResult/TrSteps/TrStepHeader.tsx +50 -0
  120. package/src/components/TestResult/TrSteps/TrStepInfo.tsx +1 -0
  121. package/src/components/TestResult/TrSteps/index.tsx +12 -32
  122. package/src/components/TestResult/TrSteps/styles.scss +8 -0
  123. package/src/components/TestResult/TrSteps/wrongAttachment.tsx +2 -1
  124. package/src/components/TestResult/TrTabs/index.tsx +1 -0
  125. package/src/components/TestResult/TrTeardown/index.tsx +3 -1
  126. package/src/components/TestResult/bodyItems.ts +151 -0
  127. package/src/components/TestResult/index.tsx +2 -0
  128. package/src/components/Timeline/index.tsx +12 -2
  129. package/src/components/ToggleLayout/index.tsx +1 -0
  130. package/src/components/Tree/index.tsx +6 -2
  131. package/src/components/Tree/styles.scss +11 -0
  132. package/src/index.tsx +9 -3
  133. package/src/locales/ar.json +427 -0
  134. package/src/locales/az.json +43 -46
  135. package/src/locales/de.json +5 -0
  136. package/src/locales/en.json +5 -0
  137. package/src/locales/es.json +5 -0
  138. package/src/locales/fr.json +5 -0
  139. package/src/locales/he.json +5 -0
  140. package/src/locales/hy.json +5 -0
  141. package/src/locales/it.json +5 -0
  142. package/src/locales/ja.json +5 -0
  143. package/src/locales/ka.json +5 -0
  144. package/src/locales/kr.json +5 -0
  145. package/src/locales/nl.json +5 -0
  146. package/src/locales/pl.json +5 -0
  147. package/src/locales/pt.json +5 -0
  148. package/src/locales/ru.json +5 -0
  149. package/src/locales/sv.json +5 -0
  150. package/src/locales/tr.json +5 -0
  151. package/src/locales/uk.json +5 -0
  152. package/src/locales/zh-TW.json +432 -0
  153. package/src/locales/zh.json +5 -0
  154. package/src/stores/categories.ts +1 -0
  155. package/src/stores/chart.ts +2 -1
  156. package/src/stores/env.ts +14 -4
  157. package/src/stores/envInfo.ts +1 -0
  158. package/src/stores/globals.ts +1 -0
  159. package/src/stores/locale.ts +1 -0
  160. package/src/stores/qualityGate.ts +1 -0
  161. package/src/stores/sections.ts +1 -0
  162. package/src/stores/stats.ts +12 -6
  163. package/src/stores/testResult.ts +1 -0
  164. package/src/stores/testResults.ts +1 -0
  165. package/src/stores/timeline.ts +2 -0
  166. package/src/stores/tree.ts +2 -0
  167. package/src/stores/treeFilters/actions.ts +1 -0
  168. package/src/stores/treeFilters/store.ts +1 -0
  169. package/src/stores/treeFilters/utils.ts +1 -0
  170. package/src/stores/variables.ts +1 -0
  171. package/src/utils/time.ts +1 -0
  172. package/src/utils/treeFilters.ts +2 -0
  173. package/test/components/EnvironmentPicker.test.tsx +133 -0
  174. package/test/components/Header/CiInfo.test.tsx +15 -0
  175. package/test/components/Header.test.tsx +1 -0
  176. package/test/components/TestResult/PwTraceButton.test.tsx +104 -0
  177. package/test/components/TestResult/TrErrorStep.test.tsx +127 -0
  178. package/test/components/TestResult/TrOverview.test.tsx +114 -0
  179. package/test/components/TestResult/bodyItems.test.ts +194 -0
  180. package/test/components/TestResult/openPwTraceInNewTab.test.ts +65 -0
  181. package/test/components/Timeline.test.tsx +104 -0
  182. package/test/stores/treeFilters/actions.test.ts +82 -0
  183. package/test/utils/ownerAddress.test.ts +1 -0
  184. package/test/utils/treeFilters.test.ts +1 -0
  185. package/tsconfig.json +1 -2
  186. package/tsconfig.node.json +2 -1
  187. package/types.d.ts +2 -0
  188. package/.eslintrc.cjs +0 -18
  189. package/dist/multi/173.app-8be6acc0a596a2197dbf.js +0 -1
  190. package/dist/multi/174.app-8be6acc0a596a2197dbf.js +0 -1
  191. package/dist/multi/252.app-8be6acc0a596a2197dbf.js +0 -1
  192. package/dist/multi/282.app-8be6acc0a596a2197dbf.js +0 -1
  193. package/dist/multi/29.app-8be6acc0a596a2197dbf.js +0 -1
  194. package/dist/multi/416.app-8be6acc0a596a2197dbf.js +0 -1
  195. package/dist/multi/527.app-8be6acc0a596a2197dbf.js +0 -1
  196. package/dist/multi/600.app-8be6acc0a596a2197dbf.js +0 -1
  197. package/dist/multi/605.app-8be6acc0a596a2197dbf.js +0 -1
  198. package/dist/multi/638.app-8be6acc0a596a2197dbf.js +0 -1
  199. package/dist/multi/672.app-8be6acc0a596a2197dbf.js +0 -1
  200. package/dist/multi/686.app-8be6acc0a596a2197dbf.js +0 -1
  201. package/dist/multi/725.app-8be6acc0a596a2197dbf.js +0 -1
  202. package/dist/multi/741.app-8be6acc0a596a2197dbf.js +0 -1
  203. package/dist/multi/749.app-8be6acc0a596a2197dbf.js +0 -1
  204. package/dist/multi/755.app-8be6acc0a596a2197dbf.js +0 -1
  205. package/dist/multi/943.app-8be6acc0a596a2197dbf.js +0 -1
  206. package/dist/multi/980.app-8be6acc0a596a2197dbf.js +0 -1
  207. package/dist/multi/app-8be6acc0a596a2197dbf.js +0 -2
  208. package/dist/single/app-8221eb856e47b4ef50d6.js +0 -2
  209. package/src/components/TestResult/TrPwTraces/PwTrace.tsx +0 -34
@@ -1,5 +1,6 @@
1
1
  import { fetchReportJsonData } from "@allurereport/web-commons";
2
2
  import { signal } from "@preact/signals";
3
+
3
4
  import type { StoreSignalState } from "@/stores/types";
4
5
 
5
6
  export type Variables = Record<string, any>;
package/src/utils/time.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { getLocaleDateTimeOverride } from "@allurereport/web-commons";
2
+
2
3
  import { currentLocale, currentLocaleIso, useI18n } from "@/stores/locale";
3
4
 
4
5
  const defaultOptions: Intl.DateTimeFormatOptions = {
@@ -11,7 +11,9 @@ import {
11
11
  ordinal,
12
12
  reverse,
13
13
  } from "@allurereport/core-api";
14
+
14
15
  import type { SortBy } from "@/stores/treeSort";
16
+
15
17
  import type { AwesomeRecursiveTree, AwesomeTree, AwesomeTreeGroup, AwesomeTreeLeaf } from "../../types";
16
18
 
17
19
  const leafComparatorByTreeSortBy = (sortBy: SortBy = "status,asc"): Comparator<TreeLeaf<AwesomeTreeLeaf>> => {
@@ -0,0 +1,133 @@
1
+ import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/preact";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ import { currentEnvironment, environmentsStore } from "@/stores/env";
5
+
6
+ const longEnvironmentName = "я".repeat(64);
7
+
8
+ const setElementSizeMocks = () => {
9
+ let isNarrowViewport = false;
10
+ const clientWidthDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "clientWidth");
11
+ const scrollWidthDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "scrollWidth");
12
+
13
+ Object.defineProperty(HTMLElement.prototype, "clientWidth", {
14
+ configurable: true,
15
+ get() {
16
+ const textContent = this.textContent ?? "";
17
+
18
+ if (textContent.includes(longEnvironmentName)) {
19
+ return isNarrowViewport ? 120 : 420;
20
+ }
21
+
22
+ return 200;
23
+ },
24
+ });
25
+
26
+ Object.defineProperty(HTMLElement.prototype, "scrollWidth", {
27
+ configurable: true,
28
+ get() {
29
+ const textContent = this.textContent ?? "";
30
+
31
+ if (textContent.includes(longEnvironmentName)) {
32
+ return 300;
33
+ }
34
+
35
+ return 200;
36
+ },
37
+ });
38
+
39
+ const setNarrowViewport = (isNarrow: boolean) => {
40
+ isNarrowViewport = isNarrow;
41
+ };
42
+
43
+ const restore = () => {
44
+ if (clientWidthDescriptor) {
45
+ Object.defineProperty(HTMLElement.prototype, "clientWidth", clientWidthDescriptor);
46
+ } else {
47
+ delete (HTMLElement.prototype as Partial<HTMLElement>).clientWidth;
48
+ }
49
+
50
+ if (scrollWidthDescriptor) {
51
+ Object.defineProperty(HTMLElement.prototype, "scrollWidth", scrollWidthDescriptor);
52
+ } else {
53
+ delete (HTMLElement.prototype as Partial<HTMLElement>).scrollWidth;
54
+ }
55
+ };
56
+
57
+ return {
58
+ setNarrowViewport,
59
+ restore,
60
+ };
61
+ };
62
+
63
+ describe("components > EnvironmentPicker", () => {
64
+ let setNarrowViewport: (isNarrow: boolean) => void;
65
+ let restoreElementSizeMocks: () => void;
66
+
67
+ beforeEach(() => {
68
+ vi.stubGlobal(
69
+ "matchMedia",
70
+ vi.fn().mockImplementation(() => ({
71
+ matches: false,
72
+ media: "",
73
+ onchange: null,
74
+ addListener: vi.fn(),
75
+ removeListener: vi.fn(),
76
+ addEventListener: vi.fn(),
77
+ removeEventListener: vi.fn(),
78
+ dispatchEvent: vi.fn(),
79
+ })),
80
+ );
81
+ const controls = setElementSizeMocks();
82
+ setNarrowViewport = controls.setNarrowViewport;
83
+ restoreElementSizeMocks = controls.restore;
84
+ currentEnvironment.value = "long-environment";
85
+ environmentsStore.value = {
86
+ loading: false,
87
+ error: undefined,
88
+ data: [
89
+ { id: "default", name: "default" },
90
+ { id: "long-environment", name: longEnvironmentName },
91
+ ],
92
+ };
93
+ });
94
+
95
+ afterEach(() => {
96
+ restoreElementSizeMocks();
97
+ vi.unstubAllGlobals();
98
+ cleanup();
99
+ });
100
+
101
+ it("should show tooltip on hover after resize truncates selected value", async () => {
102
+ const { EnvironmentPicker } = await import("@/components/EnvironmentPicker");
103
+
104
+ render(<EnvironmentPicker />);
105
+ const getPickerButton = () => screen.getByTestId("environment-picker-button");
106
+ const getTooltipTrigger = () => getPickerButton().parentElement?.parentElement as HTMLElement;
107
+ const queryTooltip = () => screen.queryByTestId("environment-picker-selected-tooltip");
108
+
109
+ expect(queryTooltip()).not.toBeInTheDocument();
110
+
111
+ setNarrowViewport(true);
112
+ await waitFor(() => {
113
+ fireEvent(window, new Event("resize"));
114
+ expect(getTooltipTrigger().childElementCount).toBe(2);
115
+ });
116
+
117
+ const tooltipTrigger = getTooltipTrigger();
118
+ fireEvent.mouseEnter(tooltipTrigger);
119
+ await waitFor(() => {
120
+ expect(queryTooltip()).toBeInTheDocument();
121
+ });
122
+
123
+ fireEvent.mouseLeave(tooltipTrigger);
124
+ await waitFor(() => {
125
+ expect(queryTooltip()).not.toBeInTheDocument();
126
+ });
127
+
128
+ fireEvent.click(getPickerButton());
129
+ fireEvent.mouseEnter(getTooltipTrigger());
130
+
131
+ expect(queryTooltip()).not.toBeInTheDocument();
132
+ }, 15000);
133
+ });
@@ -3,6 +3,7 @@ import { getReportOptions } from "@allurereport/web-commons";
3
3
  import { cleanup, render, screen } from "@testing-library/preact";
4
4
  import { h } from "preact";
5
5
  import { type Mock, beforeEach, describe, expect, it, vi } from "vitest";
6
+
6
7
  import { CiInfo } from "@/components/Header/CiInfo";
7
8
 
8
9
  const fixtures = {
@@ -106,6 +107,7 @@ describe("components > Header > CiInfo", () => {
106
107
  render(<CiInfo />);
107
108
 
108
109
  expect(screen.getByRole("link")).toHaveAttribute("href", fixtures.pullRequestUrl);
110
+ expect(screen.getByRole("link")).toHaveAttribute("rel", "noopener noreferrer");
109
111
  });
110
112
 
111
113
  it("should presence use job run url as href when provided", () => {
@@ -133,6 +135,19 @@ describe("components > Header > CiInfo", () => {
133
135
  expect(screen.getByRole("link")).toHaveAttribute("href", fixtures.jobRunUrl);
134
136
  });
135
137
 
138
+ it("should not render a clickable link when ci url has unsafe protocol", () => {
139
+ (getReportOptions as Mock).mockReturnValueOnce({
140
+ ci: {
141
+ pullRequestUrl: "javascript:alert(1)",
142
+ pullRequestName: fixtures.pullRequestName,
143
+ },
144
+ });
145
+
146
+ render(<CiInfo />);
147
+
148
+ expect(screen.queryByRole("link")).not.toBeInTheDocument();
149
+ });
150
+
136
151
  it("should presence pull request name as text when provided", () => {
137
152
  (getReportOptions as Mock).mockReturnValueOnce({
138
153
  ci: {
@@ -2,6 +2,7 @@ import * as webCommons from "@allurereport/web-commons";
2
2
  import { signal } from "@preact/signals";
3
3
  import { cleanup, render, screen } from "@testing-library/preact";
4
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
5
+
5
6
  import { Header } from "@/components/Header";
6
7
  import { CiInfo } from "@/components/Header/CiInfo";
7
8
  import type * as routerModule from "@/stores/router";
@@ -0,0 +1,104 @@
1
+ import { fireEvent, render, screen, waitFor } from "@testing-library/preact";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ type SetupOptions = {
5
+ canOpenInNewTab: boolean;
6
+ failFetch?: boolean;
7
+ };
8
+
9
+ const setup = async ({ canOpenInNewTab, failFetch }: SetupOptions) => {
10
+ vi.resetModules();
11
+
12
+ const fetchFromUrl = failFetch
13
+ ? vi.fn().mockRejectedValue(new Error("fetch failed"))
14
+ : vi.fn().mockResolvedValue({
15
+ blob: vi.fn().mockResolvedValue(new Blob(["trace"])),
16
+ });
17
+ const openModal = vi.fn();
18
+ const closeModal = vi.fn();
19
+ const openPlaywrightTraceInNewTab = vi.fn().mockReturnValue(canOpenInNewTab);
20
+
21
+ vi.doMock("@allurereport/web-commons", () => ({
22
+ fetchFromUrl,
23
+ }));
24
+ vi.doMock("@allurereport/web-components", () => ({
25
+ Button: ({ onClick, text }: { onClick?: () => void; text?: string }) => <button onClick={onClick}>{text}</button>,
26
+ Text: ({ children }: { children: unknown }) => <div>{children}</div>,
27
+ TooltipWrapper: ({ children }: { children: unknown }) => <div>{children}</div>,
28
+ IconButton: ({ onClick }: { onClick?: () => void }) => <button onClick={onClick}>open trace</button>,
29
+ allureIcons: { lineArrowsExpand3: "lineArrowsExpand3" },
30
+ }));
31
+ vi.doMock("@/stores", () => ({
32
+ useI18n: () => ({ t: (key: string) => key }),
33
+ }));
34
+ vi.doMock("@/stores/modal", () => ({
35
+ openModal,
36
+ closeModal,
37
+ }));
38
+ vi.doMock("@/components/TestResult/TrPwTraces/openPwTraceInNewTab", () => ({ openPlaywrightTraceInNewTab }));
39
+
40
+ const { PwTraceButton } = await import("@/components/TestResult/TrPwTraces/PwTraceButton");
41
+
42
+ render(
43
+ <PwTraceButton
44
+ link={{
45
+ id: "trace-id",
46
+ ext: ".zip",
47
+ name: "trace",
48
+ contentType: "application/vnd.allure.playwright-trace",
49
+ }}
50
+ />,
51
+ );
52
+
53
+ fireEvent.click(screen.getByRole("button", { name: "open trace" }));
54
+
55
+ return { fetchFromUrl, openModal, closeModal, openPlaywrightTraceInNewTab };
56
+ };
57
+
58
+ describe("components > TestResult > PwTraceButton", () => {
59
+ beforeEach(() => {
60
+ vi.clearAllMocks();
61
+ });
62
+
63
+ it("opens trace viewer in new tab and does not open modal", async () => {
64
+ const { openModal, openPlaywrightTraceInNewTab } = await setup({
65
+ canOpenInNewTab: true,
66
+ });
67
+
68
+ await waitFor(() => {
69
+ expect(openPlaywrightTraceInNewTab).toHaveBeenCalledTimes(1);
70
+ });
71
+
72
+ expect(openModal).not.toHaveBeenCalled();
73
+ });
74
+
75
+ it("shows popup-blocked modal when new tab cannot be opened", async () => {
76
+ const { openModal, openPlaywrightTraceInNewTab } = await setup({
77
+ canOpenInNewTab: false,
78
+ });
79
+
80
+ await waitFor(() => {
81
+ expect(openModal).toHaveBeenCalledTimes(1);
82
+ });
83
+
84
+ expect(openPlaywrightTraceInNewTab).toHaveBeenCalledTimes(1);
85
+ expect(openModal).toHaveBeenCalledWith(
86
+ expect.objectContaining({
87
+ title: "Playwright Trace Viewer | trace.zip",
88
+ }),
89
+ );
90
+ });
91
+
92
+ it("shows error modal when trace attachment fetch fails", async () => {
93
+ const { openModal, openPlaywrightTraceInNewTab } = await setup({
94
+ canOpenInNewTab: true,
95
+ failFetch: true,
96
+ });
97
+
98
+ await waitFor(() => {
99
+ expect(openModal).toHaveBeenCalledTimes(1);
100
+ });
101
+
102
+ expect(openPlaywrightTraceInNewTab).not.toHaveBeenCalled();
103
+ });
104
+ });
@@ -0,0 +1,127 @@
1
+ import { cleanup, fireEvent, render } from "@testing-library/preact";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ const { matchMediaMock } = vi.hoisted(() => {
5
+ const matchMediaMock = vi.fn().mockImplementation(() => ({
6
+ matches: false,
7
+ media: "",
8
+ onchange: null,
9
+ addListener: vi.fn(),
10
+ removeListener: vi.fn(),
11
+ addEventListener: vi.fn(),
12
+ removeEventListener: vi.fn(),
13
+ dispatchEvent: vi.fn(),
14
+ }));
15
+
16
+ vi.stubGlobal("matchMedia", matchMediaMock);
17
+
18
+ return { matchMediaMock };
19
+ });
20
+
21
+ import { TrErrorStep } from "@/components/TestResult/TrSteps/TrErrorStep";
22
+ import { collapsedTrees } from "@/stores/tree";
23
+
24
+ const mockTrError = vi.fn(({ message }: { message?: string }) => <div data-testid="error-content">{message}</div>);
25
+
26
+ vi.mock("@/components/TestResult/TrError", () => ({
27
+ TrError: (props: { message?: string; showMessage?: boolean }) => mockTrError(props),
28
+ }));
29
+
30
+ describe("components > TestResult > TrErrorStep", () => {
31
+ beforeEach(() => {
32
+ cleanup();
33
+ mockTrError.mockClear();
34
+ matchMediaMock.mockClear();
35
+ collapsedTrees.value = new Set();
36
+ });
37
+
38
+ it("should render the title and pass showMessage=false to TrError", () => {
39
+ const view = render(
40
+ <TrErrorStep
41
+ stepIndex={2}
42
+ item={{
43
+ type: "error",
44
+ id: "__test-error__:test-result-id",
45
+ title: "boom",
46
+ status: "failed",
47
+ error: {
48
+ message: "boom\nsecond line",
49
+ trace: "error trace",
50
+ },
51
+ }}
52
+ />,
53
+ );
54
+
55
+ expect(view.getByTestId("test-result-step-title")).toHaveTextContent("boom");
56
+ expect(mockTrError.mock.calls[0]?.[0]).toMatchObject({
57
+ message: "boom\nsecond line",
58
+ trace: "error trace",
59
+ showMessage: false,
60
+ status: "failed",
61
+ });
62
+ });
63
+
64
+ it("should be expanded by default when the error has trace or diff content", () => {
65
+ const view = render(
66
+ <TrErrorStep
67
+ stepIndex={1}
68
+ item={{
69
+ type: "error",
70
+ id: "expanded-test-id",
71
+ title: "error with trace",
72
+ status: "failed",
73
+ error: {
74
+ message: "error with trace",
75
+ trace: "error trace",
76
+ },
77
+ }}
78
+ />,
79
+ );
80
+
81
+ expect(view.getByTestId("test-result-step-content")).toBeInTheDocument();
82
+ });
83
+
84
+ it("should collapse the content when the header is clicked", () => {
85
+ const view = render(
86
+ <TrErrorStep
87
+ stepIndex={1}
88
+ item={{
89
+ type: "error",
90
+ id: "collapsible-test-id",
91
+ title: "collapsible error",
92
+ status: "broken",
93
+ error: {
94
+ message: "collapsible error",
95
+ trace: "some trace",
96
+ },
97
+ }}
98
+ />,
99
+ );
100
+
101
+ expect(view.getByTestId("error-content")).toBeInTheDocument();
102
+
103
+ fireEvent.click(view.getByTestId("test-result-step-header"));
104
+
105
+ expect(view.queryByTestId("error-content")).not.toBeInTheDocument();
106
+ });
107
+
108
+ it("shouldn't render an arrow or content when the error has only a message", () => {
109
+ const view = render(
110
+ <TrErrorStep
111
+ stepIndex={1}
112
+ item={{
113
+ type: "error",
114
+ id: "no-content-id",
115
+ title: "only message",
116
+ status: "failed",
117
+ error: {
118
+ message: "only message",
119
+ },
120
+ }}
121
+ />,
122
+ );
123
+
124
+ expect(view.queryByTestId("test-result-step-arrow-button")).not.toBeInTheDocument();
125
+ expect(view.queryByTestId("test-result-step-content")).not.toBeInTheDocument();
126
+ });
127
+ });
@@ -0,0 +1,114 @@
1
+ import { cleanup, render, screen } from "@testing-library/preact";
2
+ import type { AwesomeTestResult } from "types";
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+
5
+ import { TrOverview } from "@/components/TestResult/TrOverview";
6
+
7
+ vi.mock("@/components/TestResult/TestStepsEmpty", () => ({
8
+ default: () => <div data-testid="test-steps-empty" />,
9
+ }));
10
+
11
+ vi.mock("@/components/TestResult/TrSteps", () => ({
12
+ TrSteps: () => <div data-testid="tr-steps" />,
13
+ }));
14
+
15
+ vi.mock("@/components/TestResult/TrDescription", () => ({
16
+ TrDescription: () => null,
17
+ }));
18
+
19
+ vi.mock("@/components/TestResult/TrLinks", () => ({
20
+ TrLinks: () => null,
21
+ }));
22
+
23
+ vi.mock("@/components/TestResult/TrMetadata", () => ({
24
+ TrMetadata: () => null,
25
+ }));
26
+
27
+ vi.mock("@/components/TestResult/TrParameters", () => ({
28
+ TrParameters: () => null,
29
+ }));
30
+
31
+ vi.mock("@/components/TestResult/TrPwTraces", () => ({
32
+ TrPwTraces: () => null,
33
+ }));
34
+
35
+ vi.mock("@/components/TestResult/TrSetup", () => ({
36
+ TrSetup: () => null,
37
+ }));
38
+
39
+ vi.mock("@/components/TestResult/TrTeardown", () => ({
40
+ TrTeardown: () => null,
41
+ }));
42
+
43
+ vi.mock("@/stores/locale", () => ({
44
+ useI18n: () => ({
45
+ t: (key: string) => (key === "error" ? "Error" : key),
46
+ }),
47
+ }));
48
+
49
+ vi.mock("@/stores/testResult", () => ({
50
+ currentTrId: { value: "current-test-result-id" },
51
+ }));
52
+
53
+ const makeTestResult = (overrides: Partial<AwesomeTestResult> = {}): AwesomeTestResult =>
54
+ ({
55
+ id: "test-result-id",
56
+ name: "test",
57
+ status: "failed",
58
+ fullName: "test.fullName",
59
+ flaky: false,
60
+ muted: false,
61
+ known: false,
62
+ hidden: false,
63
+ labels: [],
64
+ groupedLabels: {},
65
+ parameters: [],
66
+ links: [],
67
+ steps: [],
68
+ error: undefined,
69
+ descriptionHtml: undefined,
70
+ testCase: undefined,
71
+ environment: "default",
72
+ setup: [],
73
+ teardown: [],
74
+ history: [],
75
+ retries: [],
76
+ breadcrumbs: [],
77
+ retry: false,
78
+ titlePath: [],
79
+ attachments: [],
80
+ ...overrides,
81
+ }) as AwesomeTestResult;
82
+
83
+ describe("components > TestResult > TrOverview", () => {
84
+ beforeEach(() => {
85
+ cleanup();
86
+ });
87
+
88
+ it("should render TrSteps when the test has steps", () => {
89
+ render(
90
+ <TrOverview
91
+ testResult={makeTestResult({
92
+ steps: [{ type: "step", name: "step", status: "passed", parameters: [], steps: [] }],
93
+ })}
94
+ />,
95
+ );
96
+
97
+ expect(screen.getByTestId("tr-steps")).toBeInTheDocument();
98
+ expect(screen.queryByTestId("test-steps-empty")).not.toBeInTheDocument();
99
+ });
100
+
101
+ it("should render TrSteps when the test has an error", () => {
102
+ render(<TrOverview testResult={makeTestResult({ error: { message: "boom" } })} />);
103
+
104
+ expect(screen.getByTestId("tr-steps")).toBeInTheDocument();
105
+ expect(screen.queryByTestId("test-steps-empty")).not.toBeInTheDocument();
106
+ });
107
+
108
+ it("should render the empty state when there are no body items", () => {
109
+ render(<TrOverview testResult={makeTestResult()} />);
110
+
111
+ expect(screen.getByTestId("test-steps-empty")).toBeInTheDocument();
112
+ expect(screen.queryByTestId("tr-steps")).not.toBeInTheDocument();
113
+ });
114
+ });