@allurereport/web-awesome 3.4.1 → 3.6.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 (38) hide show
  1. package/dist/multi/{507.app-648d4a8e7d5405e104a1.js → 507.app-15cc636581486c011867.js} +1 -1
  2. package/dist/multi/app-15cc636581486c011867.js +2 -0
  3. package/dist/multi/manifest.json +23 -23
  4. package/dist/multi/{styles-b72a0161ddf41447b31c.css → styles-fd12e72f7e024e668bb4.css} +3 -3
  5. package/dist/single/app-bbd6e33664f6d94cfaac.js +2 -0
  6. package/dist/single/manifest.json +1 -1
  7. package/package.json +7 -7
  8. package/src/components/MainReport/index.tsx +23 -11
  9. package/src/components/ReportGlobalAttachments/index.tsx +80 -14
  10. package/src/components/ReportGlobalAttachments/styles.scss +18 -2
  11. package/src/components/ReportGlobalErrors/index.tsx +70 -8
  12. package/src/components/ReportGlobalErrors/styles.scss +16 -0
  13. package/src/stores/treeSort.ts +26 -23
  14. package/test/components/ReportGlobals.test.tsx +159 -0
  15. package/dist/multi/app-648d4a8e7d5405e104a1.js +0 -2
  16. package/dist/single/app-ae966ad3dc27579d6c39.js +0 -2
  17. /package/dist/multi/{173.app-648d4a8e7d5405e104a1.js → 173.app-15cc636581486c011867.js} +0 -0
  18. /package/dist/multi/{174.app-648d4a8e7d5405e104a1.js → 174.app-15cc636581486c011867.js} +0 -0
  19. /package/dist/multi/{252.app-648d4a8e7d5405e104a1.js → 252.app-15cc636581486c011867.js} +0 -0
  20. /package/dist/multi/{282.app-648d4a8e7d5405e104a1.js → 282.app-15cc636581486c011867.js} +0 -0
  21. /package/dist/multi/{29.app-648d4a8e7d5405e104a1.js → 29.app-15cc636581486c011867.js} +0 -0
  22. /package/dist/multi/{310.app-648d4a8e7d5405e104a1.js → 310.app-15cc636581486c011867.js} +0 -0
  23. /package/dist/multi/{416.app-648d4a8e7d5405e104a1.js → 416.app-15cc636581486c011867.js} +0 -0
  24. /package/dist/multi/{527.app-648d4a8e7d5405e104a1.js → 527.app-15cc636581486c011867.js} +0 -0
  25. /package/dist/multi/{600.app-648d4a8e7d5405e104a1.js → 600.app-15cc636581486c011867.js} +0 -0
  26. /package/dist/multi/{605.app-648d4a8e7d5405e104a1.js → 605.app-15cc636581486c011867.js} +0 -0
  27. /package/dist/multi/{638.app-648d4a8e7d5405e104a1.js → 638.app-15cc636581486c011867.js} +0 -0
  28. /package/dist/multi/{672.app-648d4a8e7d5405e104a1.js → 672.app-15cc636581486c011867.js} +0 -0
  29. /package/dist/multi/{686.app-648d4a8e7d5405e104a1.js → 686.app-15cc636581486c011867.js} +0 -0
  30. /package/dist/multi/{725.app-648d4a8e7d5405e104a1.js → 725.app-15cc636581486c011867.js} +0 -0
  31. /package/dist/multi/{741.app-648d4a8e7d5405e104a1.js → 741.app-15cc636581486c011867.js} +0 -0
  32. /package/dist/multi/{749.app-648d4a8e7d5405e104a1.js → 749.app-15cc636581486c011867.js} +0 -0
  33. /package/dist/multi/{755.app-648d4a8e7d5405e104a1.js → 755.app-15cc636581486c011867.js} +0 -0
  34. /package/dist/multi/{894.app-648d4a8e7d5405e104a1.js → 894.app-15cc636581486c011867.js} +0 -0
  35. /package/dist/multi/{943.app-648d4a8e7d5405e104a1.js → 943.app-15cc636581486c011867.js} +0 -0
  36. /package/dist/multi/{980.app-648d4a8e7d5405e104a1.js → 980.app-15cc636581486c011867.js} +0 -0
  37. /package/dist/multi/{app-648d4a8e7d5405e104a1.js.LICENSE.txt → app-15cc636581486c011867.js.LICENSE.txt} +0 -0
  38. /package/dist/single/{app-ae966ad3dc27579d6c39.js.LICENSE.txt → app-bbd6e33664f6d94cfaac.js.LICENSE.txt} +0 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- "main.js": "app-ae966ad3dc27579d6c39.js"
