@oneuptime/common 14.0.2 → 14.0.3

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 (166) hide show
  1. package/Models/DatabaseModels/AlertFeed.ts +2 -0
  2. package/Models/DatabaseModels/IncidentAlert.ts +540 -0
  3. package/Models/DatabaseModels/IncidentFeed.ts +2 -0
  4. package/Models/DatabaseModels/Index.ts +2 -0
  5. package/Models/DatabaseModels/Project.ts +60 -0
  6. package/Models/DatabaseModels/RumApplication.ts +52 -1
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  10. package/Server/Services/IncidentAlertService.ts +1811 -0
  11. package/Server/Services/IncidentService.ts +210 -10
  12. package/Server/Services/IncidentStateTimelineService.ts +24 -0
  13. package/Server/Services/Index.ts +2 -0
  14. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
  15. package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
  16. package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
  17. package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
  18. package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
  19. package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
  20. package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
  21. package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
  22. package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
  23. package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
  24. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
  25. package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
  26. package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
  27. package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
  28. package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
  29. package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
  30. package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
  31. package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
  32. package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
  33. package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
  34. package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
  35. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
  36. package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
  37. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
  38. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
  39. package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
  40. package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
  41. package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
  42. package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
  43. package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
  44. package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
  45. package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
  46. package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
  47. package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
  48. package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
  49. package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
  50. package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
  51. package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
  52. package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
  53. package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
  54. package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
  55. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
  56. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
  57. package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
  58. package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
  59. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
  60. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
  61. package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
  62. package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
  63. package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
  64. package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
  65. package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
  66. package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
  67. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
  68. package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
  69. package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
  70. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
  71. package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
  72. package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
  73. package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
  74. package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
  75. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
  76. package/Tests/Types/Rum/WebVitals.test.ts +173 -0
  77. package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
  78. package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
  79. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
  80. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
  81. package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
  82. package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
  83. package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
  84. package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
  85. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
  86. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
  87. package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
  88. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
  89. package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
  90. package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
  91. package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
  92. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
  93. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
  94. package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
  95. package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
  96. package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
  97. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  98. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  99. package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
  100. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
  101. package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
  102. package/Types/Incident/IncidentAlertLink.ts +38 -0
  103. package/Types/Permission.ts +48 -0
  104. package/Types/Rum/SessionReplay.ts +25 -6
  105. package/Types/Rum/WebVitals.ts +52 -0
  106. package/UI/Components/Detail/FieldLabel.tsx +8 -0
  107. package/UI/Components/InfoCard/InfoCard.tsx +47 -1
  108. package/UI/Components/ModelTable/BaseModelTable.tsx +95 -17
  109. package/UI/Components/ModelTable/Column.ts +5 -0
  110. package/UI/Components/Table/TableHeader.tsx +47 -2
  111. package/UI/Components/Table/Types/Column.ts +7 -0
  112. package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
  113. package/UI/Utils/PermissionGate.ts +11 -2
  114. package/Utils/Incident/IncidentFromAlerts.ts +516 -0
  115. package/Utils/Rum/SessionTraceState.ts +212 -0
  116. package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
  117. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  118. package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
  119. package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
  120. package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
  121. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  122. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  123. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  124. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  125. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  126. package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
  127. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
  130. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
  131. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
  132. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  133. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  134. package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
  135. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
  136. package/build/dist/Server/Services/IncidentService.js +164 -12
  137. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  138. package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -0
  139. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  140. package/build/dist/Server/Services/Index.js +2 -0
  141. package/build/dist/Server/Services/Index.js.map +1 -1
  142. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
  143. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
  144. package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
  145. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
  146. package/build/dist/Types/Permission.js +42 -0
  147. package/build/dist/Types/Permission.js.map +1 -1
  148. package/build/dist/Types/Rum/WebVitals.js +25 -0
  149. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  150. package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
  151. package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
  152. package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
  153. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  154. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +66 -14
  155. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  156. package/build/dist/UI/Components/Table/TableHeader.js +18 -2
  157. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  158. package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
  159. package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
  160. package/build/dist/UI/Utils/PermissionGate.js +4 -2
  161. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  162. package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
  163. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
  164. package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
  165. package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
  166. package/package.json +1 -1
