@allurereport/web-awesome 3.9.0 → 3.11.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 (125) hide show
  1. package/dist/multi/173.app-79d160f9989770266f96.js +1 -0
  2. package/dist/multi/174.app-79d160f9989770266f96.js +1 -0
  3. package/dist/multi/252.app-79d160f9989770266f96.js +1 -0
  4. package/dist/multi/282.app-79d160f9989770266f96.js +1 -0
  5. package/dist/multi/29.app-79d160f9989770266f96.js +1 -0
  6. package/dist/multi/310.app-79d160f9989770266f96.js +1 -0
  7. package/dist/multi/416.app-79d160f9989770266f96.js +1 -0
  8. package/dist/multi/507.app-79d160f9989770266f96.js +1 -0
  9. package/dist/multi/527.app-79d160f9989770266f96.js +1 -0
  10. package/dist/multi/600.app-79d160f9989770266f96.js +1 -0
  11. package/dist/multi/605.app-79d160f9989770266f96.js +1 -0
  12. package/dist/multi/638.app-79d160f9989770266f96.js +1 -0
  13. package/dist/multi/672.app-79d160f9989770266f96.js +1 -0
  14. package/dist/multi/686.app-79d160f9989770266f96.js +1 -0
  15. package/dist/multi/725.app-79d160f9989770266f96.js +1 -0
  16. package/dist/multi/741.app-79d160f9989770266f96.js +1 -0
  17. package/dist/multi/749.app-79d160f9989770266f96.js +1 -0
  18. package/dist/multi/755.app-79d160f9989770266f96.js +1 -0
  19. package/dist/multi/894.app-79d160f9989770266f96.js +1 -0
  20. package/dist/multi/943.app-79d160f9989770266f96.js +1 -0
  21. package/dist/multi/980.app-79d160f9989770266f96.js +1 -0
  22. package/dist/multi/app-79d160f9989770266f96.js +2 -0
  23. package/dist/multi/manifest.json +25 -25
  24. package/dist/multi/styles-b7f017f505b72e148e8b.css +58 -0
  25. package/dist/single/app-36af07268613f77d3ac5.js +2 -0
  26. package/dist/single/manifest.json +1 -1
  27. package/package.json +15 -20
  28. package/src/components/BaseLayout/styles.scss +3 -1
  29. package/src/components/Footer/FooterVersion.tsx +5 -10
  30. package/src/components/Footer/index.tsx +7 -1
  31. package/src/components/Footer/styles.scss +6 -0
  32. package/src/components/Header/CiInfo/index.tsx +17 -13
  33. package/src/components/HeaderControls/index.tsx +1 -3
  34. package/src/components/KeyboardShortcuts/styles.scss +5 -5
  35. package/src/components/MainReport/index.tsx +3 -9
  36. package/src/components/MainReport/styles.scss +1 -26
  37. package/src/components/Metadata/index.tsx +27 -6
  38. package/src/components/Metadata/styles.scss +12 -0
  39. package/src/components/ReportBody/index.tsx +3 -12
  40. package/src/components/ReportBody/styles.scss +0 -21
  41. package/src/components/ReportHeader/index.tsx +25 -13
  42. package/src/components/ReportMetadata/index.tsx +35 -4
  43. package/src/components/SideBySide/styles.scss +10 -0
  44. package/src/components/SplitLayout/index.tsx +2 -9
  45. package/src/components/SplitLayout/styles.scss +4 -7
  46. package/src/components/TestResult/TrOverview.tsx +9 -2
  47. package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +27 -1
  48. package/src/components/TestResult/TrRetriesView/styles.scss +17 -7
  49. package/src/components/TestResult/TrSetup/index.tsx +1 -1
  50. package/src/components/TestResult/TrSteps/TrAttachment.tsx +2 -1
  51. package/src/components/TestResult/TrSteps/TrAttachmentInfo.tsx +11 -3
  52. package/src/components/TestResult/TrSteps/TrBodyItems.tsx +5 -2
  53. package/src/components/TestResult/TrSteps/TrStep.tsx +6 -2
  54. package/src/components/TestResult/TrSteps/index.tsx +8 -5
  55. package/src/components/TestResult/TrTeardown/index.tsx +1 -1
  56. package/src/components/TestResult/index.tsx +2 -3
  57. package/src/components/TestResult/styles.scss +0 -5
  58. package/src/components/Tree/index.tsx +21 -1
  59. package/src/locales/ar.json +1 -0
  60. package/src/locales/az.json +1 -0
  61. package/src/locales/de.json +1 -0
  62. package/src/locales/en.json +1 -0
  63. package/src/locales/es.json +1 -0
  64. package/src/locales/fr.json +1 -0
  65. package/src/locales/he.json +1 -0
  66. package/src/locales/hy.json +1 -0
  67. package/src/locales/it.json +1 -0
  68. package/src/locales/ja.json +1 -0
  69. package/src/locales/ka.json +1 -0
  70. package/src/locales/kr.json +1 -0
  71. package/src/locales/nl.json +1 -0
  72. package/src/locales/pl.json +1 -0
  73. package/src/locales/pt.json +1 -0
  74. package/src/locales/ru.json +1 -0
  75. package/src/locales/sv.json +1 -0
  76. package/src/locales/tr.json +1 -0
  77. package/src/locales/uk.json +1 -0
  78. package/src/locales/zh-TW.json +1 -0
  79. package/src/locales/zh.json +1 -0
  80. package/src/stores/locale.ts +4 -2
  81. package/src/stores/treeSort.ts +7 -1
  82. package/src/styles/_pane-active.scss +2 -2
  83. package/src/utils/atSeparator.ts +4 -0
  84. package/src/utils/time.ts +2 -1
  85. package/src/utils/treeFilters.ts +15 -4
  86. package/test/components/Footer.test.tsx +26 -0
  87. package/test/components/Header/CiInfo.test.tsx +48 -0
  88. package/test/components/HeaderControls.test.tsx +28 -0
  89. package/test/components/ReportHeader.test.tsx +77 -0
  90. package/test/components/ReportMetadata.test.tsx +131 -0
  91. package/test/components/TestResult/TrRetriesItem.test.tsx +163 -0
  92. package/test/components/TestResult/TrSteps.test.tsx +45 -10
  93. package/test/stores/treeSort.test.ts +58 -0
  94. package/test/utils/time.test.ts +52 -0
  95. package/test/utils/treeFilters.test.ts +104 -0
  96. package/types.d.ts +22 -0
  97. package/webpack.config.js +9 -7
  98. package/dist/multi/173.app-d36b0855e3e7a53eeee9.js +0 -1
  99. package/dist/multi/174.app-d36b0855e3e7a53eeee9.js +0 -1
  100. package/dist/multi/252.app-d36b0855e3e7a53eeee9.js +0 -1
  101. package/dist/multi/282.app-d36b0855e3e7a53eeee9.js +0 -1
  102. package/dist/multi/29.app-d36b0855e3e7a53eeee9.js +0 -1
  103. package/dist/multi/310.app-d36b0855e3e7a53eeee9.js +0 -1
  104. package/dist/multi/416.app-d36b0855e3e7a53eeee9.js +0 -1
  105. package/dist/multi/507.app-d36b0855e3e7a53eeee9.js +0 -1
  106. package/dist/multi/527.app-d36b0855e3e7a53eeee9.js +0 -1
  107. package/dist/multi/600.app-d36b0855e3e7a53eeee9.js +0 -1
  108. package/dist/multi/605.app-d36b0855e3e7a53eeee9.js +0 -1
  109. package/dist/multi/638.app-d36b0855e3e7a53eeee9.js +0 -1
  110. package/dist/multi/672.app-d36b0855e3e7a53eeee9.js +0 -1
  111. package/dist/multi/686.app-d36b0855e3e7a53eeee9.js +0 -1
  112. package/dist/multi/725.app-d36b0855e3e7a53eeee9.js +0 -1
  113. package/dist/multi/741.app-d36b0855e3e7a53eeee9.js +0 -1
  114. package/dist/multi/749.app-d36b0855e3e7a53eeee9.js +0 -1
  115. package/dist/multi/755.app-d36b0855e3e7a53eeee9.js +0 -1
  116. package/dist/multi/894.app-d36b0855e3e7a53eeee9.js +0 -1
  117. package/dist/multi/943.app-d36b0855e3e7a53eeee9.js +0 -1
  118. package/dist/multi/980.app-d36b0855e3e7a53eeee9.js +0 -1
  119. package/dist/multi/app-d36b0855e3e7a53eeee9.js +0 -2
  120. package/dist/multi/styles-468416ffee9a9dea6cae.css +0 -58
  121. package/dist/single/app-62171f5f51b5954a787c.js +0 -2
  122. /package/dist/multi/{121.app-d36b0855e3e7a53eeee9.js → 121.app-79d160f9989770266f96.js} +0 -0
  123. /package/dist/multi/{779.app-d36b0855e3e7a53eeee9.js → 779.app-79d160f9989770266f96.js} +0 -0
  124. /package/dist/multi/{app-d36b0855e3e7a53eeee9.js.LICENSE.txt → app-79d160f9989770266f96.js.LICENSE.txt} +0 -0
  125. /package/dist/single/{app-62171f5f51b5954a787c.js.LICENSE.txt → app-36af07268613f77d3ac5.js.LICENSE.txt} +0 -0