2
+ "main.js": "app-bbd6e33664f6d94cfaac.js"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/web-awesome",
3
- "version": "3.4.1",
3
+ "version": "3.6.0",
4
4
  "description": "The static files for Allure Awesome Report",
5
5
  "keywords": [
6
6
  "allure",
@@ -27,11 +27,11 @@
27
27
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
28
28
  },
29
29
  "dependencies": {
30
- "@allurereport/charts-api": "3.4.1",
31
- "@allurereport/core-api": "3.4.1",
32
- "@allurereport/plugin-api": "3.4.1",
33
- "@allurereport/web-commons": "3.4.1",
34
- "@allurereport/web-components": "3.4.1",
30
+ "@allurereport/charts-api": "3.6.0",
31
+ "@allurereport/core-api": "3.6.0",
32
+ "@allurereport/plugin-api": "3.6.0",
33
+ "@allurereport/web-commons": "3.6.0",
34
+ "@allurereport/web-components": "3.6.0",
35
35
  "@preact/signals": "^2.6.1",
36
36
  "clsx": "^2.1.1",
37
37
  "d3-shape": "^3.2.0",
@@ -80,7 +80,7 @@
80
80
  "svg-sprite-loader": "^6.0.11",
81
81
  "terser-webpack-plugin": "^5.3.14",
82
82
  "typescript": "^5.6.3",
83
- "vite": "^5.4.21",
83
+ "vite": "^8.0.8",
84
84
  "vitest": "^3.2.4",
85
85
  "webpack": "^5.99.9",
86
86
  "webpack-cli": "^5.1.4",
@@ -177,17 +177,29 @@ const MainReport = () => {
177
177
  />
178
178
  <Loadable
179
179
  source={globalsStore}
180
- renderData={({ attachments = [], errors = [] }) => (
181
- <>
182
- <RootTab id={ReportRootTab.GlobalAttachments}>
183
- {t("globalAttachments")} <Counter count={attachments.length} />
184
- </RootTab>
185
- <RootTab id={ReportRootTab.GlobalErrors}>
186
- {t("globalErrors")}{" "}
187
- <Counter status={errors.length > 0 ? "failed" : undefined} count={errors.length} />
188
- </RootTab>
189
- </>
190
- )}
180
+ renderData={({ attachments = [], attachmentsByEnv = {}, errors = [], errorsByEnv = {} }) => {
181
+ const currentEnvAttachments = currentEnvironment.value
182
+ ? (attachmentsByEnv[currentEnvironment.value] ?? [])
183
+ : attachments;
184
+ const currentEnvErrors = currentEnvironment.value
185
+ ? (errorsByEnv[currentEnvironment.value] ?? [])
186
+ : errors;
187
+
188
+ return (
189
+ <>
190
+ <RootTab id={ReportRootTab.GlobalAttachments}>
191
+ {t("globalAttachments")} <Counter count={currentEnvAttachments.length} />
192
+ </RootTab>
193
+ <RootTab id={ReportRootTab.GlobalErrors}>
194
+ {t("globalErrors")}{" "}
195
+ <Counter
196
+ status={currentEnvErrors.length > 0 ? "failed" : undefined}
197
+ count={currentEnvErrors.length}
198
+ />
199
+ </RootTab>
200
+ </>
201
+ );
202
+ }}
191
203
  />
192
204
  </NavTabsList>
193
205
  <div className={styles["main-report-tabs-content"]}>
@@ -1,35 +1,101 @@
1
- import type { AttachmentTestStepResult } from "@allurereport/core-api";
1
+ import { DEFAULT_ENVIRONMENT, type AttachmentTestStepResult } from "@allurereport/core-api";
2
+ import type { PluginGlobalAttachment } from "@allurereport/plugin-api";
2
3
  import { Loadable } from "@allurereport/web-components";
4
+ import { useState } from "preact/hooks";
3
5
 
4
- import { TrAttachmentView } from "@/components/TestResult/TrAttachmentsView";
6
+ import { MetadataButton } from "@/components/MetadataButton";
7
+ import { TrAttachment } from "@/components/TestResult/TrSteps/TrAttachment";
5
8
  import { useI18n } from "@/stores";
9
+ import { currentEnvironment, environmentNameById } from "@/stores/env";
6
10
  import { globalsStore } from "@/stores/globals";
7
11
 
8
- import { AwesomeTestResult } from "../../../types";
9
-
10
12
  import * as styles from "./styles.scss";
11
13
 
12
14
  export const ReportGlobalAttachments = () => {
13
15
  const { t } = useI18n("empty");
16
+ const { t: tEnvironments } = useI18n("environments");
17
+ const [collapsedEnvs, setCollapsedEnvs] = useState<string[]>([]);
18
+
19
+ const renderAttachmentList = (attachments: PluginGlobalAttachment[]) => {
20
+ const attachmentSteps: AttachmentTestStepResult[] = attachments.map((attachment) => ({
21
+ link: attachment,
22
+ type: "attachment",
23
+ }));
24
+
25
+ return (
26
+ <div className={styles["report-global-attachments-view"]}>
27
+ {attachmentSteps.map((attachment, index) => (
28
+ <TrAttachment
29
+ item={attachment}
30
+ key={attachment.link.id ?? `${attachment.link.name}-${index}`}
31
+ stepIndex={index + 1}
32
+ />
33
+ ))}
34
+ </div>
35
+ );
36
+ };
37
+
38
+ const renderAttachmentsContent = (attachments: PluginGlobalAttachment[]) => (
39
+ <div className={styles["report-global-attachments"]}>{renderAttachmentList(attachments)}</div>
40
+ );
14
41
 
15
42
  return (
16
43
  <Loadable
17
44
  source={globalsStore}
18
- renderData={({ attachments }) => {
19
- if (!attachments.length) {
45
+ renderData={({ attachments = [], attachmentsByEnv = {} }) => {
46
+ if (currentEnvironment.value) {
47
+ const currentEnvAttachments = attachmentsByEnv[currentEnvironment.value] ?? [];
48
+
49
+ if (!currentEnvAttachments.length) {
50
+ return <div className={styles["report-global-attachments-empty"]}>{t("no-attachments-results")}</div>;
51
+ }
52
+
53
+ return renderAttachmentsContent(currentEnvAttachments);
54
+ }
55
+
56
+ const entries = Object.entries(attachmentsByEnv).filter(([, envAttachments]) => envAttachments.length > 0);
57
+
58
+ if (!entries.length && !attachments.length) {
20
59
  return <div className={styles["report-global-attachments-empty"]}>{t("no-attachments-results")}</div>;
21
60
  }
22
61
 
23
- const attachmentSteps: AttachmentTestStepResult[] = attachments.map((attachment: any) => ({
24
- link: attachment,
25
- type: "attachment",
26
- }));
62
+ if (!entries.length) {
63
+ return renderAttachmentsContent(attachments);
64
+ }
65
+
66
+ if (entries.length === 1 && entries[0][0] === DEFAULT_ENVIRONMENT) {
67
+ return renderAttachmentsContent(entries[0][1] ?? []);
68
+ }
69
+
70
+ if (!attachments.length) {
71
+ return <div className={styles["report-global-attachments-empty"]}>{t("no-attachments-results")}</div>;
72
+ }
27
73
 
28
74
  return (
29
- <TrAttachmentView
30
- className={styles["report-global-attachments"]}
31
- testResult={{ attachments: attachmentSteps } as AwesomeTestResult}
32
- />
75
+ <div className={styles["report-global-attachments"]}>
76
+ {entries.map(([environmentId, envAttachments]) => {
77
+ const isOpened = !collapsedEnvs.includes(environmentId);
78
+ const toggleEnv = () => {
79
+ setCollapsedEnvs((prev) =>
80
+ isOpened ? prev.concat(environmentId) : prev.filter((currentId) => currentId !== environmentId),
81
+ );
82
+ };
83
+
84
+ return (
85
+ <div key={environmentId} className={styles["report-global-attachments-section"]}>
86
+ <MetadataButton
87
+ isOpened={isOpened}
88
+ setIsOpen={toggleEnv}
89
+ title={`${tEnvironments("environment", { count: 1 })}: "${environmentNameById(environmentId)}"`}
90
+ titleTooltipText={environmentNameById(environmentId)}
91
+ truncateTitle
92
+ counter={envAttachments.length}
93
+ />
94
+ {isOpened ? renderAttachmentList(envAttachments) : null}
95
+ </div>
96
+ );
97
+ })}
98
+ </div>
33
99
  );
34
100
  }}
35
101
  />
@@ -1,6 +1,22 @@
1
1
  .report-global-attachments {
2
- padding-left: 0;
3
- padding-right: 0;
2
+ padding: 12px 0 40px 8px;
3
+ }
4
+
5
+ .report-global-attachments-view {
6
+ min-height: 0 !important;
7
+ padding: 20px 0 8px;
8
+
9
+ [data-testid="test-result-attachment-header"] + div {
10
+ margin-top: 6px;
11
+ }
12
+ }
13
+
14
+ .report-global-attachments-section {
15
+ &:not(:last-child) {
16
+ padding-bottom: 8px;
17
+ margin-bottom: 14px;
18
+ border-bottom: 1px solid var(--on-border-muted);
19
+ }
4
20
  }
5
21
 
6
22
  .report-global-attachments-empty {
@@ -1,30 +1,92 @@
1
+ import { DEFAULT_ENVIRONMENT } from "@allurereport/core-api";
2
+ import type { PluginGlobalError } from "@allurereport/plugin-api";
1
3
  import { Loadable } from "@allurereport/web-components";
4
+ import { useState } from "preact/hooks";
2
5
 
6
+ import { MetadataButton } from "@/components/MetadataButton";
3
7
  import { TrError } from "@/components/TestResult/TrError";
4
8
  import { useI18n } from "@/stores";
9
+ import { currentEnvironment, environmentNameById } from "@/stores/env";
5
10
  import { globalsStore } from "@/stores/globals";
6
11
 
7
12
  import * as styles from "./styles.scss";
8
13
 
9
14
  export const ReportGlobalErrors = () => {
10
15
  const { t } = useI18n("empty");
16
+ const { t: tEnvironments } = useI18n("environments");
17
+ const [collapsedEnvs, setCollapsedEnvs] = useState<string[]>([]);
18
+
19
+ const renderErrors = (errors: PluginGlobalError[]) => (
20
+ <ul className={styles["report-global-errors"]}>
21
+ {errors.map((error, i) => (
22
+ <li key={i} className={styles["report-global-errors-item"]}>
23
+ <TrError {...error} />
24
+ </li>
25
+ ))}
26
+ </ul>
27
+ );
28
+
29
+ const renderErrorsContent = (errors: PluginGlobalError[]) => (
30
+ <div className={styles["report-global-errors-container"]}>{renderErrors(errors)}</div>
31
+ );
11
32
 
12
33
  return (
13
34
  <Loadable
14
35
  source={globalsStore}
15
- renderData={({ errors }) => {
36
+ renderData={({ errors = [], errorsByEnv = {} }) => {
37
+ if (currentEnvironment.value) {
38
+ const currentEnvErrors = errorsByEnv[currentEnvironment.value] ?? [];
39
+
40
+ if (!currentEnvErrors.length) {
41
+ return <div className={styles["report-global-errors-empty"]}>{t("no-global-errors-results")}</div>;
42
+ }
43
+
44
+ return renderErrorsContent(currentEnvErrors);
45
+ }
46
+
47
+ const entries = Object.entries(errorsByEnv).filter(([, envErrors]) => envErrors.length > 0);
48
+
49
+ if (!entries.length && !errors.length) {
50
+ return <div className={styles["report-global-errors-empty"]}>{t("no-global-errors-results")}</div>;
51
+ }
52
+
53
+ if (!entries.length) {
54
+ return renderErrorsContent(errors);
55
+ }
56
+
57
+ if (entries.length === 1 && entries[0][0] === DEFAULT_ENVIRONMENT) {
58
+ return renderErrorsContent(entries[0][1] ?? []);
59
+ }
60
+
16
61
  if (!errors.length) {
17
62
  return <div className={styles["report-global-errors-empty"]}>{t("no-global-errors-results")}</div>;
18
63
  }
19
64
 
20
65
  return (
21
- <ul className={styles["report-global-errors"]}>
22
- {errors.map((error, i) => (
23
- <li key={i} style={{ marginBottom: "8px" }}>
24
- <TrError {...error} />
25
- </li>
26
- ))}
27
- </ul>
66
+ <div className={styles["report-global-errors-container"]}>
67
+ {entries.map(([environmentId, envErrors]) => {
68
+ const isOpened = !collapsedEnvs.includes(environmentId);
69
+ const toggleEnv = () => {
70
+ setCollapsedEnvs((prev) =>
71
+ isOpened ? prev.concat(environmentId) : prev.filter((currentId) => currentId !== environmentId),
72
+ );
73
+ };
74
+
75
+ return (
76
+ <div key={environmentId} className={styles["report-global-errors-section"]}>
77
+ <MetadataButton
78
+ isOpened={isOpened}
79
+ setIsOpen={toggleEnv}
80
+ title={`${tEnvironments("environment", { count: 1 })}: "${environmentNameById(environmentId)}"`}
81
+ titleTooltipText={environmentNameById(environmentId)}
82
+ truncateTitle
83
+ counter={envErrors.length}
84
+ />
85
+ {isOpened ? renderErrors(envErrors) : null}
86
+ </div>
87
+ );
88
+ })}
89
+ </div>
28
90
  );
29
91
  }}
30
92
  />
@@ -1,9 +1,25 @@
1
1
  @import "~@allurereport/web-components/mixins.scss";
2
2
 
3
+ .report-global-errors-container {
4
+ padding: 12px 0 40px 8px;
5
+ }
6
+
3
7
  .report-global-errors {
4
8
  padding: 20px 0;
5
9
  }
6
10
 
11
+ .report-global-errors-item {
12
+ margin-bottom: 8px;
13
+ }
14
+
15
+ .report-global-errors-section {
16
+ &:not(:last-child) {
17
+ padding-bottom: 8px;
18
+ margin-bottom: 14px;
19
+ border-bottom: 1px solid var(--on-border-muted);
20
+ }
21
+ }
22
+
7
23
  .report-global-errors-empty {
8
24
  display: flex;
9
25
  padding: 48px 0;
@@ -1,5 +1,5 @@
1
1
  import { getParamValue, hasParam, setParams } from "@allurereport/web-commons";
2
- import { computed, effect } from "@preact/signals";
2
+ import { computed, effect, signal } from "@preact/signals";
3
3
 
4
4
  export type SortByDirection = "asc" | "desc";
5
5
  export type SortByField = "order" | "duration" | "status" | "name";
@@ -16,21 +16,6 @@ const SORT_BY_PARAM = "sortBy";
16
16
 
17
17
  const hasSortByParam = computed(() => hasParam(SORT_BY_PARAM));
18
18
 
19
- export const setSortBy = (sortByValue: SortBy) => {
20
- if (hasSortByParam.peek()) {
21
- setParams({
22
- key: SORT_BY_PARAM,
23
- value: undefined,
24
- });
25
- }
26
-
27
- if (typeof window === "undefined") {
28
- return;
29
- }
30
-
31
- localStorage.setItem(SORT_BY_STORAGE_KEY, sortByValue);
32
- };
33
-
34
19
  const validateSortBy = (sortByValue: string): sortByValue is SortBy => {
35
20
  const parts = sortByValue.split(",");
36
21
  if (parts.length !== 2) {
@@ -41,6 +26,30 @@ const validateSortBy = (sortByValue: string): sortByValue is SortBy => {
41
26
  return SORT_BY_FIELDS.includes(field as SortByField) && DIRECTIONS.includes(direction as SortByDirection);
42
27
  };
43
28
 
29
+ const getInitialSortBy = (): SortBy => {
30
+ if (typeof window === "undefined") {
31
+ return DEFAULT_SORT_BY;
32
+ }
33
+ const stored = localStorage.getItem(SORT_BY_STORAGE_KEY);
34
+ if (stored && validateSortBy(stored.toLowerCase())) {
35
+ return stored.toLowerCase() as SortBy;
36
+ }
37
+ return DEFAULT_SORT_BY;
38
+ };
39
+
40
+ const sortBySignal = signal<SortBy>(getInitialSortBy());
41
+
42
+ export const setSortBy = (sortByValue: SortBy) => {
43
+ if (hasSortByParam.peek()) {
44
+ setParams({
45
+ key: SORT_BY_PARAM,
46
+ value: undefined,
47
+ });
48
+ }
49
+
50
+ sortBySignal.value = sortByValue;
51
+ };
52
+
44
53
  export const sortBy = computed<SortBy>(() => {
45
54
  const urlSortBy = getParamValue(SORT_BY_PARAM) ?? undefined;
46
55
 
@@ -53,13 +62,7 @@ export const sortBy = computed<SortBy>(() => {
53
62
  return DEFAULT_SORT_BY;
54
63
  }
55
64
 
56
- const storageSortBy = localStorage.getItem(SORT_BY_STORAGE_KEY);
57
-
58
- if (storageSortBy && validateSortBy(storageSortBy.toLowerCase())) {
59
- return storageSortBy.toLowerCase() as SortBy;
60
- }
61
-
62
- return DEFAULT_SORT_BY;
65
+ return sortBySignal.value;
63
66
  });
64
67
 
65
68
  effect(() => {
@@ -0,0 +1,159 @@
1
+ import { signal } from "@preact/signals";
2
+ import { cleanup, render, screen } from "@testing-library/preact";
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
+
5
+ const environmentNames: Record<string, string> = {
6
+ prod_env: "Prod",
7
+ qa_env: "QA",
8
+ };
9
+
10
+ const setupGlobalsComponent = async (data: Record<string, unknown>) => {
11
+ vi.resetModules();
12
+
13
+ const currentEnvironment = signal("");
14
+ const globalsStore = signal({
15
+ loading: false,
16
+ error: undefined,
17
+ data,
18
+ });
19
+
20
+ vi.doMock("@allurereport/web-components", () => ({
21
+ Loadable: ({ source, renderData }: { source: { value: { data: unknown } }; renderData: (data: any) => unknown }) =>
22
+ renderData(source.value.data),
23
+ }));
24
+ vi.doMock("@/stores", () => ({
25
+ useI18n: () => ({
26
+ t: (key: string) => key,
27
+ }),
28
+ }));
29
+ vi.doMock("@/stores/env", () => ({
30
+ currentEnvironment,
31
+ environmentNameById: (environmentId: string) => environmentNames[environmentId] ?? environmentId,
32
+ }));
33
+ vi.doMock("@/stores/globals", () => ({
34
+ globalsStore,
35
+ }));
36
+ vi.doMock("@/components/MetadataButton", () => ({
37
+ MetadataButton: ({ title, counter }: { title?: string; counter?: number }) => (
38
+ <div>{counter === undefined ? title : `${title} (${counter})`}</div>
39
+ ),
40
+ }));
41
+ vi.doMock("@/components/TestResult/TrSteps/TrAttachment", () => ({
42
+ TrAttachment: ({ item }: { item: { link: { name?: string; originalFileName: string } } }) => (
43
+ <span>{item.link.name ?? item.link.originalFileName}</span>
44
+ ),
45
+ }));
46
+ vi.doMock("@/components/TestResult/TrError", () => ({
47
+ TrError: ({ message }: { message: string }) => <div>{message}</div>,
48
+ }));
49
+ };
50
+
51
+ describe("components > Report globals", () => {
52
+ afterEach(() => {
53
+ cleanup();
54
+ });
55
+
56
+ it("should render grouped global attachments using quality-gate style environment sections", async () => {
57
+ await setupGlobalsComponent({
58
+ attachments: [
59
+ {
60
+ id: "default",
61
+ name: "default.log",
62
+ originalFileName: "default.log",
63
+ ext: ".log",
64
+ used: true,
65
+ missed: false,
66
+ environment: "default",
67
+ },
68
+ {
69
+ id: "qa",
70
+ name: "qa.log",
71
+ originalFileName: "qa.log",
72
+ ext: ".log",
73
+ used: true,
74
+ missed: false,
75
+ environment: "QA",
76
+ },
77
+ {
78
+ id: "prod",
79
+ name: "prod.log",
80
+ originalFileName: "prod.log",
81
+ ext: ".log",
82
+ used: true,
83
+ missed: false,
84
+ environment: "Prod",
85
+ },
86
+ ],
87
+ attachmentsByEnv: {
88
+ default: [
89
+ {
90
+ id: "default",
91
+ name: "default.log",
92
+ originalFileName: "default.log",
93
+ ext: ".log",
94
+ used: true,
95
+ missed: false,
96
+ environment: "default",
97
+ },
98
+ ],
99
+ qa_env: [
100
+ {
101
+ id: "qa",
102
+ name: "qa.log",
103
+ originalFileName: "qa.log",
104
+ ext: ".log",
105
+ used: true,
106
+ missed: false,
107
+ environment: "QA",
108
+ },
109
+ ],
110
+ prod_env: [
111
+ {
112
+ id: "prod",
113
+ name: "prod.log",
114
+ originalFileName: "prod.log",
115
+ ext: ".log",
116
+ used: true,
117
+ missed: false,
118
+ environment: "Prod",
119
+ },
120
+ ],
121
+ },
122
+ });
123
+
124
+ const { ReportGlobalAttachments } = await import("@/components/ReportGlobalAttachments");
125
+
126
+ render(<ReportGlobalAttachments />);
127
+
128
+ expect(screen.queryByText(/all/i)).not.toBeInTheDocument();
129
+ expect(screen.getByText("default.log")).toBeInTheDocument();
130
+ expect(screen.getByText('environment: "default" (1)')).toBeInTheDocument();
131
+ expect(screen.getByText('environment: "QA" (1)')).toBeInTheDocument();
132
+ expect(screen.getByText('environment: "Prod" (1)')).toBeInTheDocument();
133
+ }, 15000);
134
+
135
+ it("should render grouped global errors using quality-gate style environment sections", async () => {
136
+ await setupGlobalsComponent({
137
+ errors: [
138
+ { message: "Default failure", environment: "default" },
139
+ { message: "QA failure", environment: "QA" },
140
+ { message: "Prod failure", environment: "Prod" },
141
+ ],
142
+ errorsByEnv: {
143
+ default: [{ message: "Default failure", environment: "default" }],
144
+ qa_env: [{ message: "QA failure", environment: "QA" }],
145
+ prod_env: [{ message: "Prod failure", environment: "Prod" }],
146
+ },
147
+ });
148
+
149
+ const { ReportGlobalErrors } = await import("@/components/ReportGlobalErrors");
150
+
151
+ render(<ReportGlobalErrors />);
152
+
153
+ expect(screen.queryByText(/all/i)).not.toBeInTheDocument();
154
+ expect(screen.getByText("Default failure")).toBeInTheDocument();
155
+ expect(screen.getByText('environment: "default" (1)')).toBeInTheDocument();
156
+ expect(screen.getByText('environment: "QA" (1)')).toBeInTheDocument();
157
+ expect(screen.getByText('environment: "Prod" (1)')).toBeInTheDocument();
158
+ }, 15000);
159
+ });