@@ -0,0 +1,320 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ act,
4
+ cleanup,
5
+ fireEvent,
6
+ render,
7
+ screen,
8
+ within,
9
+ } from "@testing-library/react";
10
+ import * as React from "react";
11
+ import {
12
+ afterEach,
13
+ beforeEach,
14
+ describe,
15
+ expect,
16
+ jest,
17
+ test,
18
+ } from "@jest/globals";
19
+ import KubernetesContainersTab from "../../../../App/FeatureSet/Dashboard/src/Components/Kubernetes/KubernetesContainersTab";
20
+ import { KUBERNETES_RESOURCE_METRIC_DESCRIPTIONS } from "../../../../App/FeatureSet/Dashboard/src/Components/MetricDescriptions/KubernetesResourceMetricDescriptions";
21
+ import {
22
+ KubernetesContainerSpec,
23
+ KubernetesContainerStatus,
24
+ } from "../../../Types/Kubernetes/KubernetesObjectParser";
25
+
26
+ /*
27
+ * A pod's Containers tab: one card per container with State / Ready /
28
+ * Restarts tiles and its resource Requests and Limits. Each of those now
29
+ * carries an (i) that says what the value means - "Limits" in particular is
30
+ * where a customer learns that crossing the memory limit gets the container
31
+ * killed. Rendered for real; tippy is portalled to document.body and its box
32
+ * never finishes animating under jsdom, so presence is asserted.
33
+ */
34
+
35
+ const D: typeof KUBERNETES_RESOURCE_METRIC_DESCRIPTIONS =
36
+ KUBERNETES_RESOURCE_METRIC_DESCRIPTIONS;
37
+
38
+ function container(
39
+ overrides: Partial<KubernetesContainerSpec> = {},
40
+ ): KubernetesContainerSpec {
41
+ return {
42
+ name: "app",
43
+ image: "nginx:1.27",
44
+ command: [],
45
+ args: [],
46
+ env: [{ name: "MODE", value: "production" }],
47
+ ports: [],
48
+ resources: {
49
+ requests: { cpu: "250m", memory: "256Mi" },
50
+ limits: { cpu: "1", memory: "512Mi" },
51
+ },
52
+ volumeMounts: [],
53
+ ...overrides,
54
+ };
55
+ }
56
+
57
+ function status(
58
+ overrides: Partial<KubernetesContainerStatus> = {},
59
+ ): KubernetesContainerStatus {
60
+ return {
61
+ name: "app",
62
+ ready: true,
63
+ restartCount: 3,
64
+ state: "running",
65
+ reason: "",
66
+ image: "nginx:1.27",
67
+ ...overrides,
68
+ };
69
+ }
70
+
71
+ async function tooltipFor(label: string): Promise<string> {
72
+ const button: HTMLElement = screen.getByRole("button", {
73
+ name: `About ${label}`,
74
+ });
75
+
76
+ fireEvent.mouseEnter(button);
77
+ await act(async () => {
78
+ jest.advanceTimersByTime(200);
79
+ });
80
+
81
+ const tooltips: Array<HTMLElement> = screen.getAllByRole("tooltip");
82
+ const text: string = tooltips[tooltips.length - 1]?.textContent || "";
83
+
84
+ fireEvent.mouseLeave(button);
85
+ await act(async () => {
86
+ jest.advanceTimersByTime(500);
87
+ });
88
+
89
+ return text;
90
+ }
91
+
92
+ beforeEach(() => {
93
+ jest.useFakeTimers();
94
+ });
95
+
96
+ afterEach(() => {
97
+ cleanup();
98
+ jest.useRealTimers();
99
+ });
100
+
101
+ describe("KubernetesContainersTab: every number on a container card is explained", () => {
102
+ test("State, Ready, Restarts, Requests and Limits each have an (i)", () => {
103
+ render(
104
+ <KubernetesContainersTab
105
+ containers={[container()]}
106
+ initContainers={[]}
107
+ containerStatuses={[status()]}
108
+ initContainerStatuses={[]}
109
+ />,
110
+ );
111
+
112
+ for (const label of ["State", "Ready", "Restarts", "Requests", "Limits"]) {
113
+ expect(
114
+ screen.getByRole("button", { name: `About ${label}` }),
115
+ ).toBeInTheDocument();
116
+ }
117
+ });
118
+
119
+ test.each([
120
+ ["State", D.containerState],
121
+ ["Ready", D.containerReady],
122
+ ["Restarts", D.containerRestarts],
123
+ ["Requests", D.containerRequests],
124
+ ["Limits", D.containerLimits],
125
+ ])(
126
+ "hovering the %s (i) shows what it means",
127
+ async (label: string, text: string) => {
128
+ render(
129
+ <KubernetesContainersTab
130
+ containers={[container()]}
131
+ initContainers={[]}
132
+ containerStatuses={[status()]}
133
+ initContainerStatuses={[]}
134
+ />,
135
+ );
136
+
137
+ expect(await tooltipFor(label)).toBe(text);
138
+ },
139
+ );
140
+
141
+ test("nothing is explained before someone reaches for it", () => {
142
+ render(
143
+ <KubernetesContainersTab
144
+ containers={[container()]}
145
+ initContainers={[]}
146
+ containerStatuses={[status()]}
147
+ initContainerStatuses={[]}
148
+ />,
149
+ );
150
+
151
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
152
+ expect(screen.queryByText(D.containerLimits)).not.toBeInTheDocument();
153
+ });
154
+
155
+ test("the values the tiles explain are still shown beside them", () => {
156
+ render(
157
+ <KubernetesContainersTab
158
+ containers={[container()]}
159
+ initContainers={[]}
160
+ containerStatuses={[status({ restartCount: 7 })]}
161
+ initContainerStatuses={[]}
162
+ />,
163
+ );
164
+
165
+ expect(screen.getByText("7")).toBeInTheDocument();
166
+ expect(screen.getByText("running")).toBeInTheDocument();
167
+ expect(screen.getByText("Yes")).toBeInTheDocument();
168
+ expect(screen.getByText("250m (0.25 CPU cores)")).toBeInTheDocument();
169
+ expect(screen.getByText("512Mi (512 MB)")).toBeInTheDocument();
170
+ });
171
+
172
+ test("a container with no status yet shows no State / Ready / Restarts (i)", () => {
173
+ render(
174
+ <KubernetesContainersTab
175
+ containers={[container()]}
176
+ initContainers={[]}
177
+ containerStatuses={[]}
178
+ initContainerStatuses={[]}
179
+ />,
180
+ );
181
+
182
+ for (const label of ["State", "Ready", "Restarts"]) {
183
+ expect(
184
+ screen.queryByRole("button", { name: `About ${label}` }),
185
+ ).not.toBeInTheDocument();
186
+ }
187
+
188
+ // Its resources are still explained.
189
+ expect(
190
+ screen.getByRole("button", { name: "About Requests" }),
191
+ ).toBeInTheDocument();
192
+ expect(
193
+ screen.getByRole("button", { name: "About Limits" }),
194
+ ).toBeInTheDocument();
195
+ });
196
+
197
+ test("only the resource sections a container declares get an (i)", () => {
198
+ render(
199
+ <KubernetesContainersTab
200
+ containers={[
201
+ container({
202
+ resources: { requests: { cpu: "100m" }, limits: {} },
203
+ }),
204
+ ]}
205
+ initContainers={[]}
206
+ containerStatuses={[status()]}
207
+ initContainerStatuses={[]}
208
+ />,
209
+ );
210
+
211
+ expect(
212
+ screen.getByRole("button", { name: "About Requests" }),
213
+ ).toBeInTheDocument();
214
+ expect(
215
+ screen.queryByRole("button", { name: "About Limits" }),
216
+ ).not.toBeInTheDocument();
217
+ });
218
+
219
+ test("a container with no requests or limits shows neither (i)", () => {
220
+ render(
221
+ <KubernetesContainersTab
222
+ containers={[container({ resources: { requests: {}, limits: {} } })]}
223
+ initContainers={[]}
224
+ containerStatuses={[status()]}
225
+ initContainerStatuses={[]}
226
+ />,
227
+ );
228
+
229
+ expect(
230
+ screen.queryByRole("button", { name: "About Requests" }),
231
+ ).not.toBeInTheDocument();
232
+ expect(
233
+ screen.queryByRole("button", { name: "About Limits" }),
234
+ ).not.toBeInTheDocument();
235
+ });
236
+
237
+ test("init containers are explained the same way, card by card", () => {
238
+ render(
239
+ <KubernetesContainersTab
240
+ containers={[container()]}
241
+ initContainers={[container({ name: "migrate" })]}
242
+ containerStatuses={[status()]}
243
+ initContainerStatuses={[
244
+ status({ name: "migrate", state: "terminated", ready: false }),
245
+ ]}
246
+ />,
247
+ );
248
+
249
+ // One set per card: the regular container and the init container.
250
+ for (const label of ["State", "Ready", "Restarts", "Requests", "Limits"]) {
251
+ expect(
252
+ screen.getAllByRole("button", { name: `About ${label}` }),
253
+ ).toHaveLength(2);
254
+ }
255
+ });
256
+
257
+ test("no (i) sits inside another button or a link", () => {
258
+ render(
259
+ <KubernetesContainersTab
260
+ containers={[container()]}
261
+ initContainers={[]}
262
+ containerStatuses={[status()]}
263
+ initContainerStatuses={[]}
264
+ />,
265
+ );
266
+
267
+ const infoButtons: Array<HTMLElement> = screen.getAllByRole("button", {
268
+ name: /^About /,
269
+ });
270
+
271
+ expect(infoButtons).toHaveLength(5);
272
+
273
+ for (const button of infoButtons) {
274
+ expect(button.parentElement?.closest("button, a")).toBeNull();
275
+ }
276
+ });
277
+
278
+ test("clicking an (i) does not toggle the card's expandable sections", () => {
279
+ render(
280
+ <KubernetesContainersTab
281
+ containers={[container()]}
282
+ initContainers={[]}
283
+ containerStatuses={[status()]}
284
+ initContainerStatuses={[]}
285
+ />,
286
+ );
287
+
288
+ fireEvent.click(screen.getByRole("button", { name: "About Limits" }));
289
+
290
+ // The env vars stay collapsed: the toggle still reads "▶".
291
+ const envToggle: HTMLElement = screen.getByRole("button", {
292
+ name: /Environment Variables/,
293
+ });
294
+
295
+ expect(within(envToggle).getByText("▶")).toBeInTheDocument();
296
+ expect(screen.queryByText("production")).not.toBeInTheDocument();
297
+ });
298
+
299
+ test("the Requests and Limits (i)s sit beside their headings", () => {
300
+ render(
301
+ <KubernetesContainersTab
302
+ containers={[container()]}
303
+ initContainers={[]}
304
+ containerStatuses={[status()]}
305
+ initContainerStatuses={[]}
306
+ />,
307
+ );
308
+
309
+ for (const label of ["Requests", "Limits"]) {
310
+ const heading: HTMLElement = screen.getByText(label, {
311
+ selector: "span",
312
+ });
313
+ const button: HTMLElement = screen.getByRole("button", {
314
+ name: `About ${label}`,
315
+ });
316
+
317
+ expect(heading.parentElement).toContainElement(button);
318
+ }
319
+ });
320
+ });