@oneuptime/common 14.0.8 → 14.0.9

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 (639) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  4. package/Models/DatabaseModels/Project.ts +55 -5
  5. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  6. package/Models/DatabaseModels/RunnerJob.ts +75 -0
  7. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  8. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  9. package/Server/API/AIChatAPI.ts +6 -3
  10. package/Server/API/AIInvestigationAPI.ts +17 -10
  11. package/Server/API/AutoRemediationAPI.ts +207 -25
  12. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  13. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  14. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  15. package/Server/Infrastructure/GlobalCache.ts +32 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/TelemetryIngest.ts +41 -18
  21. package/Server/Services/AIService.ts +70 -1
  22. package/Server/Services/AlertService.ts +352 -11
  23. package/Server/Services/AlertSeverityService.ts +65 -15
  24. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  25. package/Server/Services/IncidentService.ts +280 -104
  26. package/Server/Services/IncidentSeverityService.ts +66 -15
  27. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  28. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  29. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  30. package/Server/Services/KubernetesClusterService.ts +129 -21
  31. package/Server/Services/ProjectService.ts +32 -0
  32. package/Server/Services/ProxmoxResourceService.ts +322 -5
  33. package/Server/Services/RunnerJobService.ts +241 -104
  34. package/Server/Services/RunnerService.ts +1 -1
  35. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  36. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  37. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  38. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  39. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  40. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  41. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
  42. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
  43. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
  44. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  45. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  46. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  47. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
  48. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  49. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  50. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  51. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  52. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  53. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
  54. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  55. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  56. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  57. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  58. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  59. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  60. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  61. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  62. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  63. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  64. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  65. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  66. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  67. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  68. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  69. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  70. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  71. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  72. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
  73. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  74. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  75. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  76. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  77. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  78. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  79. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  80. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  81. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  82. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  83. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  84. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  85. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  86. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  87. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  88. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  89. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  90. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  91. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  92. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  93. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  94. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  95. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  96. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  97. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  98. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  99. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  100. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  101. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  102. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  103. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  104. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  105. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  106. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  107. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  108. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  109. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  110. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  111. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  112. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  113. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  114. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  115. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  116. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  117. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  118. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  119. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  120. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  121. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  122. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  123. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  124. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  125. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  126. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  127. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  128. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  129. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  130. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  131. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  132. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  133. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  134. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  135. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  136. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  137. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  138. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  139. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  140. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  141. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  142. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  143. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  144. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  145. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  146. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  147. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  148. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  149. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  150. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  151. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  152. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  153. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  154. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  155. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  156. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  157. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  158. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  159. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  160. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  161. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  162. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  163. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  164. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  165. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  166. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  167. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  168. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  169. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  170. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  171. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  172. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  173. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  174. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  175. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  176. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  177. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  178. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  179. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  180. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  181. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  182. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  183. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  184. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  185. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  186. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  187. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  188. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  189. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  190. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  191. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  192. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  193. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  194. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  195. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  196. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  197. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  198. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  199. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  200. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  201. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  202. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  203. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  204. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  205. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  206. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  207. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  208. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  209. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  210. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  211. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  212. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  213. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  214. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  215. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  216. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  217. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  218. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  219. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  220. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  221. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  222. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  223. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  224. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  225. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  226. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  227. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
  228. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
  229. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
  230. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  231. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  232. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  233. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  234. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  235. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  236. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  237. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  238. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  239. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  240. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  241. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  242. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  243. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  244. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  245. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  246. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  247. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  248. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  249. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  250. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  251. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  252. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  253. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
  254. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  255. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  256. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  257. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  258. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  259. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  260. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  261. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  262. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  263. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  264. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  265. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  266. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  267. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  268. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  269. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  270. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  271. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  272. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  273. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  274. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  275. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  276. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  277. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  278. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  279. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  280. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  281. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  282. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  283. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  284. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  285. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  286. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
  287. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  288. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  289. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  290. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  291. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  292. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  293. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  294. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  295. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  296. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  297. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  298. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  299. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  300. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  301. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  302. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  303. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  304. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  305. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  306. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  307. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  308. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  309. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  310. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  311. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  312. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  313. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  314. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  315. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  316. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  317. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  318. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  319. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  320. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  321. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  322. package/Tests/UI/Components/Input.test.tsx +80 -1
  323. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  324. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  325. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  326. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  327. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  328. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  329. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  330. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  331. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  332. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  333. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  334. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  335. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  336. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  337. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  338. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  339. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  340. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  341. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  342. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  343. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  344. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  345. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  346. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  347. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  348. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  349. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  350. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  351. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  352. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  353. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  354. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  355. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  356. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  357. package/Types/AI/AIRunType.ts +9 -4
  358. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  359. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
  360. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  361. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  362. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  363. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  364. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  365. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  366. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  367. package/Types/Team/ComplianceRule.ts +434 -0
  368. package/Types/Team/ComplianceRuleType.ts +21 -0
  369. package/Types/Team/TeamComplianceStatus.ts +81 -0
  370. package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
  371. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  372. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  373. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  374. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  375. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  376. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  377. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  378. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  379. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  380. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  381. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  382. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  383. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  384. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  385. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  386. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  387. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  388. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  389. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  390. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  391. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  392. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  393. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  394. package/UI/Components/Input/Input.tsx +90 -57
  395. package/UI/Components/List/ListRow.tsx +8 -74
  396. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  397. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  398. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  399. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  400. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  401. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  402. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  403. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  404. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  405. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  406. package/UI/Components/Table/TableBody.tsx +8 -2
  407. package/UI/Components/Table/TableRow.tsx +20 -140
  408. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  409. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  410. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  411. package/UI/Components/TelemetryViewer/types.ts +2 -0
  412. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  413. package/UI/Components/Toggle/Toggle.tsx +26 -0
  414. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  415. package/UI/Styles/Theme.css +14 -2
  416. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  417. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  418. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  419. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  420. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  421. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  422. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  423. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  424. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  425. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  426. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  427. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  428. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  429. package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
  430. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  431. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  432. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  433. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  434. package/build/dist/Server/API/AIChatAPI.js +6 -1
  435. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  436. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  437. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  438. package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
  439. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  440. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  441. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  442. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  443. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  444. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  445. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  446. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  447. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  448. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  449. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  450. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  451. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  452. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  453. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  454. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  455. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  456. package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
  457. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  458. package/build/dist/Server/Services/AIService.js +59 -1
  459. package/build/dist/Server/Services/AIService.js.map +1 -1
  460. package/build/dist/Server/Services/AlertService.js +248 -13
  461. package/build/dist/Server/Services/AlertService.js.map +1 -1
  462. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  463. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  464. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  465. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  466. package/build/dist/Server/Services/IncidentService.js +199 -72
  467. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  468. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  469. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  470. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  471. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  472. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  473. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  474. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  475. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  476. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  477. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  478. package/build/dist/Server/Services/ProjectService.js +22 -0
  479. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  480. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  481. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  482. package/build/dist/Server/Services/RunnerJobService.js +178 -92
  483. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  484. package/build/dist/Server/Services/RunnerService.js +1 -1
  485. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  486. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  487. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  488. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  489. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  490. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  491. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  492. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  493. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  494. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  495. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  496. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  497. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  498. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
  499. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  500. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
  501. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  502. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
  503. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  504. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  505. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  506. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  507. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  508. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  509. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
  510. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  512. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  513. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  514. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  515. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  516. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  517. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  518. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  519. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  520. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  521. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
  522. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  523. package/build/dist/Types/AI/AIRunType.js +9 -4
  524. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  525. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
  526. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  527. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  528. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  529. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  530. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  531. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  532. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  533. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  534. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  535. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  536. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  537. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  538. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  539. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  540. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  541. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  542. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  543. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  544. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  545. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
  546. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  547. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  548. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  549. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  550. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  551. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  552. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  553. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  554. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  555. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  556. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  557. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  558. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  559. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  560. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  561. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  562. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  563. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  564. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  565. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  566. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  567. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  568. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  569. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  570. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  571. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  572. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  573. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  574. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  575. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  576. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  577. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  578. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  579. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  580. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  581. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  582. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  583. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  584. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  585. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  586. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  587. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  588. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  589. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  590. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  591. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  592. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  593. package/build/dist/UI/Components/Input/Input.js +69 -54
  594. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  595. package/build/dist/UI/Components/List/ListRow.js +4 -39
  596. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  597. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  598. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  600. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  601. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  602. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  603. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  604. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  605. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  606. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  607. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  608. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  609. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  610. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  611. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  612. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  613. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  614. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  615. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  616. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  617. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  618. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  619. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  620. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  621. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  622. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  626. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  627. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  628. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  629. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  630. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  631. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  632. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  633. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  634. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  635. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  636. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  637. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  638. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  639. package/package.json +1 -1
