@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
@@ -2,7 +2,26 @@ import { getReportOptions } from "@allurereport/web-commons";
2
2
  import { computed, signal } from "@preact/signals";
3
3
  import type { AwesomeReportOptions, Layout } from "types";
4
4
 
5
- export const layoutStore = signal<Layout>("base");
5
+ const reportOptions = getReportOptions<AwesomeReportOptions>();
6
+
7
+ const DEFAULT_LAYOUT = "base";
8
+
9
+ const getInitialLayout = (): Layout => {
10
+ if (typeof window === "undefined") {
11
+ return DEFAULT_LAYOUT;
12
+ }
13
+
14
+ const lsLayout = localStorage.getItem("layout") as Layout | null;
15
+
16
+ if (lsLayout !== null) {
17
+ return lsLayout;
18
+ }
19
+
20
+ return lsLayout ?? reportOptions?.layout ?? DEFAULT_LAYOUT;
21
+ };
22
+
23
+ export const layoutStore = signal<Layout>(getInitialLayout());
24
+
6
25
  export const isLayoutLoading = signal(false);
7
26
 
8
27
  export const setLayout = (newLayout: Layout): void => {
@@ -24,13 +43,3 @@ export const toggleLayout = () => {
24
43
  };
25
44
 
26
45
  export const isSplitMode = computed(() => layoutStore.value === "split");
27
-
28
- export const getLayout = () => {
29
- const { layout } = getReportOptions<AwesomeReportOptions>() ?? {};
30
- const layoutFromLS = window.localStorage.getItem("layout") || (layout as Layout) || "base";
31
-
32
- if (layoutFromLS) {
33
- setLayout(layoutFromLS as Layout);
34
- return;
35
- }
36
- };
@@ -1,5 +1,11 @@
1
1
  import { formatDuration } from "@allurereport/core-api";
2
- import { DEFAULT_LOCALE, LANG_LOCALE, type LangLocale, getReportOptions } from "@allurereport/web-commons";
2
+ import {
3
+ DEFAULT_LOCALE,
4
+ LANG_LOCALE,
5
+ type LangLocale,
6
+ getLocaleDateTimeOverride,
7
+ getReportOptions,
8
+ } from "@allurereport/web-commons";
3
9
  import { computed, signal } from "@preact/signals";
4
10
  import i18next, { type TOptions } from "i18next";
5
11
  import type { AwesomeReportOptions } from "types";
@@ -8,7 +14,6 @@ const namespaces = [
8
14
  "empty",
9
15
  "execution",
10
16
  "filters",
11
- "filters.description",
12
17
  "search",
13
18
  "severity",
14
19
  "sort-by",
@@ -28,6 +33,8 @@ const namespaces = [
28
33
  "sections",
29
34
  "timeline",
30
35
  "transitions",
36
+ "trHistory",
37
+ "attachments",
31
38
  ] as const;
32
39
 
33
40
  export const currentLocale = signal<LangLocale>("en" as LangLocale);
@@ -49,7 +56,8 @@ export const waitForI18next = i18next
49
56
  namespace: string,
50
57
  callback: (errorValue: unknown, translations: null) => void,
51
58
  ) => {
52
- await import(`@/locales/${language}.json`)
59
+ const loadLocale = language === "en-iso" ? "en" : language;
60
+ await import(`@/locales/${loadLocale}.json`)
53
61
  .then((resources: Record<string, null>) => {
54
62
  callback(null, resources[namespace]);
55
63
  })
@@ -65,43 +73,68 @@ export const waitForI18next = i18next
65
73
  interpolation: { escapeValue: false },
66
74
  })