@@ -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
+ });
@@ -19,9 +19,25 @@ vi.hoisted(() => {
19
19
 
20
20
  import type { TrBodyItem } from "@/components/TestResult/bodyItems";
21
21
  import { TrSteps } from "@/components/TestResult/TrSteps";
22
+ import { TrStep } from "@/components/TestResult/TrSteps/TrStep";
22
23
  import { collapsedTrees, expandedTrees } from "@/stores/tree";
23
24
 
24
- const passedStep = {
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 = {
25
41
  type: "step",
26
42
  item: {
27
43
  stepId: "passed-step",
@@ -33,10 +49,10 @@ const passedStep = {
33
49
  hasSimilarErrorInSubSteps: false,
34
50
  },
35
51
  suppressInlineError: false,
36
- bodyItems: [],
52
+ bodyItems: [nestedPassedStep],
37
53
  } satisfies TrBodyItem;
38
54
 
39
- const failedStep = {
55
+ const failedStepWithContent = {
40
56
  type: "step",
41
57
  item: {
42
58
  stepId: "failed-step",
@@ -48,7 +64,7 @@ const failedStep = {
48
64
  hasSimilarErrorInSubSteps: false,
49
65
  },
50
66
  suppressInlineError: false,
51
- bodyItems: [],
67
+ bodyItems: [nestedPassedStep],
52
68
  } satisfies TrBodyItem;
53
69
 
54
70
  describe("components > TestResult > TrSteps", () => {
@@ -59,15 +75,34 @@ describe("components > TestResult > TrSteps", () => {
59
75
  globalThis.allureReportOptions = { stepTreeExpansion: "expand_failed_only" } as any;
60
76
  });
61
77
 
62
- it("collapses passed-only root steps by default with expand_failed_only", () => {
63
- const view = render(<TrSteps id="passed-test" bodyItems={[passedStep]} />);
78
+ it("always shows the steps root container regardless of step status", () => {
79
+ const view = render(<TrSteps id="test" bodyItems={[passedStepWithContent]} />);
64
80
 
65
- expect(view.queryByTestId("test-result-steps-root")).not.toBeInTheDocument();
81
+ expect(view.getByTestId("test-result-steps-root")).toBeInTheDocument();
66
82
  });
67
83
 
68
- it("opens root steps by default when expand_failed_only finds failed context", () => {
69
- const view = render(<TrSteps id="failed-test" bodyItems={[passedStep, failedStep]} />);
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} />);
70
86
 
71
- expect(view.getByTestId("test-result-steps-root")).toBeInTheDocument();
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);
72
107
  });
73
108
  });
@@ -0,0 +1,58 @@
1
+ import { epic, feature, label, story } from "allure-js-commons";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ const STORAGE_KEY = "ALLURE_REPORT_SORT_BY";
5
+
6
+ beforeEach(async () => {
7
+ await epic("coverage");
8
+ await feature("sort");
9
+ await story("treeSort");
10
+ await label("coverage", "sort");
11
+ });
12
+
13
+ describe("stores > treeSort", () => {
14
+ beforeEach(() => {
15
+ vi.resetModules();
16
+ localStorage.clear();
17
+ delete (globalThis as any).allureReportOptions;
18
+ });
19
+
20
+ it("defaults to order,asc when no config and no localStorage value", async () => {
21
+ const { sortBy } = await import("../../src/stores/treeSort.js");
22
+
23
+ expect(sortBy.value).toBe("order,asc");
24
+ });
25
+
26
+ it("uses defaultSortBy from reportOptions when localStorage is empty", async () => {
27
+ (globalThis as any).allureReportOptions = { defaultSortBy: "name,asc" };
28
+
29
+ const { sortBy } = await import("../../src/stores/treeSort.js");
30
+
31
+ expect(sortBy.value).toBe("name,asc");
32
+ });
33
+
34
+ it("is case-insensitive for defaultSortBy", async () => {
35
+ (globalThis as any).allureReportOptions = { defaultSortBy: "Name,ASC" };
36
+
37
+ const { sortBy } = await import("../../src/stores/treeSort.js");
38
+
39
+ expect(sortBy.value).toBe("name,asc");
40
+ });
41
+
42
+ it("ignores invalid defaultSortBy and falls back to order,asc", async () => {
43
+ (globalThis as any).allureReportOptions = { defaultSortBy: "invalid,value" };
44
+
45
+ const { sortBy } = await import("../../src/stores/treeSort.js");
46
+
47
+ expect(sortBy.value).toBe("order,asc");
48
+ });
49
+
50
+ it("localStorage takes priority over defaultSortBy from reportOptions", async () => {
51
+ localStorage.setItem(STORAGE_KEY, "duration,desc");
52
+ (globalThis as any).allureReportOptions = { defaultSortBy: "name,asc" };
53
+
54
+ const { sortBy } = await import("../../src/stores/treeSort.js");
55
+
56
+ expect(sortBy.value).toBe("duration,desc");
57
+ });
58
+ });
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ import { timestampToDate } from "@/utils/time";
4
+
5
+ const numericDateTimeOptions: Intl.DateTimeFormatOptions = {
6
+ month: "numeric",
7
+ day: "numeric",
8
+ year: "numeric",
9
+ hour: "numeric",
10
+ minute: "numeric",
11
+ second: "numeric",
12
+ hour12: false,
13
+ };
14
+
15
+ vi.mock("@/stores/locale", () => ({
16
+ currentLocale: { value: "en" },
17
+ currentLocaleIso: { value: "en-US" },
18
+ useI18n: () => ({
19
+ t: (key: string) => (key === "at" ? "at" : key),
20
+ }),
21
+ }));
22
+
23
+ vi.mock("@allurereport/web-commons", async (importOriginal) => ({
24
+ ...(await importOriginal()),
25
+ getLocaleDateTimeOverride: () => undefined,
26
+ }));
27
+
28
+ describe("utils > timestampToDate", () => {
29
+ it("should keep Intl-provided at separator for long English dates", () => {
30
+ const timestamp = 1527776360360;
31
+ const options: Intl.DateTimeFormatOptions = {
32
+ month: "long",
33
+ day: "numeric",
34
+ year: "numeric",
35
+ hour: "numeric",
36
+ minute: "numeric",
37
+ second: "numeric",
38
+ };
39
+ const intlFormatted = new Intl.DateTimeFormat("en-US", options).format(new Date(timestamp));
40
+ const formatted = timestampToDate(timestamp, options);
41
+
42
+ expect(formatted).toBe(intlFormatted);
43
+ expect(formatted).not.toContain("May 31 at 2018 at");
44
+ });
45
+
46
+ it("should add at separator when Intl returns comma-separated date and time", () => {
47
+ const timestamp = 1779887536194;
48
+ const intlFormatted = new Intl.DateTimeFormat("en-US", numericDateTimeOptions).format(new Date(timestamp));
49
+
50
+ expect(timestampToDate(timestamp, numericDateTimeOptions)).toBe(intlFormatted.replace(",", " at"));
51
+ });
52
+ });
@@ -345,6 +345,110 @@ describe("utils > treeFilters", () => {
345
345
  );
346
346
  });
347
347
 
348
+ it("sorts groups by earliest leaf groupOrder when sorting by order ascending", () => {
349
+ const group = {
350
+ leaves: [],
351
+ groups: ["groupA", "groupB", "groupC"],
352
+ };
353
+ const leavesById = {
354
+ t1: { name: "t1", status: "passed", groupOrder: 5 } as AwesomeTestResult,
355
+ t2: { name: "t2", status: "passed", groupOrder: 2 } as AwesomeTestResult,
356
+ t3: { name: "t3", status: "passed", groupOrder: 8 } as AwesomeTestResult,
357
+ };
358
+ const groupsById = {
359
+ groupA: { name: "groupA", leaves: ["t1"], groups: [] as string[] },
360
+ groupB: { name: "groupB", leaves: ["t2"], groups: [] as string[] },
361
+ groupC: { name: "groupC", leaves: ["t3"], groups: [] as string[] },
362
+ };
363
+ const result = createRecursiveTree({
364
+ group: group as any,
365
+ leavesById: leavesById as any,
366
+ groupsById: groupsById as any,
367
+ filterPredicate: alwaysTruePredicate,
368
+ sortBy: "order,asc",
369
+ });
370
+
371
+ expect(result.trees.map((t) => t.name)).toEqual(["groupB", "groupA", "groupC"]);
372
+ });
373
+
374
+ it("sorts groups by earliest leaf groupOrder when sorting by order descending", () => {
375
+ const group = {
376
+ leaves: [],
377
+ groups: ["groupA", "groupB", "groupC"],
378
+ };
379
+ const leavesById = {
380
+ t1: { name: "t1", status: "passed", groupOrder: 5 } as AwesomeTestResult,
381
+ t2: { name: "t2", status: "passed", groupOrder: 2 } as AwesomeTestResult,
382
+ t3: { name: "t3", status: "passed", groupOrder: 8 } as AwesomeTestResult,
383
+ };
384
+ const groupsById = {
385
+ groupA: { name: "groupA", leaves: ["t1"], groups: [] as string[] },
386
+ groupB: { name: "groupB", leaves: ["t2"], groups: [] as string[] },
387
+ groupC: { name: "groupC", leaves: ["t3"], groups: [] as string[] },
388
+ };
389
+ const result = createRecursiveTree({
390
+ group: group as any,
391
+ leavesById: leavesById as any,
392
+ groupsById: groupsById as any,
393
+ filterPredicate: alwaysTruePredicate,
394
+ sortBy: "order,desc",
395
+ });
396
+
397
+ expect(result.trees.map((t) => t.name)).toEqual(["groupC", "groupA", "groupB"]);
398
+ });
399
+
400
+ it("sorts groups by aggregated duration when sorting by duration ascending", () => {
401
+ const group = {
402
+ leaves: [],
403
+ groups: ["groupA", "groupB", "groupC"],
404
+ };
405
+ const leavesById = {
406
+ t1: { name: "t1", status: "passed", duration: 3000 } as AwesomeTestResult,
407
+ t2: { name: "t2", status: "passed", duration: 1000 } as AwesomeTestResult,
408
+ t3: { name: "t3", status: "passed", duration: 5000 } as AwesomeTestResult,
409
+ };
410
+ const groupsById = {
411
+ groupA: { name: "groupA", leaves: ["t1"], groups: [] as string[] },
412
+ groupB: { name: "groupB", leaves: ["t2"], groups: [] as string[] },
413
+ groupC: { name: "groupC", leaves: ["t3"], groups: [] as string[] },
414
+ };
415
+ const result = createRecursiveTree({
416
+ group: group as any,
417
+ leavesById: leavesById as any,
418
+ groupsById: groupsById as any,
419
+ filterPredicate: alwaysTruePredicate,
420
+ sortBy: "duration,asc",
421
+ });
422
+
423
+ expect(result.trees.map((t) => t.name)).toEqual(["groupB", "groupA", "groupC"]);
424
+ });
425
+
426
+ it("sorts groups by aggregated duration when sorting by duration descending", () => {
427
+ const group = {
428
+ leaves: [],
429
+ groups: ["groupA", "groupB", "groupC"],
430
+ };
431
+ const leavesById = {
432
+ t1: { name: "t1", status: "passed", duration: 3000 } as AwesomeTestResult,
433
+ t2: { name: "t2", status: "passed", duration: 1000 } as AwesomeTestResult,
434
+ t3: { name: "t3", status: "passed", duration: 5000 } as AwesomeTestResult,
435
+ };
436
+ const groupsById = {
437
+ groupA: { name: "groupA", leaves: ["t1"], groups: [] as string[] },
438
+ groupB: { name: "groupB", leaves: ["t2"], groups: [] as string[] },
439
+ groupC: { name: "groupC", leaves: ["t3"], groups: [] as string[] },
440
+ };
441
+ const result = createRecursiveTree({
442
+ group: group as any,
443
+ leavesById: leavesById as any,
444
+ groupsById: groupsById as any,
445
+ filterPredicate: alwaysTruePredicate,
446
+ sortBy: "duration,desc",
447
+ });
448
+
449
+ expect(result.trees.map((t) => t.name)).toEqual(["groupC", "groupA", "groupB"]);
450
+ });
451
+
348
452
  it("keeps problem-heavy groups first when sorting by status in descending order", () => {
349
453
  const group = {
350
454
  leaves: [],
package/types.d.ts CHANGED
@@ -14,6 +14,23 @@ import type {
14
14
  export type Layout = "base" | "split";
15
15
  export type StepTreeExpansion = "collapsed" | "expand_failed_only" | "expanded";
16
16
 
17
+ export type AwesomeRunSummary = {
18
+ start: number;
19
+ stop: number;
20
+ duration: number;
21
+ };
22
+
23
+ export type AwesomeExecutorInfo = {
24
+ name?: string;
25
+ type?: string;
26
+ url?: string;
27
+ buildOrder?: number;
28
+ buildName?: string;
29
+ buildUrl?: string;
30
+ reportName?: string;
31
+ reportUrl?: string;
32
+ };
33
+
17
34
  export type AwesomeReportOptions = {
18
35
  allureVersion: string;
19
36
  reportName?: string;
@@ -28,7 +45,10 @@ export type AwesomeReportOptions = {
28
45
  sections?: string[];
29
46
  cacheKey: string;
30
47
  ci?: CiDescriptor;
48
+ executor?: AwesomeExecutorInfo;
49
+ runSummary?: AwesomeRunSummary;
31
50
  stepTreeExpansion?: StepTreeExpansion;
51
+ defaultSortBy?: string;
32
52
  };
33
53
 
34
54
  export type AwesomeFixtureResult = Omit<
@@ -122,6 +142,8 @@ export type AwesomeRecursiveTree = DefaultTreeGroup & {
122
142
  nodeId: string;
123
143
  leaves: AwesomeTreeLeaf[];
124
144
  trees: AwesomeRecursiveTree[];
145
+ duration: number;
146
+ groupOrder: number;
125
147
  };
126
148
 
127
149
  // TODO: maybe it should call `TestCase` instead of Group
package/webpack.config.js CHANGED
@@ -1,14 +1,16 @@
1
+ import { createRequire } from "node:module";
1
2
  import { dirname, join } from "node:path";
2
3
  import { env } from "node:process";
3
4
  import { fileURLToPath } from "node:url";
4
5
 
5
- import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
6
- import HtmlWebpackPlugin from "html-webpack-plugin";
7
- import MiniCssExtractPlugin from "mini-css-extract-plugin";
8
- import SpriteLoaderPlugin from "svg-sprite-loader/plugin.js";
9
- import TerserPlugin from "terser-webpack-plugin";
10
- import webpack from "webpack";
11
- import { WebpackManifestPlugin } from "webpack-manifest-plugin";
6
+ const require = createRequire(import.meta.url);
7
+ const ForkTsCheckerPlugin = require("fork-ts-checker-webpack-plugin");
8
+ const HtmlWebpackPlugin = require("html-webpack-plugin");
9
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
10
+ const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
11
+ const TerserPlugin = require("terser-webpack-plugin");
12
+ const webpack = require("webpack");
13
+ const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
12
14
 
13
15
  const { SINGLE_FILE_MODE } = env;
14
16
  const baseDir = dirname(fileURLToPath(import.meta.url));
@@ -1 +0,0 @@
1
- "use strict";(self.webpackChunk_allurereport_web_awesome=self.webpackChunk_allurereport_web_awesome||[]).push([[173],{9173:function(t){t.exports=JSON.parse('{"statuses":{"passed":"წარმატებული","failed":"წარუმატებელი","broken":"დამტვრეული","skipped":"გამოტოვებული","unknown":"უცნობი","total":"ჯამი","flakyTests":"არასტაბილური","newTests":"ახალი","retryTests":"ხელახალი ცდა"},"testSummary":{"total":"ჯამი","flaky":"არასტაბილური ტესტები","retries":"ხელახალი ტესტები","new":"ახალი ტესტები"},"tabs":{"total":"ყველა","results":"შედეგები","globalAttachments":"გლობალური დანართები","globalErrors":"გლობალური შეცდომები","qualityGates":"ხარისხის კარიბჭეები","categories":"კატეგორიები"},"search":{"search":"ძებნა","search-placeholder":"ტესტების ძიება"},"filters":{"flaky":"არასტაბილური","nonFlaky":"სტაბილური","retry":"ხელახალი","new":"ახალი","fixed":"გასწორებული","regressed":"რეგრესია","malfunctioned":"დაზიანებული","transition":"გადასვლა","status":"სტატუსი","severity":"სიმძიმე","owner":"მფლობელი","layer":"ფენა","tags":"ტეგები","categories":"კატეგორიები","goto_filter":"ფილტრზე გადასვლა","errors":{"max_values_one":"ფილტრაციისთვის გამოიყენება მხოლოდ პირველი {{count}} მნიშვნელობა.\\nდამატებითი მნიშვნელობები იგნორირდება","max_values_other":"ფილტრაციისთვის გამოიყენება მხოლოდ პირველი {{count}} მნიშვნელობა.\\nდამატებითი მნიშვნელობები იგნორირდება"},"description":{"flaky":"არასტაბილური ტესტების ჩვენება","retry":"განმეორებით გაშვებული ტესტების შედეგების ჩვენება","new":"აჩვენებს ტესტების შედეგებს, რომლებიც პირველად ჩანს ამ ანგარიშში","fixed":"აჩვენებს ტესტებს, რომლებიც ახლა წარმატებულია, მაგრამ ადრე იყო \\"წარუმატებელი\\" ან \\"დამტვრეული\\"","regressed":"აჩვენებს ტესტების შედეგებს, რომლებმაც შეცვალეს სტატუსი \\"წარმატებული\\" ან \\"დამტვრეული\\"-დან \\"წარუმატებელი\\"-ზე","malfunctioned":"აჩვენებს ტესტების შედეგებს, რომლებმაც შეცვალეს სტატუსი \\"წარმატებული\\" ან \\"წარუმატებელი\\"-დან \\"დამტვრეული\\"-ზე","tags":"აჩვენებს ტესტების შედეგებს, რომლებსაც აქვთ მითითებული ტეგები","categories":"აჩვენებს ტესტების შედეგებს, რომლებსაც აქვთ მითითებული კატეგორიები"}},"sort-by":{"sort-by-text":"დალაგება:","sort-by-category":"დალაგება","direction-category":"მიმართულება"},"sort-by.values":{"order":"შეკვეთა","alphabet":"ანბანი","duration":"ხანგრძლივობა","status":"სტატუსი"},"sort-by.directions":{"order-desc":"ბოლო – პირველი","order-asc":"პირველი – ბოლო","order-asc-short":"პირველი","order-desc-short":"ბოლო","alphabet-asc":"ა – ჰ","alphabet-desc":"ჰ – ა","alphabet-asc-short":"ა – ჰ","alphabet-desc-short":"ჰ – ა","duration-asc":"1 – 9","duration-desc":"9 – 1","duration-asc-short":"1 – 9","duration-desc-short":"9 – 1","status-asc":"როგორც ფილტრის სიაში","status-desc":"შებრუნებული","status-asc-short":"ჩვეულებრივი","status-desc-short":"შებრუნებული"},"empty":{"no-results":"შედეგები არ არის","no-value":"მნიშვნელობა არ არის","no-value-for":"მნიშვნელობა არ არის: {{entity}}","no-transition":"გადასვლა არ არის","no-layer":"ფენა არ არის","no-owner":"მფლობელი არ არის","no-severity":"სიმძიმე არ არის","no-status":"სტატუსი არ არის","no-environment":"გარემო არ არის","no-flaky":"არასტაბილურობა არ არის","empty-value":"ცარიელი","no-tests-found":"შედეგები არ მოიძებნა","no-message-provided":"შეტყობინება არ არის მოწოდებული","clear-filters":"ფილტრების გასუფთავება","no-attachments-results":"დანართების ინფორმაცია არ არის ხელმისაწვდომი","no-global-errors-results":"გლობალური შეცდომების ინფორმაცია არ არის ხელმისაწვდომი","no-quality-gate-results":"ხარისხის კარიბჭის შედეგები არ არის ხელმისაწვდომი","no-history-results":"ისტორიის ინფორმაცია არ არის ხელმისაწვდომი","no-retries-results":"ხელახალი ცდების ინფორმაცია არ არის ხელმისაწვდომი","no-test-steps-results":"ტესტის ნაბიჯების ინფორმაცია ხელმისაწვდომი არ არის","no-test-case-results":"ტესტის შემთხვევის შედეგები არ არის","no-environments-results":"გარემობის ინფორმაცია არ არის ხელმისაწვდომი","no-categories-results":"კატეგორიები ხელმისაწვდომი არ არის"},"severity":{"blocker":"ბლოკერი","critical":"კრიტიკული","normal":"ჩვეულებრივი","minor":"მნიშვნელობა","trivial":"უმნიშვნელო"},"execution":{"name":"შესრულება","body":"ტესტის შინაარსი","setup":"მომზადება","teardown":"დასრულება"},"environments":{"environment_one":"გარემობა","environment_other":"გარემობები","all":"ყველა"},"ui":{"labels":"ლეიბლები","metadata":"მეტამონაცემები","parameters":"პარამეტრები","category":"კატეგორია","description":"აღწერა","links":"ბმულები","overview":"მიმოხილვა","history":"ისტორია","attachments":"დანართები","retries":"ხელახალი ცდები","environments":"გარემობები","error":"შეცდომა","goToStep":"გადადით საფეხურზე","showLess":"ნაკლების ჩვენება","showMore":"მეტის ჩვენება","copy":"კოპირება","copy-email":"ელფოსტის კოპირება","attempt":"მცდელობა {{attempt}} {{total}}-დან","at":"ზე","variables":"ცვლადები","openPwTrace":"გახსენი Playwright Trace","pwTracePopupBlocked":"ბრაუზერმა დაბლოკა Playwright Trace Viewer-ის გახსნა.","pwTracePopupBlockedHint":"დაუშვით ამომხტარი ფანჯრები და ხელახლა სცადეთ.","finishedAtOriginal":"{{formattedCreatedAt}}, გამოსვლის კოდით {{original}}","finishedAtBoth":"{{formattedCreatedAt}}, გამოსვლის კოდით {{actual}} (თავდაპირველი {{original}})"},"controls":{"newTabAttachment":"დანართის გახსნა ახალ ჩანართში","nextTR":"შემდეგი ტესტის შედეგი","prevTR":"წინა ტესტის შედეგი","downloadAttachment":"დანართის ჩამოტვირთვა","previewAttachment":"წინასწარი ნახვა","viewCode":"კოდის ჩვენება","syntaxHighlight":"სინტაქსის მონიშვნა","backto":"უკან დაბრუნება","clipboard":"კოპირება ბუფერში","clipboardError":"ბუფერში კოპირება ვერ მოხერხდა. შესაძლოა, ფუნქცია არ იყოს მხარდაჭერილი თქვენს ბრაუზერში","clipboardSuccess":"წარმატებით დაკოპირდა","collapse":"ჩაკეცვა","expand":"გახსნა","fullscreen":"სრულ ეკრანზე","language":"ენის შეცვლა","openInNewTab":"გახსნა ახალ ჩანართში","openPreview":"გახსენი წინასწარი დათვალიერება","noSelectedTR":"არჩეული ტესტის შედეგი არ არის","comparison":"შედარება","showDiff":"მაჩვენე განსხვავება","viewMode":"ნახვის რეჟიმი","unified":"გაერთიანებული","side-by-side":"გვერდიგვერდ","compareBy":"შედარება","chars":"სიმბოლოები","words":"სიტყვები","lines":"ხაზები","actual":"ნამდვილი","expected":"მოსალოდნელი"},"errors":{"missedAttachment":"დანართი ვერ მოიძებნა"},"sections":{"report":"Report","charts":"გრაფიკები","timeline":"დროის ხაზი"},"timeline":{"empty":"მონაცემები არ არის","empty_host":"მონაცემები არ არის {{ host }}-ისთვის","selected_one":"არჩეულია {{ count }} ტესტი ({{ percentage }}%) {{ minDuration }}-ზე მეტი და {{ maxDuration }}-ზე ნაკლები ხანგრძლივობით","selected_other":"არჩეულია {{ count }} ტესტი ({{ percentage }}%) {{ minDuration }}-ზე მეტი და {{ maxDuration }}-ზე ნაკლები ხანგრძლივობით","host":"ჰოსტი: {{ host }}"},"charts":{"trend":{"title":"ტრენდის გრაფიკი: {{type}}","type":{"status":"სტატუსი","severity":"სიმძიმე"}},"currentStatus":{"title":"მიმდინარე სტატუსი","status":{"passed":"$t(statuses:passed, capitalize)","failed":"$t(statuses:failed, capitalize)","broken":"$t(statuses:broken, capitalize)","skipped":"$t(statuses:skipped, capitalize)","unknown":"$t(statuses:unknown, capitalize)"},"percentage":"{{percentage}}%","of":"{{total}}-დან","total":"სულ","tests":{"new_zero":"ახალი ტესტები არ არის","new_one":"{{count}} ახალი ტესტი","new_other":"{{count}} ახალი ტესტი","flaky_zero":"არასტაბილური ტესტები არ არის","flaky_one":"{{count}} არასტაბილური ტესტი","flaky_other":"{{count}} არასტაბილური ტესტი","retries_zero":"განმეორებითი ტესტები არ არის","retries_one":"{{count}} განმეორებითი ტესტი","retries_other":"{{count}} განმეორებითი ტესტი"}},"statusDynamics":{"title":"სტატუსის დინამიკა","status":{"passed":"$t(statuses:passed, capitalize)","failed":"$t(statuses:failed, capitalize)","broken":"$t(statuses:broken, capitalize)","skipped":"$t(statuses:skipped, capitalize)","unknown":"$t(statuses:unknown, capitalize)"},"no-history":"$t(empty:no-history-results)","no-results":"$t(empty:no-results)","tooltips":{"current":"ბოლო ანგარიში ({{timestamp, timestamp_long_no_seconds}})","history":"{{timestamp, timestamp_long_no_seconds}} ანგარიში"},"ticks":{"current":"ბოლო","history":"{{timestamp, timestamp_date}}"}},"statusTransitions":{"title":"სტატუსის გადასვლები","legend":{"trend":"გამოსწორების მაჩვენებელი"},"transitions":{"new":"$t(transitions:new, capitalize)","fixed":"$t(transitions:fixed, capitalize)","regressed":"$t(transitions:regressed, capitalize)","malfunctioned":"$t(transitions:malfunctioned, capitalize)"},"no-history":"$t(empty:no-history-results)","no-results":"$t(empty:no-results)","tooltips":{"current":"ბოლო ანგარიში ({{timestamp, timestamp_long_no_seconds}})","history":"{{timestamp, timestamp_long_no_seconds}} ანგარიში"},"ticks":{"current":"ბოლო","history":"{{timestamp, timestamp_date}}"}},"durations":{"title":"ხანგრძლივობის ჰისტოგრამა {{groupBy}}-ის მიხედვით","title_none":"ხანგრძლივობის ჰისტოგრამა","no-results":"$t(empty:no-results)","groupBy":{"none":"არცერთი","layer":"ფენა"},"ticks":{"durationRange":"{{from, format_duration}} - {{to, format_duration}}"},"tooltips":{"durationRange":"{{from, format_duration}} - {{to, format_duration}}"},"legend":{"value":"{{value}}","total":"ტესტების რაოდენობა"}},"stabilityDistribution":{"title":"სტაბილურობის განაწილება","no-results":"$t(empty:no-results)","legend":{"stabilityRate":"სტაბილურობის მაჩვენებელი"}},"testBaseGrowthDynamics":{"title":"ტესტების ბაზის ზრდის დინამიკა","status":{"newpassed":"ახალი $t(statuses:passed)","newfailed":"ახალი $t(statuses:failed)","newbroken":"ახალი $t(statuses:broken)","newskipped":"ახალი $t(statuses:skipped)","newunknown":"ახალი $t(statuses:unknown)","removedpassed":"წაშლილი $t(statuses:passed)","removedfailed":"წაშლილი $t(statuses:failed)","removedbroken":"წაშლილი $t(statuses:broken)","removedskipped":"წაშლილი $t(statuses:skipped)","removedunknown":"წაშლილი $t(statuses:unknown)"},"legend":{"trend":"ზრდის ტენდენция"},"no-history":"$t(empty:no-history-results)","no-results":"$t(empty:no-results)","tooltips":{"current":"უახლესი ანგარიში ({{timestamp, timestamp_long_no_seconds}})","history":"ანგარიში {{timestamp, timestamp_long_no_seconds}}"},"ticks":{"current":"უახლესი","history":"{{timestamp, timestamp_date}}"}},"statusAgePyramid":{"title":"ასაკობრივი პირამიდა სტატუსის მიხედვით","status":{"passed":"$t(statuses:passed, capitalize)","failed":"$t(statuses:failed, capitalize)","broken":"$t(statuses:broken, capitalize)","skipped":"$t(statuses:skipped, capitalize)","unknown":"$t(statuses:unknown, capitalize)"},"no-history":"$t(empty:no-history-results)","no-results":"$t(empty:no-results)","tooltips":{"current":"უახლესი ანგარიში ({{timestamp, timestamp_long_no_seconds}})","history":"ანგარიში {{timestamp, timestamp_long_no_seconds}}"},"ticks":{"current":"უახლესი","history":"{{timestamp, timestamp_date}}"}},"trSeverities":{"title":"ტესტის შედეგები სიმძიმის მიხედვით","no-results":"$t(empty:no-results)","status":{"passed":"$t(statuses:passed, capitalize)","failed":"$t(statuses:failed, capitalize)","broken":"$t(statuses:broken, capitalize)","skipped":"$t(statuses:skipped, capitalize)","unknown":"$t(statuses:unknown, capitalize)"},"severity":{"blocker":"$t(severity:blocker, capitalize)","critical":"$t(severity:critical, capitalize)","normal":"$t(severity:normal, capitalize)","minor":"$t(severity:minor, capitalize)","trivial":"$t(severity:trivial, capitalize)","unset":"სიმძიმე არ არის"}},"durationDynamics":{"title":"ხანგრძლივობის დინამიკა","durations":{"sequential":"თანმიმდევრული ხანგრძლივობა","duration":"ტესტის ხანგრძლივობა","speedup":"აჩქარება"},"no-results":"$t(empty:no-results)","tooltips":{"current":"ბოლო ანგარიში ({{timestamp, timestamp_long_no_seconds}})","history":"{{timestamp, timestamp_long_no_seconds}} ანგარიში"},"ticks":{"current":"ბოლო","history":"{{timestamp, timestamp_date}}"},"legend":{"duration":"{{duration, format_duration}}","speedup":"{{speedup}}x"}}},"transitions":{"description":{"new":"ამ ტესტის შედეგის პირველი გამოჩენა ანგარიშში","fixed":"ადრე \\"წარუმატებელი\\" ან \\"დამტვრეული\\" ტესტი, რომელიც ახლა \\"წარმატებული\\"ა","regressed":"ადრე \\"წარმატებული\\" ან \\"დამტვრეული\\" ტესტი, რომელიც ახლა \\"წარუმატებელი\\"ა","malfunctioned":"ადრე \\"წარმატებული\\" ან \\"წარუმატებელი\\" ტესტი, რომელიც ახლა \\"დამტვრეული\\"ა","retries":"{{count}} ხელახალი ცდა","flaky":"შედეგები არასტაბილურია გაშვებებს შორის"},"new":"ახალი","fixed":"გასწორებული","regressed":"რეგრესია","malfunctioned":"დაზიანებული"},"trHistory":{"unknown-date":"უცნობი თარიღი"},"attachments":{"imageDiff":{"mode":{"diff":"სხვაობა","actual":"ფაქტობრივი","expected":"მოსალოდნელი","side-by-side":"გვერდიგვერდ","overlay":"ფენა"},"empty":{"failed-to-load":"სურათების შედარების ჩატვირთვა ვერ მოხერხდა"},"image":{"diff":"სხვაობა","actual":"ფაქტობრივი","expected":"მოსალოდნელი"}}},"shortcuts":{"title":"კლავიატურის მალსახმობები","groups":{"global":"ზოგადი","tree":"ხე","testResult":"ტესტის შედეგი"},"items":{"toggleHelp":"მალსახმობების ჩვენება ან დამალვა","focusSearch":"ძიებაზე ფოკუსირება","blurSearch":"ძიებიდან გასვლა","focusTree":"ხის პანელზე ფოკუსირება","focusTestResult":"ტესტის შედეგის პანელზე ფოკუსირება","toggleLayout":"გაყოფილი ხედის გადართვა (Shift+L)","toggleLayoutAlt":"გაყოფილი ხედის გადართვა (Ctrl/Cmd+\\\\)","moveUp":"ზევით გადაადგილება","moveDown":"ქვევით გადაადგილება","collapse":"ჩაკეცვა / მშობელზე გადასვლა","expand":"გაშლა / პირველი შვილი","parent":"მშობელზე გადასვლა (p)","parentAlt":"მშობელზე გადასვლა (u)","openNode":"ტესტის გახსნა / ჯგუფის გადართვა (o)","collapseNode":"კვანძის ჩაკეცვა (-)","expandNode":"კვანძის გაშლა (+)","collapseAll":"ყველა შვილის ჩაკეცვა (C)","expandAll":"ყველა შვილის გაშლა (A)","subtreeCycle":"ქვეხის ჩაკეცვა/გაშლის ციკლი — ისრის ღილაკის მსგავსად (>)","subtreeCollapse":"მთელი ქვეხის ჩაკეცვა (Shift+C)","subtreeExpand":"მთელი ქვეხის გაშლა (Shift+A)","subtreeFirstLevel":"მხოლოდ პირველი დონის გაშლა (f)","toggleNode":"ჯგუფის ან გარემოს გადართვა","openTest":"ტესტის შედეგის გახსნა","firstTest":"პირველ ტესტზე გადასვლა (g g), სიის ზევით გადახვევა","lastTest":"ბოლო ტესტზე გადასვლა","treeTop":"პირველი მწკრივი + ზევით გადახვევა (Home)","scrollTreeTop":"ზევით გადახვევა — ცვლადების და ფილტრების ჩვენება (z t)","toggleReportVariables":"ცვლადების ჩვენება/დამალვა","toggleReportMetadata":"მეტამონაცემების ჩვენება/დამალვა","reportTabResults":"ანგარიშის ჩანართი: შედეგები","reportTabCategories":"ანგარიშის ჩანართი: კატეგორიები","reportTabQualityGate":"ანგარიშის ჩანართი: ხარისხის კარიბჭე","reportTabGlobalAttachments":"ანგარიშის ჩანართი: გლობალური დანართები","reportTabGlobalErrors":"ანგარიშის ჩანართი: გლობალური შეცდომები","reportTabNext":"შემდეგი ანგარიშის ჩანართი","reportTabPrev":"წინა ანგარიშის ჩანართი","nextTest":"შემდეგი ტესტის შედეგი","prevTest":"წინა ტესტის შედეგი","testTabNext":"შემდეგი ტესტის ჩანართი","testTabPrev":"წინა ტესტის ჩანართი","tabOverview":"მიმოხილვის ჩანართი","tabHistory":"ისტორიის ჩანართი","tabRetries":"გამეორებების ჩანართი","tabAttachments":"დანართების ჩანართი","tabEnvironments":"გარემოების ჩანართი","toggleLabels":"ლეიბლების ჩვენება/დამალვა","toggleParameters":"პარამეტრების ჩვენება/დამალვა","toggleLinks":"ბმულების ჩვენება/დამალვა","backToTree":"ხეზე დაბრუნება"}}}')}}]);