@@ -0,0 +1,1217 @@
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 getJestMockFunction, { MockFunction } from "../../MockType";
20
+ import {
21
+ ChartZoomRecord,
22
+ chartZoomStandIns,
23
+ doubleClickOn as doubleClickOnChartIn,
24
+ dragAcross as dragAcrossChartIn,
25
+ resetChartZoomStandIns,
26
+ settle,
27
+ windowKey,
28
+ windowOf,
29
+ } from "./ChartZoomHarness";
30
+
31
+ /*
32
+ * Issue #4105, on the page in the customer's screenshot: the Kubernetes
33
+ * cluster Overview. Dragging across any of its charts (Availability, CPU,
34
+ * Memory, Filesystem, Network) must narrow the WHOLE page to the window
35
+ * dragged out - every chart, every tile, the uptime badge, and the picker,
36
+ * which then reads as a custom range with a "Reset zoom" button beside it.
37
+ * A double-click on any chart (not just the one dragged on) must put the
38
+ * page back on the range it had before the first zoom, however many zooms
39
+ * deep the reader went.
40
+ *
41
+ * The page is rendered whole with its data sources stubbed. The metric
42
+ * aggregates answer with a minute-by-minute series for exactly the window
43
+ * each query asks for, so the tests can see which window every chart and
44
+ * tile was drawn from. The line chart is stood in for (see
45
+ * ChartZoomHarness): the stand-in resolves its zoom handlers the way the
46
+ * real wrapper does - its host's, else the page's for a time axis - and
47
+ * exposes them as a "drag" button and a double-clickable plot. The zoom
48
+ * scope, the picker and its Reset zoom button are real.
49
+ */
50
+
51
+ type AggregateWindow = { name: string; start: Date; end: Date };
52
+
53
+ type AggregateRequest = {
54
+ aggregateBy: {
55
+ query: { name: string };
56
+ startTimestamp: Date;
57
+ endTimestamp: Date;
58
+ };
59
+ };
60
+
61
+ const mockAggregate: MockFunction = getJestMockFunction();
62
+ const mockInventorySummary: MockFunction = getJestMockFunction();
63
+ const mockFetchTopPods: MockFunction = getJestMockFunction();
64
+ const mockFetchWarnings: MockFunction = getJestMockFunction();
65
+
66
+ jest.mock("../../../UI/Utils/Navigation", () => {
67
+ return {
68
+ __esModule: true,
69
+ default: {
70
+ getLastParamAsObjectID: () => {
71
+ const { default: ObjectIDType } = jest.requireActual(
72
+ "../../../Types/ObjectID",
73
+ ) as { default: new (id: string) => unknown };
74
+ return new ObjectIDType("0193c0de-7777-4aaa-8bbb-000000000007");
75
+ },
76
+ navigate: () => {
77
+ return undefined;
78
+ },
79
+ },
80
+ };
81
+ });
82
+
83
+ jest.mock("../../../UI/Utils/Project", () => {
84
+ return {
85
+ __esModule: true,
86
+ default: {
87
+ getCurrentProjectId: () => {
88
+ const { default: ObjectIDType } = jest.requireActual(
89
+ "../../../Types/ObjectID",
90
+ ) as { default: new (id: string) => unknown };
91
+ return new ObjectIDType("10000000-0000-4000-8000-000000000001");
92
+ },
93
+ },
94
+ };
95
+ });
96
+
97
+ // The inventory summary: a "now" snapshot that must not follow the zoom.
98
+ jest.mock("../../../UI/Utils/API/API", () => {
99
+ return {
100
+ __esModule: true,
101
+ default: {
102
+ post: (...args: Array<unknown>) => {
103
+ return mockInventorySummary(...args);
104
+ },
105
+ getFriendlyMessage: (error: unknown) => {
106
+ return (
107
+ ((error as { message?: unknown } | null)?.message as string) ||
108
+ "Could not load"
109
+ );
110
+ },
111
+ },
112
+ };
113
+ });
114
+
115
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
116
+ return {
117
+ __esModule: true,
118
+ default: {
119
+ getItem: () => {
120
+ return Promise.resolve({
121
+ name: "production-us-east",
122
+ clusterIdentifier: "production-us-east-1",
123
+ otelCollectorStatus: "connected",
124
+ lastSeenAt: new Date("2026-09-28T11:59:00.000Z"),
125
+ });
126
+ },
127
+ getCommonHeaders: () => {
128
+ return {};
129
+ },
130
+ },
131
+ };
132
+ });
133
+
134
+ jest.mock("../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI", () => {
135
+ return {
136
+ __esModule: true,
137
+ default: {
138
+ aggregate: (...args: Array<unknown>) => {
139
+ return mockAggregate(...args);
140
+ },
141
+ },
142
+ };
143
+ });
144
+
145
+ jest.mock(
146
+ "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/Utils/KubernetesResourceUtils",
147
+ () => {
148
+ const actual: {
149
+ default: {
150
+ formatCpuValue: (value: number | null) => string;
151
+ formatMemoryValue: (bytes: number | null) => string;
152
+ };
153
+ } = jest.requireActual(
154
+ "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/Utils/KubernetesResourceUtils",
155
+ ) as {
156
+ default: {
157
+ formatCpuValue: (value: number | null) => string;
158
+ formatMemoryValue: (bytes: number | null) => string;
159
+ };
160
+ };
161
+
162
+ return {
163
+ __esModule: true,
164
+ default: {
165
+ formatCpuValue: actual.default.formatCpuValue,
166
+ formatMemoryValue: actual.default.formatMemoryValue,
167
+ fetchResourceListWithMemory: (...args: Array<unknown>) => {
168
+ return mockFetchTopPods(...args);
169
+ },
170
+ fetchNodeAllocatableMemory: () => {
171
+ return Promise.resolve(new Map<string, number>());
172
+ },
173
+ },
174
+ };
175
+ },
176
+ );
177
+
178
+ jest.mock(
179
+ "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/Utils/KubernetesObjectFetcher",
180
+ () => {
181
+ return {
182
+ __esModule: true,
183
+ ...(jest.requireActual(
184
+ "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/Utils/KubernetesObjectFetcher",
185
+ ) as Record<string, unknown>),
186
+ fetchClusterWarningEvents: (...args: Array<unknown>) => {
187
+ return mockFetchWarnings(...args);
188
+ },
189
+ };
190
+ },
191
+ );
192
+
193
+ jest.mock(
194
+ "../../../../App/FeatureSet/Dashboard/src/Components/ResourceActivity/ResourceActivityCards",
195
+ () => {
196
+ return {
197
+ __esModule: true,
198
+ default: () => {
199
+ return <div data-testid="resource-activity-cards" />;
200
+ },
201
+ };
202
+ },
203
+ );
204
+
205
+ /*
206
+ * The hero's refresh control, minus its animation-frame countdown: it still
207
+ * renders the page's picker where the page puts it, and a Refresh button.
208
+ */
209
+ jest.mock(
210
+ "../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/AutoRefreshControl",
211
+ () => {
212
+ return {
213
+ __esModule: true,
214
+ default: (props: {
215
+ timeRangePicker?: React.ReactElement | undefined;
216
+ onManualRefresh: () => void;
217
+ isRefreshing: boolean;
218
+ }) => {
219
+ return (
220
+ <div
221
+ data-testid="auto-refresh-control"
222
+ data-refreshing={String(props.isRefreshing)}
223
+ >
224
+ {props.timeRangePicker}
225
+ <button type="button" onClick={props.onManualRefresh}>
226
+ Refresh now
227
+ </button>
228
+ </div>
229
+ );
230
+ },
231
+ };
232
+ },
233
+ );
234
+
235
+ jest.mock("../../../UI/Components/ModelDetail/CardModelDetail", () => {
236
+ return {
237
+ __esModule: true,
238
+ default: () => {
239
+ return <div data-testid="cluster-details" />;
240
+ },
241
+ };
242
+ });
243
+
244
+ jest.mock("../../../UI/Components/Charts/Line/LineChart", () => {
245
+ return {
246
+ __esModule: true,
247
+ default: (
248
+ jest.requireActual(
249
+ "./ChartZoomHarness",
250
+ ) as typeof import("./ChartZoomHarness")
251
+ ).StandInLineChart,
252
+ };
253
+ });
254
+
255
+ import KubernetesClusterOverview, {
256
+ ClusterChartCard,
257
+ } from "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Index";
258
+ import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
259
+ import { TimeRangeZoomProvider } from "../../../UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext";
260
+ import { TimeRangeZoom } from "../../../UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom";
261
+ import { RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID } from "../../../UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton";
262
+ import { TIME_RANGE_ZOOM_HINT_TEST_ID } from "../../../UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint";
263
+ import { getTimeRangeButtonLabel } from "../../../UI/Components/Date/TimeRangePickerDropdown";
264
+ import { TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX } from "../../../UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker";
265
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
266
+ import IconProp from "../../../Types/Icon/IconProp";
267
+ import TimeRange from "../../../Types/Time/TimeRange";
268
+
269
+ const PAGE_PROPS: PageComponentProps = {} as PageComponentProps;
270
+
271
+ const REFRESH_STORAGE_KEY: string = "kubernetes-overview-auto-refresh-interval";
272
+
273
+ const MINUTE_MS: number = 60 * 1000;
274
+
275
+ const NOW: Date = new Date("2026-09-28T12:00:00.000Z");
276
+ const PAST_THIRTY_MINUTES_START: Date = new Date("2026-09-28T11:30:00.000Z");
277
+
278
+ const DRAG_START: Date = new Date("2026-09-28T11:40:00.000Z");
279
+ const DRAG_END: Date = new Date("2026-09-28T11:50:00.000Z");
280
+ const NESTED_DRAG_START: Date = new Date("2026-09-28T11:42:00.000Z");
281
+ const NESTED_DRAG_END: Date = new Date("2026-09-28T11:45:00.000Z");
282
+
283
+ const CHART_TITLES: Array<string> = [
284
+ "Availability",
285
+ "CPU",
286
+ "Memory",
287
+ "Filesystem",
288
+ "Network",
289
+ ];
290
+
291
+ // The six queries a golden-metrics load issues for the page's window.
292
+ const GOLDEN_METRICS: Array<string> = [
293
+ "k8s.node.cpu.utilization",
294
+ "k8s.node.memory.usage",
295
+ "k8s.node.filesystem.usage",
296
+ "k8s.node.filesystem.available",
297
+ "k8s.node.network.io",
298
+ "oneuptime.host.heartbeat",
299
+ ];
300
+
301
+ const ALLOCATABLE_CPU_METRIC: string = "k8s.node.allocatable_cpu";
302
+
303
+ const NODE_ATTRIBUTES: Record<string, string> = {
304
+ "resource.k8s.node.name": "node-a",
305
+ };
306
+
307
+ const ALLOCATABLE_CORES: number = 4;
308
+
309
+ /*
310
+ * Cores in use at a minute: its minute-of-the-hour / 50, so the CPU tile
311
+ * (the mean of a window's last five minutes) reads differently for every
312
+ * window and shows which window it was computed from:
313
+ * Past 30 Minutes (11:30-12:00): minutes 55-59 -> 1.14 cores -> 28.5%
314
+ * the drag (11:40-11:50): minutes 45-49 -> 0.94 cores -> 23.5%
315
+ * the nested drag (11:42-11:45): minutes 42-44 -> 0.86 cores -> 21.5%
316
+ */
317
+ function coresInUseAt(time: number): number {
318
+ return new Date(time).getUTCMinutes() / 50;
319
+ }
320
+
321
+ /*
322
+ * The agent went quiet from 11:50 to 11:57, so the uptime badge reads
323
+ * differently for every window too:
324
+ * Past 30 Minutes: 7 of the 30 minutes it can judge down -> 76.67%
325
+ * (the minute still filling up counts once a heartbeat is in)
326
+ * the drag (11:40-11:50): all 10 minutes up -> 100.0%
327
+ */
328
+ const OUTAGE_START: Date = new Date("2026-09-28T11:50:00.000Z");
329
+ const OUTAGE_END: Date = new Date("2026-09-28T11:57:00.000Z");
330
+ const UPTIME_PAST_THIRTY_MINUTES: string = "76.67% uptime";
331
+ const UPTIME_DRAG: string = "100.0% uptime";
332
+
333
+ const CPU_TILE_PAST_THIRTY_MINUTES: string = "28.5%";
334
+ const CPU_TILE_DRAG: string = "23.5%";
335
+ const CPU_TILE_NESTED_DRAG: string = "21.5%";
336
+
337
+ function minuteStartsWithin(start: Date, end: Date): Array<number> {
338
+ const minutes: Array<number> = [];
339
+ const first: number = Math.ceil(start.getTime() / MINUTE_MS) * MINUTE_MS;
340
+
341
+ for (let time: number = first; time < end.getTime(); time += MINUTE_MS) {
342
+ minutes.push(time);
343
+ }
344
+
345
+ return minutes;
346
+ }
347
+
348
+ /*
349
+ * One row per minute of exactly the window asked for, shaped like each
350
+ * metric's real rows.
351
+ */
352
+ function rowsFor(request: AggregateRequest): Array<Record<string, unknown>> {
353
+ const name: string = request.aggregateBy.query.name;
354
+ const minutes: Array<number> = minuteStartsWithin(
355
+ new Date(request.aggregateBy.startTimestamp),
356
+ new Date(request.aggregateBy.endTimestamp),
357
+ );
358
+
359
+ switch (name) {
360
+ case ALLOCATABLE_CPU_METRIC:
361
+ return [
362
+ {
363
+ timestamp: new Date(request.aggregateBy.endTimestamp),
364
+ value: ALLOCATABLE_CORES,
365
+ attributes: NODE_ATTRIBUTES,
366
+ },
367
+ ];
368
+ case "k8s.node.cpu.utilization":
369
+ return minutes.map((time: number): Record<string, unknown> => {
370
+ return {
371
+ timestamp: new Date(time),
372
+ value: coresInUseAt(time),
373
+ attributes: NODE_ATTRIBUTES,
374
+ };
375
+ });
376
+ case "k8s.node.memory.usage":
377
+ return minutes.map((time: number): Record<string, unknown> => {
378
+ return {
379
+ timestamp: new Date(time),
380
+ value: 2 * 1024 * 1024 * 1024,
381
+ attributes: NODE_ATTRIBUTES,
382
+ };
383
+ });
384
+ case "k8s.node.filesystem.usage":
385
+ case "k8s.node.filesystem.available":
386
+ return minutes.map((time: number): Record<string, unknown> => {
387
+ return {
388
+ timestamp: new Date(time),
389
+ value: name === "k8s.node.filesystem.usage" ? 30 : 70,
390
+ attributes: NODE_ATTRIBUTES,
391
+ };
392
+ });
393
+ case "k8s.node.network.io":
394
+ return minutes.flatMap((time: number): Array<Record<string, unknown>> => {
395
+ return ["receive", "transmit"].map(
396
+ (direction: string): Record<string, unknown> => {
397
+ return {
398
+ timestamp: new Date(time),
399
+ value: time,
400
+ attributes: {
401
+ ...NODE_ATTRIBUTES,
402
+ direction: direction,
403
+ interface: "eth0",
404
+ },
405
+ };
406
+ },
407
+ );
408
+ });
409
+ case "oneuptime.host.heartbeat":
410
+ return minutes
411
+ .filter((time: number): boolean => {
412
+ return time < OUTAGE_START.getTime() || time >= OUTAGE_END.getTime();
413
+ })
414
+ .map((time: number): Record<string, unknown> => {
415
+ return { timestamp: new Date(time), value: 2 };
416
+ });
417
+ default:
418
+ return [];
419
+ }
420
+ }
421
+
422
+ /*
423
+ * While set, aggregate answers are held back until the test releases them,
424
+ * to play a slow response against a faster, newer one.
425
+ */
426
+ let heldAggregates: Array<{
427
+ release: () => void;
428
+ fail: () => void;
429
+ }> | null = null;
430
+
431
+ function answerAggregate(
432
+ request: AggregateRequest,
433
+ ): Promise<{ data: Array<Record<string, unknown>> }> {
434
+ if (!heldAggregates) {
435
+ return Promise.resolve({ data: rowsFor(request) });
436
+ }
437
+
438
+ const held: Array<{ release: () => void; fail: () => void }> = heldAggregates;
439
+
440
+ return new Promise(
441
+ (
442
+ resolve: (value: { data: Array<Record<string, unknown>> }) => void,
443
+ reject: (reason: Error) => void,
444
+ ) => {
445
+ held.push({
446
+ release: () => {
447
+ resolve({ data: rowsFor(request) });
448
+ },
449
+ fail: () => {
450
+ reject(new Error("The old window failed to load"));
451
+ },
452
+ });
453
+ },
454
+ );
455
+ }
456
+
457
+ function aggregateWindows(): Array<AggregateWindow> {
458
+ return mockAggregate.mock.calls.map(
459
+ (call: Array<unknown>): AggregateWindow => {
460
+ const request: AggregateRequest = call[0] as AggregateRequest;
461
+ return {
462
+ name: request.aggregateBy.query.name,
463
+ start: new Date(request.aggregateBy.startTimestamp),
464
+ end: new Date(request.aggregateBy.endTimestamp),
465
+ };
466
+ },
467
+ );
468
+ }
469
+
470
+ function goldenWindows(): Array<AggregateWindow> {
471
+ return aggregateWindows().filter((window: AggregateWindow): boolean => {
472
+ return GOLDEN_METRICS.includes(window.name);
473
+ });
474
+ }
475
+
476
+ // The window each golden query of the most recent load asked for.
477
+ function latestGoldenLoad(): Array<AggregateWindow> {
478
+ return goldenWindows().slice(-GOLDEN_METRICS.length);
479
+ }
480
+
481
+ function expectLatestLoadFor(start: Date, end: Date): void {
482
+ const load: Array<AggregateWindow> = latestGoldenLoad();
483
+
484
+ expect(
485
+ load
486
+ .map((window: AggregateWindow): string => {
487
+ return window.name;
488
+ })
489
+ .sort(),
490
+ ).toEqual([...GOLDEN_METRICS].sort());
491
+
492
+ for (const window of load) {
493
+ expect({
494
+ name: window.name,
495
+ window: windowKey(window.start, window.end),
496
+ }).toEqual({ name: window.name, window: windowKey(start, end) });
497
+ }
498
+ }
499
+
500
+ async function renderOverview(): Promise<void> {
501
+ render(<KubernetesClusterOverview {...PAGE_PROPS} />);
502
+ await settle();
503
+ expect(screen.getAllByTestId("line-chart")).toHaveLength(5);
504
+ }
505
+
506
+ // The golden chart card titled `title` (a tile can share the title).
507
+ function chartCard(title: string): HTMLElement {
508
+ const cards: Array<HTMLElement> = screen
509
+ .getAllByText(title, { selector: "span" })
510
+ .map((span: HTMLElement): HTMLElement | null => {
511
+ return span.closest('[class~="group/zoomhint"]');
512
+ })
513
+ .filter((card: HTMLElement | null): card is HTMLElement => {
514
+ return card !== null && within(card).queryByTestId("line-chart") !== null;
515
+ });
516
+
517
+ if (cards.length !== 1) {
518
+ throw new Error(
519
+ `Expected one "${title}" chart card, found ${cards.length}`,
520
+ );
521
+ }
522
+
523
+ return cards[0]!;
524
+ }
525
+
526
+ function chartWindowOf(title: string): string {
527
+ return windowOf(chartCard(title));
528
+ }
529
+
530
+ function chartWindows(): Array<string> {
531
+ return CHART_TITLES.map((title: string): string => {
532
+ return chartWindowOf(title);
533
+ });
534
+ }
535
+
536
+ async function dragAcross(
537
+ title: string,
538
+ start: Date,
539
+ end: Date,
540
+ ): Promise<void> {
541
+ await dragAcrossChartIn(chartCard(title), start, end);
542
+ }
543
+
544
+ async function doubleClickOn(title: string): Promise<void> {
545
+ await doubleClickOnChartIn(chartCard(title));
546
+ }
547
+
548
+ function pickerLabel(): string {
549
+ return (
550
+ screen.getByTestId(`${TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX}-button`)
551
+ .textContent || ""
552
+ );
553
+ }
554
+
555
+ function customLabel(start: Date, end: Date): string {
556
+ return getTimeRangeButtonLabel({
557
+ range: TimeRange.CUSTOM,
558
+ startAndEndDate: new InBetween<Date>(start, end),
559
+ });
560
+ }
561
+
562
+ function resetZoomButton(): HTMLElement | null {
563
+ return screen.queryByTestId(RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID);
564
+ }
565
+
566
+ const PAST_THIRTY_MINUTES_WINDOW: string = windowKey(
567
+ PAST_THIRTY_MINUTES_START,
568
+ NOW,
569
+ );
570
+ const DRAG_WINDOW: string = windowKey(DRAG_START, DRAG_END);
571
+ const NESTED_DRAG_WINDOW: string = windowKey(
572
+ NESTED_DRAG_START,
573
+ NESTED_DRAG_END,
574
+ );
575
+
576
+ beforeEach(() => {
577
+ jest.useFakeTimers();
578
+ jest.setSystemTime(NOW);
579
+ window.localStorage.setItem(REFRESH_STORAGE_KEY, "off");
580
+
581
+ heldAggregates = null;
582
+ resetChartZoomStandIns();
583
+
584
+ mockAggregate.mockReset();
585
+ mockAggregate.mockImplementation((request: unknown) => {
586
+ return answerAggregate(request as AggregateRequest);
587
+ });
588
+ mockInventorySummary.mockReset();
589
+ mockInventorySummary.mockImplementation(() => {
590
+ return Promise.resolve({
591
+ data: {
592
+ nodeCount: 1,
593
+ podCount: 4,
594
+ namespaceCount: 2,
595
+ podPhaseCounts: { running: 4, pending: 0, failed: 0, succeeded: 0 },
596
+ nodeReadyCounts: { ready: 1, notReady: 0 },
597
+ nodePressureCounts: {
598
+ memoryPressure: 0,
599
+ diskPressure: 0,
600
+ pidPressure: 0,
601
+ },
602
+ degradedPods: [],
603
+ degradedNodes: [],
604
+ },
605
+ });
606
+ });
607
+ mockFetchTopPods.mockReset();
608
+ mockFetchTopPods.mockImplementation(() => {
609
+ return Promise.resolve([]);
610
+ });
611
+ mockFetchWarnings.mockReset();
612
+ mockFetchWarnings.mockImplementation(() => {
613
+ return Promise.resolve([]);
614
+ });
615
+ });
616
+
617
+ afterEach(() => {
618
+ cleanup();
619
+ window.localStorage.clear();
620
+ jest.useRealTimers();
621
+ });
622
+
623
+ describe("cluster Overview before any zoom", () => {
624
+ test("opens on Past 30 Minutes: every chart and every golden query covers it", async () => {
625
+ await renderOverview();
626
+
627
+ expect(pickerLabel()).toBe("Past 30 Minutes");
628
+ expect(chartWindows()).toEqual(
629
+ CHART_TITLES.map((): string => {
630
+ return PAST_THIRTY_MINUTES_WINDOW;
631
+ }),
632
+ );
633
+ expectLatestLoadFor(PAST_THIRTY_MINUTES_START, NOW);
634
+ expect(screen.getByText(CPU_TILE_PAST_THIRTY_MINUTES)).toBeInTheDocument();
635
+ expect(resetZoomButton()).toBeNull();
636
+ });
637
+
638
+ test("every chart takes one and the same zoom - the page's - and none is handed one of its own", async () => {
639
+ await renderOverview();
640
+
641
+ expect(chartZoomStandIns.lineCharts.length).toBeGreaterThanOrEqual(5);
642
+
643
+ for (const record of chartZoomStandIns.lineCharts) {
644
+ expect(record.hostSelect).toBeUndefined();
645
+ expect(record.hostReset).toBeUndefined();
646
+ expect(record.hostDisable).toBeUndefined();
647
+ expect(record.select).toBeInstanceOf(Function);
648
+ }
649
+
650
+ const selects: Set<unknown> = new Set(
651
+ chartZoomStandIns.lineCharts.map((record: ChartZoomRecord): unknown => {
652
+ return record.select;
653
+ }),
654
+ );
655
+
656
+ expect(selects.size).toBe(1);
657
+ });
658
+
659
+ test("no chart offers a reset while there is nothing to undo, so a stray double-click does nothing", async () => {
660
+ await renderOverview();
661
+
662
+ for (const record of chartZoomStandIns.lineCharts) {
663
+ expect(record.reset).toBeUndefined();
664
+ }
665
+
666
+ const loadsBefore: number = goldenWindows().length;
667
+
668
+ await doubleClickOn("CPU");
669
+
670
+ expect(goldenWindows()).toHaveLength(loadsBefore);
671
+ expect(pickerLabel()).toBe("Past 30 Minutes");
672
+ });
673
+
674
+ test("every golden chart card names the drag on hover", async () => {
675
+ await renderOverview();
676
+
677
+ const hints: Array<HTMLElement> = screen.getAllByTestId(
678
+ TIME_RANGE_ZOOM_HINT_TEST_ID,
679
+ );
680
+
681
+ expect(hints).toHaveLength(5);
682
+
683
+ for (const title of CHART_TITLES) {
684
+ const hint: HTMLElement = within(chartCard(title)).getByTestId(
685
+ TIME_RANGE_ZOOM_HINT_TEST_ID,
686
+ );
687
+
688
+ expect(hint).toHaveTextContent("Drag to zoom");
689
+ expect(hint).toHaveClass("group-hover/zoomhint:opacity-100");
690
+ expect(hint).toHaveClass("pointer-events-none");
691
+ }
692
+ });
693
+ });
694
+
695
+ describe("dragging across a cluster Overview chart", () => {
696
+ test("narrows every chart on the page to the dragged window, not just the one dragged on", async () => {
697
+ await renderOverview();
698
+
699
+ await dragAcross("CPU", DRAG_START, DRAG_END);
700
+
701
+ expect(chartWindows()).toEqual(
702
+ CHART_TITLES.map((): string => {
703
+ return DRAG_WINDOW;
704
+ }),
705
+ );
706
+ });
707
+
708
+ test("re-issues every golden-metrics query for the dragged window", async () => {
709
+ await renderOverview();
710
+
711
+ const loadsBefore: number = goldenWindows().length;
712
+
713
+ await dragAcross("CPU", DRAG_START, DRAG_END);
714
+
715
+ expect(goldenWindows()).toHaveLength(loadsBefore + GOLDEN_METRICS.length);
716
+ expectLatestLoadFor(DRAG_START, DRAG_END);
717
+ });
718
+
719
+ test("the charts are drawn from the zoomed data, not the data from before", async () => {
720
+ await renderOverview();
721
+
722
+ await dragAcross("CPU", DRAG_START, DRAG_END);
723
+
724
+ expect(
725
+ within(chartCard("CPU"))
726
+ .getByTestId("line-chart")
727
+ .getAttribute("data-first-point"),
728
+ ).toBe(DRAG_START.toISOString());
729
+ expect(
730
+ within(chartCard("Network"))
731
+ .getByTestId("line-chart")
732
+ .getAttribute("data-series"),
733
+ ).toBe("In,Out");
734
+ });
735
+
736
+ test("the tiles follow: the CPU tile is read from the zoomed window's last five minutes", async () => {
737
+ await renderOverview();
738
+
739
+ await dragAcross("CPU", DRAG_START, DRAG_END);
740
+
741
+ expect(screen.getByText(CPU_TILE_DRAG)).toBeInTheDocument();
742
+ expect(screen.queryByText(CPU_TILE_PAST_THIRTY_MINUTES)).toBeNull();
743
+ });
744
+
745
+ test("the Availability tile and the uptime badge are read for the zoomed window as well", async () => {
746
+ await renderOverview();
747
+
748
+ const uptimeBadge: () => string = (): string => {
749
+ return screen.getByText(/% uptime$/).textContent || "";
750
+ };
751
+
752
+ expect(uptimeBadge()).toBe(UPTIME_PAST_THIRTY_MINUTES);
753
+
754
+ await dragAcross("Availability", DRAG_START, DRAG_END);
755
+
756
+ expect(uptimeBadge()).toBe(UPTIME_DRAG);
757
+ // The tile beside it says the same.
758
+ expect(screen.getByText("100.0%")).toBeInTheDocument();
759
+
760
+ await doubleClickOn("CPU");
761
+
762
+ expect(uptimeBadge()).toBe(UPTIME_PAST_THIRTY_MINUTES);
763
+ });
764
+
765
+ test("the picker shows the dragged window as a custom range, with Reset zoom beside it", async () => {
766
+ await renderOverview();
767
+
768
+ await dragAcross("CPU", DRAG_START, DRAG_END);
769
+
770
+ expect(pickerLabel()).toBe(customLabel(DRAG_START, DRAG_END));
771
+
772
+ const reset: HTMLElement | null = resetZoomButton();
773
+
774
+ expect(reset).toBeVisible();
775
+ expect(reset).toHaveAttribute(
776
+ "title",
777
+ `Go back to ${TimeRange.PAST_THIRTY_MINS}, the time range before the zoom`,
778
+ );
779
+ // The button sits in the hero, beside the picker it undoes.
780
+ expect(
781
+ within(screen.getByTestId("auto-refresh-control")).getByTestId(
782
+ RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID,
783
+ ),
784
+ ).toBe(reset);
785
+ });
786
+
787
+ test("every chart can now undo the zoom, and names the double-click", async () => {
788
+ await renderOverview();
789
+
790
+ await dragAcross("CPU", DRAG_START, DRAG_END);
791
+
792
+ for (const title of CHART_TITLES) {
793
+ expect(
794
+ within(chartCard(title)).getByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID),
795
+ ).toHaveTextContent("Double-click to reset");
796
+ }
797
+
798
+ const zoomedRenders: Array<ChartZoomRecord> =
799
+ chartZoomStandIns.lineCharts.slice(-5);
800
+
801
+ for (const record of zoomedRenders) {
802
+ expect(record.reset).toBeInstanceOf(Function);
803
+ }
804
+ });
805
+
806
+ test("a drag from right to left zooms to the same window", async () => {
807
+ await renderOverview();
808
+
809
+ await dragAcross("Filesystem", DRAG_END, DRAG_START);
810
+
811
+ expect(chartWindows()[0]).toBe(DRAG_WINDOW);
812
+ expectLatestLoadFor(DRAG_START, DRAG_END);
813
+ });
814
+
815
+ test.each(CHART_TITLES)(
816
+ "a drag on the %s chart zooms the whole page",
817
+ async (title: string) => {
818
+ await renderOverview();
819
+
820
+ await dragAcross(title, DRAG_START, DRAG_END);
821
+
822
+ expect(chartWindows()).toEqual(
823
+ CHART_TITLES.map((): string => {
824
+ return DRAG_WINDOW;
825
+ }),
826
+ );
827
+ expect(pickerLabel()).toBe(customLabel(DRAG_START, DRAG_END));
828
+ },
829
+ );
830
+
831
+ test("the 'now' snapshots - inventory, top consumers, warnings - are not refetched by a zoom", async () => {
832
+ await renderOverview();
833
+
834
+ const summaries: number = mockInventorySummary.mock.calls.length;
835
+ const topPods: number = mockFetchTopPods.mock.calls.length;
836
+ const warnings: number = mockFetchWarnings.mock.calls.length;
837
+
838
+ await dragAcross("CPU", DRAG_START, DRAG_END);
839
+ expect(chartWindowOf("CPU")).toBe(DRAG_WINDOW);
840
+ await doubleClickOn("Memory");
841
+ expect(chartWindowOf("CPU")).toBe(PAST_THIRTY_MINUTES_WINDOW);
842
+
843
+ expect(mockInventorySummary.mock.calls).toHaveLength(summaries);
844
+ expect(mockFetchTopPods.mock.calls).toHaveLength(topPods);
845
+ expect(mockFetchWarnings.mock.calls).toHaveLength(warnings);
846
+ });
847
+ });
848
+
849
+ describe("undoing a cluster Overview zoom", () => {
850
+ test("a double-click on a DIFFERENT chart (Memory, after a drag on CPU) puts the page back on Past 30 Minutes", async () => {
851
+ await renderOverview();
852
+
853
+ await dragAcross("CPU", DRAG_START, DRAG_END);
854
+ await doubleClickOn("Memory");
855
+
856
+ expect(pickerLabel()).toBe("Past 30 Minutes");
857
+ expect(chartWindows()).toEqual(
858
+ CHART_TITLES.map((): string => {
859
+ return PAST_THIRTY_MINUTES_WINDOW;
860
+ }),
861
+ );
862
+ expect(resetZoomButton()).toBeNull();
863
+ });
864
+
865
+ test("the reset re-issues the golden queries for the original window, and the tiles read it again", async () => {
866
+ await renderOverview();
867
+
868
+ await dragAcross("CPU", DRAG_START, DRAG_END);
869
+ await doubleClickOn("Memory");
870
+
871
+ expectLatestLoadFor(PAST_THIRTY_MINUTES_START, NOW);
872
+ expect(screen.getByText(CPU_TILE_PAST_THIRTY_MINUTES)).toBeInTheDocument();
873
+ });
874
+
875
+ test("once undone, no chart offers a reset any more", async () => {
876
+ await renderOverview();
877
+
878
+ await dragAcross("CPU", DRAG_START, DRAG_END);
879
+ await doubleClickOn("Memory");
880
+
881
+ for (const record of chartZoomStandIns.lineCharts.slice(-5)) {
882
+ expect(record.reset).toBeUndefined();
883
+ }
884
+
885
+ for (const title of CHART_TITLES) {
886
+ expect(
887
+ within(chartCard(title)).getByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID),
888
+ ).toHaveTextContent(/^Drag to zoom$/);
889
+ }
890
+ });
891
+
892
+ test("Reset zoom beside the picker does what a double-click does", async () => {
893
+ await renderOverview();
894
+
895
+ await dragAcross("Network", DRAG_START, DRAG_END);
896
+ fireEvent.click(resetZoomButton()!);
897
+ await settle();
898
+
899
+ expect(pickerLabel()).toBe("Past 30 Minutes");
900
+ expect(chartWindows()).toEqual(
901
+ CHART_TITLES.map((): string => {
902
+ return PAST_THIRTY_MINUTES_WINDOW;
903
+ }),
904
+ );
905
+ expectLatestLoadFor(PAST_THIRTY_MINUTES_START, NOW);
906
+ expect(resetZoomButton()).toBeNull();
907
+ });
908
+
909
+ test("a second drag inside the zoom narrows further, and ONE reset returns all the way to Past 30 Minutes", async () => {
910
+ await renderOverview();
911
+
912
+ await dragAcross("CPU", DRAG_START, DRAG_END);
913
+ await dragAcross("Filesystem", NESTED_DRAG_START, NESTED_DRAG_END);
914
+
915
+ expect(chartWindows()).toEqual(
916
+ CHART_TITLES.map((): string => {
917
+ return NESTED_DRAG_WINDOW;
918
+ }),
919
+ );
920
+ expectLatestLoadFor(NESTED_DRAG_START, NESTED_DRAG_END);
921
+ expect(pickerLabel()).toBe(customLabel(NESTED_DRAG_START, NESTED_DRAG_END));
922
+ expect(screen.getByText(CPU_TILE_NESTED_DRAG)).toBeInTheDocument();
923
+ // Still offering the way back to where the reader started.
924
+ expect(resetZoomButton()).toHaveAttribute(
925
+ "title",
926
+ `Go back to ${TimeRange.PAST_THIRTY_MINS}, the time range before the zoom`,
927
+ );
928
+
929
+ await doubleClickOn("Availability");
930
+
931
+ expect(pickerLabel()).toBe("Past 30 Minutes");
932
+ expect(chartWindows()).toEqual(
933
+ CHART_TITLES.map((): string => {
934
+ return PAST_THIRTY_MINUTES_WINDOW;
935
+ }),
936
+ );
937
+ expectLatestLoadFor(PAST_THIRTY_MINUTES_START, NOW);
938
+ expect(resetZoomButton()).toBeNull();
939
+ });
940
+
941
+ test("picking a range in the picker ends the zoom: no Reset zoom, and a double-click no longer retimes the page", async () => {
942
+ await renderOverview();
943
+
944
+ await dragAcross("CPU", DRAG_START, DRAG_END);
945
+ expect(resetZoomButton()).toBeVisible();
946
+
947
+ fireEvent.click(
948
+ screen.getByTestId(
949
+ `${TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX}-button`,
950
+ ),
951
+ );
952
+ fireEvent.click(screen.getByRole("button", { name: "Past 1 Hour" }));
953
+ await settle();
954
+
955
+ const oneHourAgo: Date = new Date(NOW.getTime() - 60 * MINUTE_MS);
956
+
957
+ expect(pickerLabel()).toBe("Past 1 Hour");
958
+ expect(resetZoomButton()).toBeNull();
959
+ expect(chartWindowOf("CPU")).toBe(windowKey(oneHourAgo, NOW));
960
+ expectLatestLoadFor(oneHourAgo, NOW);
961
+
962
+ const loadsBefore: number = goldenWindows().length;
963
+
964
+ await doubleClickOn("CPU");
965
+
966
+ expect(goldenWindows()).toHaveLength(loadsBefore);
967
+ expect(pickerLabel()).toBe("Past 1 Hour");
968
+ });
969
+
970
+ test("a zoom made after picking a range undoes back to THAT range", async () => {
971
+ await renderOverview();
972
+
973
+ fireEvent.click(
974
+ screen.getByTestId(
975
+ `${TELEMETRY_TIME_RANGE_PICKER_TEST_ID_PREFIX}-button`,
976
+ ),
977
+ );
978
+ fireEvent.click(screen.getByRole("button", { name: "Past 1 Hour" }));
979
+ await settle();
980
+
981
+ await dragAcross("CPU", DRAG_START, DRAG_END);
982
+
983
+ expect(resetZoomButton()).toHaveAttribute(
984
+ "title",
985
+ `Go back to ${TimeRange.PAST_ONE_HOUR}, the time range before the zoom`,
986
+ );
987
+
988
+ await doubleClickOn("Network");
989
+
990
+ expect(pickerLabel()).toBe("Past 1 Hour");
991
+ expect(chartWindowOf("Memory")).toBe(
992
+ windowKey(new Date(NOW.getTime() - 60 * MINUTE_MS), NOW),
993
+ );
994
+ });
995
+ });
996
+
997
+ describe("the cluster Overview zoom and its data loads", () => {
998
+ test("auto-refresh keeps a zoomed page on the zoomed window, and the reset still works afterwards", async () => {
999
+ window.localStorage.setItem(REFRESH_STORAGE_KEY, "30s");
1000
+
1001
+ await renderOverview();
1002
+ await dragAcross("CPU", DRAG_START, DRAG_END);
1003
+
1004
+ const loadsBefore: number = goldenWindows().length;
1005
+
1006
+ await act(async () => {
1007
+ jest.advanceTimersByTime(30 * 1000);
1008
+ });
1009
+ await settle();
1010
+
1011
+ // The tick reloaded, and for the pinned window rather than "now".
1012
+ expect(goldenWindows().length).toBe(loadsBefore + GOLDEN_METRICS.length);
1013
+ expectLatestLoadFor(DRAG_START, DRAG_END);
1014
+ expect(chartWindowOf("CPU")).toBe(DRAG_WINDOW);
1015
+ expect(pickerLabel()).toBe(customLabel(DRAG_START, DRAG_END));
1016
+ expect(resetZoomButton()).toBeVisible();
1017
+
1018
+ await doubleClickOn("CPU");
1019
+
1020
+ // Back on the preset, which now ends at the clock the tick moved on.
1021
+ const now: Date = new Date(Date.now());
1022
+ const thirtyMinutesEarlier: Date = new Date(now.getTime() - 30 * MINUTE_MS);
1023
+
1024
+ expect(pickerLabel()).toBe("Past 30 Minutes");
1025
+ expect(chartWindowOf("CPU")).toBe(windowKey(thirtyMinutesEarlier, now));
1026
+ expectLatestLoadFor(thirtyMinutesEarlier, now);
1027
+ });
1028
+
1029
+ test("a manual Refresh while zoomed reloads the zoomed window and keeps the zoom", async () => {
1030
+ await renderOverview();
1031
+ await dragAcross("CPU", DRAG_START, DRAG_END);
1032
+
1033
+ fireEvent.click(screen.getByRole("button", { name: "Refresh now" }));
1034
+ await settle();
1035
+
1036
+ expectLatestLoadFor(DRAG_START, DRAG_END);
1037
+ expect(chartWindowOf("Availability")).toBe(DRAG_WINDOW);
1038
+ expect(resetZoomButton()).toBeVisible();
1039
+ });
1040
+
1041
+ test("a slow load from before the zoom cannot drag the charts back to the old window", async () => {
1042
+ await renderOverview();
1043
+
1044
+ // A refresh for Past 30 Minutes goes out and is slow to answer...
1045
+ heldAggregates = [];
1046
+ const slowLoad: Array<{ release: () => void; fail: () => void }> =
1047
+ heldAggregates;
1048
+ fireEvent.click(screen.getByRole("button", { name: "Refresh now" }));
1049
+ await settle();
1050
+ heldAggregates = null;
1051
+
1052
+ // ...the reader zooms in the meantime, and that answer comes back first.
1053
+ await dragAcross("CPU", DRAG_START, DRAG_END);
1054
+ expect(chartWindows()[0]).toBe(DRAG_WINDOW);
1055
+
1056
+ for (const held of slowLoad) {
1057
+ held.release();
1058
+ }
1059
+ await settle();
1060
+
1061
+ expect(chartWindows()).toEqual(
1062
+ CHART_TITLES.map((): string => {
1063
+ return DRAG_WINDOW;
1064
+ }),
1065
+ );
1066
+ expect(screen.getByText(CPU_TILE_DRAG)).toBeInTheDocument();
1067
+ expect(pickerLabel()).toBe(customLabel(DRAG_START, DRAG_END));
1068
+ expect(
1069
+ screen
1070
+ .getByTestId("auto-refresh-control")
1071
+ .getAttribute("data-refreshing"),
1072
+ ).toBe("false");
1073
+ });
1074
+
1075
+ test("a load from before a reset that fails late does not put an error over the reset page", async () => {
1076
+ await renderOverview();
1077
+ await dragAcross("CPU", DRAG_START, DRAG_END);
1078
+
1079
+ heldAggregates = [];
1080
+ const slowLoad: Array<{ release: () => void; fail: () => void }> =
1081
+ heldAggregates;
1082
+ fireEvent.click(screen.getByRole("button", { name: "Refresh now" }));
1083
+ await settle();
1084
+ heldAggregates = null;
1085
+
1086
+ await doubleClickOn("Memory");
1087
+ expect(chartWindows()[0]).toBe(PAST_THIRTY_MINUTES_WINDOW);
1088
+
1089
+ for (const held of slowLoad) {
1090
+ held.fail();
1091
+ }
1092
+ await settle();
1093
+
1094
+ expect(screen.queryByText("The old window failed to load")).toBeNull();
1095
+ expect(chartWindows()).toEqual(
1096
+ CHART_TITLES.map((): string => {
1097
+ return PAST_THIRTY_MINUTES_WINDOW;
1098
+ }),
1099
+ );
1100
+ });
1101
+
1102
+ test("allocatable CPU is read for the page's window, and never less than five minutes of it", async () => {
1103
+ await renderOverview();
1104
+
1105
+ const allocatableFor: () => AggregateWindow = (): AggregateWindow => {
1106
+ const windows: Array<AggregateWindow> = aggregateWindows().filter(
1107
+ (window: AggregateWindow): boolean => {
1108
+ return window.name === ALLOCATABLE_CPU_METRIC;
1109
+ },
1110
+ );
1111
+ return windows[windows.length - 1]!;
1112
+ };
1113
+
1114
+ // Past 30 Minutes: exactly the page's window (the golden load's lookup).
1115
+ const initialLookups: Array<string> = aggregateWindows()
1116
+ .filter((window: AggregateWindow): boolean => {
1117
+ return window.name === ALLOCATABLE_CPU_METRIC;
1118
+ })
1119
+ .map((window: AggregateWindow): string => {
1120
+ return windowKey(window.start, window.end);
1121
+ });
1122
+ expect(initialLookups).toContain(PAST_THIRTY_MINUTES_WINDOW);
1123
+
1124
+ // A ten-minute zoom is wide enough to be read as it is.
1125
+ await dragAcross("CPU", DRAG_START, DRAG_END);
1126
+ expect(windowKey(allocatableFor().start, allocatableFor().end)).toBe(
1127
+ DRAG_WINDOW,
1128
+ );
1129
+
1130
+ // A 40-second zoom would fall between samples: reach back five minutes.
1131
+ const shortStart: Date = new Date("2026-09-28T11:42:00.000Z");
1132
+ const shortEnd: Date = new Date("2026-09-28T11:42:40.000Z");
1133
+ await dragAcross("CPU", shortStart, shortEnd);
1134
+
1135
+ expectLatestLoadFor(shortStart, shortEnd);
1136
+ expect(windowKey(allocatableFor().start, allocatableFor().end)).toBe(
1137
+ windowKey(new Date(shortEnd.getTime() - 5 * MINUTE_MS), shortEnd),
1138
+ );
1139
+ // So the CPU chart still has its denominator and draws.
1140
+ expect(
1141
+ within(chartCard("CPU"))
1142
+ .getByTestId("line-chart")
1143
+ .getAttribute("data-series"),
1144
+ ).toBe("CPU %");
1145
+ });
1146
+ });
1147
+
1148
+ describe("ClusterChartCard on its own", () => {
1149
+ const WINDOW: { start: Date; end: Date } = {
1150
+ start: PAST_THIRTY_MINUTES_START,
1151
+ end: NOW,
1152
+ };
1153
+
1154
+ function zoom(isZoomed: boolean): TimeRangeZoom {
1155
+ return {
1156
+ isZoomed: isZoomed,
1157
+ rangeBeforeZoom: isZoomed ? { range: TimeRange.PAST_ONE_HOUR } : null,
1158
+ zoomToTimeRange: () => {},
1159
+ resetZoom: () => {},
1160
+ };
1161
+ }
1162
+
1163
+ function card(
1164
+ chartWindow: { start: Date; end: Date } | null,
1165
+ ): React.ReactElement {
1166
+ return (
1167
+ <ClusterChartCard
1168
+ title="CPU"
1169
+ icon={IconProp.ChartBar}
1170
+ iconColor="blue"
1171
+ data={[]}
1172
+ chartWindow={chartWindow}
1173
+ syncId="kubernetes-overview-x"
1174
+ />
1175
+ );
1176
+ }
1177
+
1178
+ test("outside a page that zooms it shows no hint and its chart offers no zoom", () => {
1179
+ render(card(WINDOW));
1180
+
1181
+ expect(screen.queryByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID)).toBeNull();
1182
+ expect(
1183
+ chartZoomStandIns.lineCharts[chartZoomStandIns.lineCharts.length - 1]
1184
+ ?.select,
1185
+ ).toBeUndefined();
1186
+ });
1187
+
1188
+ test("inside one, the hint is revealed on hover of the card and kept out of the header's way", () => {
1189
+ render(
1190
+ <TimeRangeZoomProvider zoom={zoom(false)}>
1191
+ {card(WINDOW)}
1192
+ </TimeRangeZoomProvider>,
1193
+ );
1194
+
1195
+ const hint: HTMLElement = screen.getByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID);
1196
+
1197
+ expect(hint).toHaveTextContent("Drag to zoom");
1198
+ expect(hint).toHaveClass("group-hover/zoomhint:opacity-100");
1199
+ expect(hint).toHaveClass("absolute");
1200
+ expect(hint.closest('[class~="group/zoomhint"]')).toBe(
1201
+ screen.getByTestId("line-chart").closest('[class~="group/zoomhint"]'),
1202
+ );
1203
+ });
1204
+
1205
+ test("the hint is there before the chart is, and names the reset while zoomed", () => {
1206
+ render(
1207
+ <TimeRangeZoomProvider zoom={zoom(true)}>
1208
+ {card(null)}
1209
+ </TimeRangeZoomProvider>,
1210
+ );
1211
+
1212
+ expect(screen.queryByTestId("line-chart")).toBeNull();
1213
+ expect(screen.getByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID)).toHaveTextContent(
1214
+ "Double-click to reset",
1215
+ );
1216
+ });
1217
+ });