67
75
  .then(() => {
68
- i18next.services.formatter.add("capitalize", (value) => {
76
+ i18next.services.formatter.add("capitalize", (value: string) => {
69
77
  return value.charAt(0).toLocaleUpperCase() + value.slice(1);
70
78
  });
71
- i18next.services.formatter.add("timestamp_date", (value: number, lng, options) => {
72
- const formatter = new Intl.DateTimeFormat(lng, {
73
- ...options,
74
- month: "numeric",
75
- day: "numeric",
76
- year: "numeric",
77
- });
78
- return formatter.format(value);
79
- });
80
- i18next.services.formatter.add("timestamp_long", (value: number, lng, options) => {
81
- const formatter = new Intl.DateTimeFormat(lng, {
82
- ...options,
83
- month: "numeric",
84
- day: "numeric",
85
- year: "numeric",
86
- hour: "numeric",
87
- minute: "numeric",
88
- second: "numeric",
89
- hour12: false,
90
- });
91
- return formatter.format(value).replace(",", ` ${i18next.t("ui:at")}`);
92
- });
93
- i18next.services.formatter.add("timestamp_long_no_seconds", (value: number, lng, options) => {
94
- const formatter = new Intl.DateTimeFormat(lng, {
95
- ...options,
96
- month: "numeric",
97
- day: "numeric",
98
- year: "numeric",
99
- hour: "numeric",
100
- minute: "numeric",
101
- hour12: false,
102
- });
103
- return formatter.format(value).replace(",", ` ${i18next.t("ui:at")}`);
104
- });
79
+ i18next.services.formatter.add(
80
+ "timestamp_date",
81
+ (value: number, lng: string, options?: Intl.DateTimeFormatOptions) => {
82
+ const override = getLocaleDateTimeOverride(lng, "date");
83
+ const formatter = new Intl.DateTimeFormat(override?.locale ?? lng, {
84
+ ...options,
85
+ month: "numeric",
86
+ day: "numeric",
87
+ year: "numeric",
88
+ ...(override?.options ?? {}),
89
+ });
90
+ const formatted = formatter.format(value);
91
+ return override?.stripComma ? formatted.replace(",", "") : formatted;
92
+ },
93
+ );
94
+
95
+ i18next.services.formatter.add(
96
+ "timestamp_long",
97
+ (value: number, lng: string, options?: Intl.DateTimeFormatOptions) => {
98
+ const override = getLocaleDateTimeOverride(lng, "dateTime");
99
+ const formatter = new Intl.DateTimeFormat(override?.locale ?? lng, {
100
+ ...options,
101
+ month: "numeric",
102
+ day: "numeric",
103
+ year: "numeric",
104
+ hour: "numeric",
105
+ minute: "numeric",
106
+ second: "numeric",
107
+ hour12: false,
108
+ ...(override?.options ?? {}),
109
+ });
110
+ const formatted = formatter.format(value);
111
+ if (override?.includeAtSeparator === false || override?.stripComma) {
112
+ return formatted.replace(",", "");
113
+ }
114
+ return formatted.replace(",", ` ${i18next.t("ui:at")}`);
115
+ },
116
+ );
117
+ i18next.services.formatter.add(
118
+ "timestamp_long_no_seconds",
119
+ (value: number, lng: string, options?: Intl.DateTimeFormatOptions) => {
120
+ const override = getLocaleDateTimeOverride(lng, "dateTimeNoSeconds");
121
+ const formatter = new Intl.DateTimeFormat(override?.locale ?? lng, {
122
+ ...options,
123
+ month: "numeric",
124
+ day: "numeric",
125
+ year: "numeric",
126
+ hour: "numeric",
127
+ minute: "numeric",
128
+ hour12: false,
129
+ ...(override?.options ?? {}),
130
+ });
131
+ const formatted = formatter.format(value);
132
+ if (override?.includeAtSeparator === false || override?.stripComma) {
133
+ return formatted.replace(",", "");
134
+ }
135
+ return formatted.replace(",", ` ${i18next.t("ui:at")}`);
136
+ },
137
+ );
105
138
  i18next.services.formatter.add("format_duration", (value: number) => {
106
139
  return formatDuration(value);
107
140
  });
@@ -1,9 +1,9 @@
1
- import { QualityGateValidationResult } from "@allurereport/plugin-api";
1
+ import type { QualityGateValidationResult } from "@allurereport/plugin-api";
2
2
  import { fetchReportJsonData } from "@allurereport/web-commons";
3
3
  import { signal } from "@preact/signals";
4
4
  import { type StoreSignalState } from "./types";
5
5
 
6
- export const qualityGateStore = signal<StoreSignalState<QualityGateValidationResult[]>>({
6
+ export const qualityGateStore = signal<StoreSignalState<Record<string, QualityGateValidationResult[]>>>({
7
7
  loading: true,
8
8
  error: undefined,
9
9
  data: undefined,
@@ -11,7 +11,7 @@ export const qualityGateStore = signal<StoreSignalState<QualityGateValidationRes
11
11
 
12
12
  export const fetchQualityGateResults = async () => {
13
13
  try {
14
- const data = await fetchReportJsonData<QualityGateValidationResult[]>("widgets/quality-gate.json");
14
+ const data = await fetchReportJsonData<Record<string, QualityGateValidationResult[]>>("widgets/quality-gate.json");
15
15
 
16
16
  qualityGateStore.value = {
17
17
  data,
@@ -20,7 +20,7 @@ export const fetchQualityGateResults = async () => {
20
20
  };
21
21
  } catch (err) {
22
22
  qualityGateStore.value = {
23
- ...qualityGateStore.value,
23
+ ...qualityGateStore.peek(),
24
24
  error: err.message,
25
25
  loading: false,
26
26
  };
@@ -1,108 +1,42 @@
1
- import { computed, signal } from "@preact/signals";
1
+ import { createRoute, navigateTo as routerNavigateTo } from "@allurereport/web-commons";
2
+ import { computed } from "@preact/signals";
2
3
 
3
- type NavigateToObject = {
4
- category?: string;
5
- params?: {
6
- testResultId?: string | null;
7
- subTab?: string | null;
8
- };
4
+ export const navigateToTestResult = (params: { testResultId: string; tab?: string }) => {
5
+ routerNavigateTo({ path: "/:testResultId/:tab?", params, keepSearchParams: true });
9
6
  };
10
7
 
11
- type Route = {
12
- category?: string;
13
- params?: {
14
- testResultId?: string | null;
15
- subTab?: string | null;
16
- };
8
+ export const navigateToTestResultTab = (params: { testResultId: string; tab: string }) => {
9
+ routerNavigateTo({ path: "/:testResultId/:tab?", params, keepSearchParams: true, replace: true });
17
10
  };
18
11
 
19
- export const parseHash = (): Route => {
20
- const hash = globalThis.location.hash.replace(/^#/, "").trim();
21
- const parts = hash.split("/").filter(Boolean);
22
- const [first, second] = parts;
23
-
24
- if (parts.length === 0) {
25
- return {};
26
- }
27
-
28
- if (parts.length === 1) {
29
- if (/^[a-f0-9]{32,}$/.test(first)) {
30
- return { params: { testResultId: first } };
31
- }
32
- return { category: first || "", params: { testResultId: second } };
33
- }
34
-
35
- if (parts.length === 2) {
36
- if (/^[a-f0-9]{32,}$/.test(first)) {
37
- return {
38
- params: {
39
- testResultId: first,
40
- subTab: second,
41
- },
42
- };
43
- }
44
-
45
- return {
46
- category: first,
47
- params: {
48
- testResultId: second,
49
- },
50
- };
51
- }
52
-
53
- if (parts.length === 3) {
54
- const [category, testResultId, subTab] = parts;
55
- return { category, params: { testResultId, subTab } };
56
- }
57
-
58
- return {};
12
+ export const navigateToRoot = () => {
13
+ routerNavigateTo({ path: "/", keepSearchParams: true });
59
14
  };
60
15
 
61
- export const route = signal<Route>(parseHash());
62
-
63
- export const handleHashChange = () => {
64
- const newRoute = parseHash();
65
-
66
- if (
67
- newRoute.category !== route.value?.category ||
68
- newRoute.params?.testResultId !== route.value.params?.testResultId ||
69
- newRoute.params?.subTab !== route.value.params?.subTab
70
- ) {
71
- route.value = { ...newRoute };
72
- }
16
+ export const navigateToSection = (params: { section: "timeline" | "charts" }) => {
17
+ routerNavigateTo({ path: "/:section", params, keepSearchParams: true, replace: false });
73
18
  };
74
19
 
75
- export const navigateTo = (path: NavigateToObject | string) => {
76
- let newHash = "";
20
+ const sections = ["charts", "timeline"];
77
21
 
78
- if (typeof path === "string") {
79
- newHash = path.startsWith("#") ? path.slice(1) : path;
80
- } else {
81
- const { category, params = {} } = path;
82
- const parts: string[] = [];
22
+ export const testResultRoute = computed(() =>
23
+ createRoute<{ testResultId: string; tab?: string }>("/:testResultId/:tab?", ({ params }) => {
24
+ return params.testResultId && !sections.includes(params.testResultId);
25
+ }),
26
+ );
83
27
 
84
- if (category) {
85
- parts.push(category);
86
- }
28
+ export const rootRoute = computed(() => createRoute<{}>("/"));
87
29
 
88
- if (params.testResultId) {
89
- parts.push(params.testResultId);
90
- }
30
+ export const sectionRoute = computed(() =>
31
+ createRoute<{ section: "timeline" | "charts" }>("/:section", ({ params }) => {
32
+ return sections.includes(params.section);
33
+ }),
34
+ );
91
35
 
92
- if (params.subTab) {
93
- parts.push(params.subTab);
94
- }
95
-
96
- newHash = parts.join("/");
36
+ export const openInNewTab = (path: string) => {
37
+ if (typeof window === "undefined") {
38
+ return;
97
39
  }
98
40
 
99
- history.pushState(null, "", `#${newHash}`);
100
- handleHashChange();
101
- };
102
-
103
- export const openInNewTab = (path: string) => {
104
41
  window.open(`#${path}`, "_blank");
105
42
  };
106
-
107
- export const activeTab = computed(() => route.value.category || "");
108
- export const activeSubTab = computed(() => route.value.params?.subTab || "overview");
@@ -1,63 +1,50 @@
1
1
  import { getReportOptions } from "@allurereport/web-commons";
2
- import { effect, signal } from "@preact/signals";
3
- import { navigateTo, parseHash, route } from "@/stores/router";
2
+ import { computed, effect } from "@preact/signals";
4
3
  import type { AwesomeReportOptions } from "../../types.js";
4
+ import { navigateToRoot, navigateToSection, sectionRoute } from "./router";
5
5
 
6
6
  const DEFAULT_SECTION = "default";
7
- type Section = string;
8
7
 
9
- export const currentSection = signal<Section>("");
10
- export const availableSections = signal<Section[]>([]);
8
+ type Section = "timeline" | "charts" | "default";
11
9
 
12
- const updateSectionState = (section: Section): void => {
13
- currentSection.value = section;
14
- document.documentElement.setAttribute("data-section", section);
15
- globalThis.localStorage.setItem("chosenSection", section);
16
- };
10
+ const reportOptions = getReportOptions<AwesomeReportOptions>();
11
+ const defaultSectionFromReportOptions: Section = (reportOptions?.defaultSection as Section) ?? "default";
17
12
 
18
- export const setSection = (chosenSection: Section): void => {
19
- const isDefaultSection = chosenSection === DEFAULT_SECTION;
20
- const isValidSection = availableSections.value?.includes(chosenSection);
21
- const isSectionChanged = currentSection.value !== chosenSection;
13
+ export const availableSections = (reportOptions?.sections ?? []) as Section[];
22
14
 
23
- updateSectionState(chosenSection);
15
+ const onInit = () => {
16
+ const isSectionRoute = sectionRoute.peek().matches;
17
+ const isDefaultSection = defaultSectionFromReportOptions === DEFAULT_SECTION;
18
+ const isValidSection = availableSections.includes(defaultSectionFromReportOptions);
24
19
 
25
- if (isDefaultSection) {
26
- navigateTo({ category: "" });
27
- return;
28
- }
29
-
30
- if (isSectionChanged && isValidSection) {
31
- navigateTo({ category: chosenSection });
20
+ if (!isSectionRoute && !isDefaultSection && isValidSection) {
21
+ navigateToSection({ section: defaultSectionFromReportOptions });
32
22
  }
33
23
  };
34
24
 
35
- export const getSection = () => {
36
- const { category } = parseHash();
37
- availableSections.value = getReportOptions<AwesomeReportOptions>()?.sections ?? [];
38
- const defaultSectionFromReportOptions = getReportOptions<AwesomeReportOptions>()?.defaultSection ?? "";
39
- const sectionFromUrl = parseHash().category;
40
- const sectionFromLS =
41
- globalThis.localStorage.getItem("chosenSection") === ""
42
- ? ""
43
- : globalThis.localStorage.getItem("chosenSection") || defaultSectionFromReportOptions;
44
- currentSection.value = sectionFromUrl || sectionFromLS;
45
-
46
- if (category) {
47
- setSection(category);
48
- return;
25
+ onInit();
26
+
27
+ export const currentSection = computed(() => sectionRoute.value.params.section ?? "default");
28
+
29
+ effect(() => {
30
+ const section = currentSection.value;
31
+
32
+ if (section) {
33
+ document.documentElement.setAttribute("data-section", section);
49
34
  }
35
+ });
36
+
37
+ export const setSection = (chosenSection: Section | string): void => {
38
+ const isDefaultSection = chosenSection === DEFAULT_SECTION;
39
+ const isValidSection = availableSections.includes(chosenSection as Section);
40
+ const isSectionChanged = currentSection.peek() !== chosenSection;
50
41
 
51
- if (sectionFromLS) {
52
- setSection(sectionFromLS);
42
+ if (isDefaultSection) {
43
+ navigateToRoot();
53
44
  return;
54
45
  }
55
46
 
56
- setSection("");
47
+ if (isSectionChanged && isValidSection) {
48
+ navigateToSection({ section: chosenSection as "timeline" | "charts" });
49
+ }
57
50
  };
58
-
59
- effect(() => {
60
- const category = route.value.category;
61
-
62
- setSection(category || "");
63
- });
@@ -20,7 +20,7 @@ export const statsByEnvStore = signal<StoreSignalState<Record<string, Statistic>
20
20
 
21
21
  export const fetchReportStats = async () => {
22
22
  reportStatsStore.value = {
23
- ...reportStatsStore.value,
23
+ ...reportStatsStore.peek(),
24
24
  loading: true,
25
25
  error: undefined,
26
26
  };
@@ -43,7 +43,7 @@ export const fetchReportStats = async () => {
43
43
  };
44
44
 
45
45
  export const fetchEnvStats = async (envs: string[]) => {
46
- const envsToFetch = envs.filter((env) => !statsByEnvStore.value.data?.[env]);
46
+ const envsToFetch = envs.filter((env) => !statsByEnvStore.peek().data?.[env]);
47
47
 
48
48
  // all envs have already been fetched
49
49
  if (envsToFetch.length === 0) {
@@ -51,7 +51,7 @@ export const fetchEnvStats = async (envs: string[]) => {
51
51
  }
52
52
 
53
53
  statsByEnvStore.value = {
54
- ...statsByEnvStore.value,
54
+ ...statsByEnvStore.peek(),
55
55
  loading: true,
56
56
  error: undefined,
57
57
  };
@@ -73,7 +73,7 @@ export const fetchEnvStats = async (envs: string[]) => {
73
73
  };
74
74
  } catch (err) {
75
75
  statsByEnvStore.value = {
76
- ...statsByEnvStore.value,
76
+ ...statsByEnvStore.peek(),
77
77
  error: err.message,
78
78
  loading: false,
79
79
  };
@@ -0,0 +1,5 @@
1
+ import { computed } from "@preact/signals";
2
+ import { testResultRoute } from "./router";
3
+
4
+ export const trCurrentTab = computed(() => testResultRoute.value.params.tab ?? "overview");
5
+ export const currentTrId = computed(() => testResultRoute.value.params.testResultId);
@@ -32,7 +32,7 @@ export const fetchTestResultNav = async (env?: string) => {
32
32
  };
33
33
  } catch (err) {
34
34
  testResultNavStore.value = {
35
- ...testResultNavStore.value,
35
+ ...testResultNavStore.peek(),
36
36
  error: err.message,
37
37
  loading: false,
38
38
  };
@@ -40,12 +40,14 @@ export const fetchTestResultNav = async (env?: string) => {
40
40
  };
41
41
 
42
42
  export const fetchTestResult = async (testResultId: string) => {
43
- if (!testResultId || (testResultStore.value.data && testResultId in testResultStore.value.data)) {
43
+ const trData = testResultStore.peek().data;
44
+
45
+ if (!testResultId || (trData && testResultId in trData)) {
44
46
  return;
45
47
  }
46
48
 
47
49
  testResultStore.value = {
48
- ...testResultStore.value,
50
+ ...testResultStore.peek(),
49
51
  loading: true,
50
52
  error: undefined,
51
53
  };
@@ -56,13 +58,13 @@ export const fetchTestResult = async (testResultId: string) => {
56
58
  });
57
59
 
58
60
  testResultStore.value = {
59
- data: { ...testResultStore.value.data, [testResultId]: data },
61
+ data: { ...testResultStore.peek().data, [testResultId]: data },
60
62
  error: undefined,
61
63
  loading: false,
62
64
  };
63
65
  } catch (err) {
64
66
  testResultStore.value = {
65
- ...testResultStore.value,
67
+ ...testResultStore.peek(),
66
68
  error: err.message,
67
69
  loading: false,
68
70
  };
@@ -5,8 +5,11 @@ import type { StoreSignalState } from "@/stores/types";
5
5
 
6
6
  export type TimlineTr = Pick<
7
7
  TestResult,
8
- "id" | "name" | "status" | "flaky" | "hidden" | "labels" | "environment" | "start" | "stop" | "duration"
9
- >;
8
+ "id" | "name" | "status" | "flaky" | "hidden" | "environment" | "start" | "duration"
9
+ > & {
10
+ host: string;
11
+ thread: string;
12
+ };
10
13
 
11
14
  export const timelineStore = signal<StoreSignalState<TimlineTr[]>>({
12
15
  loading: true,
@@ -1,11 +1,12 @@
1
- import { fetchReportJsonData } from "@allurereport/web-commons";
1
+ import { buildFilterPredicate, fetchReportJsonData } from "@allurereport/web-commons";
2
2
  import type { RecursiveTree } from "@allurereport/web-components/global";
3
3
  import { computed, effect, signal } from "@preact/signals";
4
4
  import type { AwesomeTree, AwesomeTreeGroup } from "types";
5
5
  import type { StoreSignalState } from "@/stores/types";
6
6
  import { loadFromLocalStorage } from "@/utils/loadFromLocalStorage";
7
7
  import { createRecursiveTree, isRecursiveTreeEmpty } from "@/utils/treeFilters";
8
- import { treeDirection, treeFilter, treeQuery, treeSortBy, treeStatus } from "./treeFilters";
8
+ import { treeFilters } from "./treeFilters/store";
9
+ import { sortBy } from "./treeSort";
9
10
 
10
11
  export const treeStore = signal<StoreSignalState<Record<string, AwesomeTree>>>({
11
12
  loading: true,
@@ -36,7 +37,7 @@ export const toggleTree = (id: string) => {
36
37
  };
37
38
 
38
39
  export const fetchEnvTreesData = async (envs: string[]) => {
39
- const envsToFetch = envs.filter((env) => !treeStore.value.data?.[env]);
40
+ const envsToFetch = envs.filter((env) => !treeStore.peek().data?.[env]);
40
41
 
41
42
  // all envs have already been fetched
42
43
  if (envsToFetch.length === 0) {
@@ -44,7 +45,7 @@ export const fetchEnvTreesData = async (envs: string[]) => {
44
45
  }
45
46
 
46
47
  treeStore.value = {
47
- ...treeStore.value,
48
+ ...treeStore.peek(),
48
49
  loading: true,
49
50
  error: undefined,
50
51
  };
@@ -54,7 +55,7 @@ export const fetchEnvTreesData = async (envs: string[]) => {
54
55
  envsToFetch.map((env) => fetchReportJsonData<AwesomeTree>(`widgets/${env}/tree.json`, { bustCache: true })),
55
56
  );
56
57
 
57
- const previous = treeStore.value.data;
58
+ const previous = treeStore.peek().data;
58
59
  treeStore.value = {
59
60
  data: envsToFetch.reduce(
60
61
  (acc, env, index) => {
@@ -70,7 +71,7 @@ export const fetchEnvTreesData = async (envs: string[]) => {
70
71
  };
71
72
  } catch (e) {
72
73
  treeStore.value = {
73
- ...treeStore.value,
74
+ ...treeStore.peek(),
74
75
  error: e.message,
75
76
  loading: false,
76
77
  };
@@ -79,6 +80,16 @@ export const fetchEnvTreesData = async (envs: string[]) => {
79
80
 
80
81
  const treeEntries = computed(() => (treeStore.value.data ? Object.entries(treeStore.value.data) : []));
81
82
 
83
+ const alwaysTruePredicate = () => true;
84
+
85
+ const filterPredicate = computed(() => {
86
+ if (treeFilters.value.length === 0) {
87
+ return alwaysTruePredicate;
88
+ }
89
+
90
+ return buildFilterPredicate(treeFilters.value);
91
+ });
92
+
82
93
  export const filteredTree = computed(() => {
83
94
  return treeEntries.value.reduce(
84
95
  (acc, [key, value]) => {
@@ -87,17 +98,13 @@ export const filteredTree = computed(() => {
87
98
  }
88
99
 
89
100
  const { root, leavesById, groupsById } = value;
101
+
90
102
  const tree = createRecursiveTree({
91
103
  group: root as AwesomeTreeGroup,
92
104
  leavesById,
93
105
  groupsById,
94
- filterOptions: {
95
- query: treeQuery.value,
96
- status: treeStatus.value,
97
- filter: treeFilter.value,
98
- sortBy: treeSortBy.value,
99
- direction: treeDirection.value,
100
- },
106
+ filterPredicate: filterPredicate.value,
107
+ sortBy: sortBy.value,
101
108
  });
102
109
 
103
110
  return Object.assign(acc, {