@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,848 @@
1
+ /** @timezone UTC */
2
+
3
+ import "@testing-library/jest-dom";
4
+ import {
5
+ act,
6
+ cleanup,
7
+ fireEvent,
8
+ render,
9
+ screen,
10
+ within,
11
+ } from "@testing-library/react";
12
+ import React, { ReactElement } from "react";
13
+
14
+ /*
15
+ * Issue #4105 on the Docker and Podman host Overview pages: a drag across
16
+ * any of the six charts (Availability, Avg/Peak CPU, Avg/Peak Memory,
17
+ * Network) zooms the WHOLE page to the window dragged out - every chart,
18
+ * the summary tiles, the hero's container count, the uptime badge and the
19
+ * Top consumer lists are fetched again for it - and a double-click on any
20
+ * chart (or Reset zoom beside the hero's picker) puts back the range from
21
+ * before the first zoom.
22
+ *
23
+ * The pages are rendered for real over a fake analytics server that only
24
+ * answers with the rows inside the window it is asked for, so what the
25
+ * tiles and lists show proves which window they were computed from. Only
26
+ * the chart itself is stood in for (see ContainerChartZoomStandIn): it
27
+ * resolves its zoom exactly the way LineChartElement does and offers the
28
+ * two gestures.
29
+ */
30
+
31
+ /*
32
+ * Function declarations, so they are hoisted along with the jest.mock calls
33
+ * that use them. They are only called at render time.
34
+ */
35
+ function mockStubModule(testId: string): {
36
+ __esModule: boolean;
37
+ default: () => ReactElement;
38
+ } {
39
+ return {
40
+ __esModule: true,
41
+ default: (): ReactElement => {
42
+ return React.createElement("div", { "data-testid": testId });
43
+ },
44
+ };
45
+ }
46
+
47
+ jest.mock("../../../UI/Components/Charts/Line/LineChart", () => {
48
+ return (
49
+ jest.requireActual(
50
+ "./ContainerChartZoomStandIn",
51
+ ) as typeof import("./ContainerChartZoomStandIn")
52
+ ).chartModuleStandIn(
53
+ "line",
54
+ jest.requireActual("../../../UI/Components/Charts/Line/LineChart"),
55
+ );
56
+ });
57
+ jest.mock(
58
+ "../../../../App/FeatureSet/Dashboard/src/Components/ResourceActivity/ResourceActivityCards",
59
+ () => {
60
+ return mockStubModule("stub-activity-cards");
61
+ },
62
+ );
63
+ jest.mock("../../../UI/Components/ModelDetail/CardModelDetail", () => {
64
+ return mockStubModule("stub-host-details");
65
+ });
66
+
67
+ import DockerHostOverview from "../../../../App/FeatureSet/Dashboard/src/Pages/Docker/View/Overview";
68
+ import PodmanHostOverview from "../../../../App/FeatureSet/Dashboard/src/Pages/Podman/View/Overview";
69
+ import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
70
+ import DockerHost from "../../../Models/DatabaseModels/DockerHost";
71
+ import PodmanHost from "../../../Models/DatabaseModels/PodmanHost";
72
+ import Route from "../../../Types/API/Route";
73
+ import AggregatedModel from "../../../Types/BaseDatabase/AggregatedModel";
74
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
75
+ import ObjectID from "../../../Types/ObjectID";
76
+ import TimeRange from "../../../Types/Time/TimeRange";
77
+ import { RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID } from "../../../UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton";
78
+ import { TIME_RANGE_ZOOM_HINT_TEST_ID } from "../../../UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint";
79
+ import { getTimeRangeButtonLabel } from "../../../UI/Components/Date/TimeRangePickerDropdown";
80
+ import AnalyticsModelAPI from "../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
81
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
82
+ import Navigation from "../../../UI/Utils/Navigation";
83
+ import ProjectUtil from "../../../UI/Utils/Project";
84
+ import { ChartTimeRangeZoomHandlers } from "../../../UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext";
85
+ import {
86
+ CHART_STAND_IN_TEST_ID,
87
+ doubleClickChart,
88
+ dragAcrossChart,
89
+ resetChartStandIns,
90
+ windowOfChart,
91
+ windowText,
92
+ zoomOfChart,
93
+ } from "./ContainerChartZoomStandIn";
94
+
95
+ /*
96
+ * ---------------------------------------------------------------------------
97
+ * A fake analytics server
98
+ * ---------------------------------------------------------------------------
99
+ */
100
+
101
+ const NOW: Date = new Date("2026-09-24T12:00:00.000Z");
102
+ const HOST_ID: string = "5a1b2c3d-4e5f-4061-8273-94a5b6c7d8e9";
103
+ const PROJECT_ID: ObjectID = new ObjectID(
104
+ "7d3f1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b",
105
+ );
106
+
107
+ const CHART_TITLES: Array<string> = [
108
+ "Availability",
109
+ "Avg CPU",
110
+ "Peak CPU",
111
+ "Avg Memory",
112
+ "Peak Memory",
113
+ "Network",
114
+ ];
115
+
116
+ const pageProps: PageComponentProps = {
117
+ pageRoute: new Route("/overview"),
118
+ currentProject: null,
119
+ hasPaymentMethod: false,
120
+ };
121
+
122
+ function at(hhmm: string): Date {
123
+ return new Date(`2026-09-24T${hhmm}:00.000Z`);
124
+ }
125
+
126
+ function minutesBetween(from: string, to: string): Array<string> {
127
+ const out: Array<string> = [];
128
+
129
+ for (
130
+ let time: number = at(from).getTime();
131
+ time <= at(to).getTime();
132
+ time += 60_000
133
+ ) {
134
+ out.push(new Date(time).toISOString().slice(11, 16));
135
+ }
136
+
137
+ return out;
138
+ }
139
+
140
+ function bucket(
141
+ hhmm: string,
142
+ container: string,
143
+ value: number,
144
+ ): AggregatedModel {
145
+ return {
146
+ timestamp: at(hhmm),
147
+ value: value,
148
+ attributes: { "resource.container.name": container },
149
+ };
150
+ }
151
+
152
+ /*
153
+ * The default range is the past 30 minutes: 11:30 to 12:00. The tiles read
154
+ * the last 5 minutes of whatever range the page is on.
155
+ *
156
+ * - "api" runs all along: 10% CPU early, 80% from 11:45 to 11:50, 20% after.
157
+ * - "batch" only ran from 11:40 to 11:48.
158
+ * - The agent missed its 11:35 and 11:36 heartbeats: two minutes down.
159
+ * - No network counters at all, so the Network chart is empty.
160
+ *
161
+ * On the default range the tiles read 11:55-12:00 (api alone, at 20%); on
162
+ * the 11:40-11:50 window they read 11:45-11:50 (api at 80%, and batch).
163
+ */
164
+ function cpuValue(hhmm: string): number {
165
+ if (hhmm < "11:45") {
166
+ return 10;
167
+ }
168
+
169
+ return hhmm <= "11:50" ? 80 : 20;
170
+ }
171
+
172
+ const FIXTURE: Record<string, Array<AggregatedModel>> = {
173
+ "container.cpu.utilization": [
174
+ ...minutesBetween("11:31", "12:00").map((hhmm: string): AggregatedModel => {
175
+ return bucket(hhmm, "api", cpuValue(hhmm));
176
+ }),
177
+ ...minutesBetween("11:40", "11:48").map((hhmm: string): AggregatedModel => {
178
+ return bucket(hhmm, "batch", 50);
179
+ }),
180
+ ],
181
+ "container.memory.percent": [
182
+ ...minutesBetween("11:31", "12:00").map((hhmm: string): AggregatedModel => {
183
+ return bucket(hhmm, "api", 30);
184
+ }),
185
+ ...minutesBetween("11:40", "11:48").map((hhmm: string): AggregatedModel => {
186
+ return bucket(hhmm, "batch", 60);
187
+ }),
188
+ ],
189
+ "container.pids.count": minutesBetween("11:31", "12:00").map(
190
+ (hhmm: string): AggregatedModel => {
191
+ return bucket(hhmm, "api", 7);
192
+ },
193
+ ),
194
+ "oneuptime.host.heartbeat": minutesBetween("11:31", "12:00")
195
+ .filter((hhmm: string): boolean => {
196
+ return hhmm !== "11:35" && hhmm !== "11:36";
197
+ })
198
+ .map((hhmm: string): AggregatedModel => {
199
+ return { timestamp: at(hhmm), value: 2 };
200
+ }),
201
+ };
202
+
203
+ interface AggregateRequest {
204
+ aggregateBy: {
205
+ query: {
206
+ name: string;
207
+ time: InBetween<Date>;
208
+ };
209
+ startTimestamp: Date;
210
+ endTimestamp: Date;
211
+ };
212
+ }
213
+
214
+ interface PendingAggregate {
215
+ request: AggregateRequest;
216
+ answer: () => void;
217
+ }
218
+
219
+ let aggregateRequests: Array<AggregateRequest>;
220
+ let pendingAggregates: Array<PendingAggregate>;
221
+ let answerAtOnce: boolean;
222
+
223
+ // Only the rows inside the window asked for, as the real server does.
224
+ function rowsFor(request: AggregateRequest): Array<AggregatedModel> {
225
+ const startMs: number = request.aggregateBy.query.time.startValue.getTime();
226
+ const endMs: number = request.aggregateBy.query.time.endValue.getTime();
227
+
228
+ return (FIXTURE[request.aggregateBy.query.name] || []).filter(
229
+ (row: AggregatedModel): boolean => {
230
+ const time: number = (row["timestamp"] as Date).getTime();
231
+
232
+ return time >= startMs && time <= endMs;
233
+ },
234
+ );
235
+ }
236
+
237
+ function serveAnalytics(): void {
238
+ jest
239
+ .spyOn(AnalyticsModelAPI, "aggregate")
240
+ .mockImplementation((...args: Array<unknown>): Promise<never> => {
241
+ const request: AggregateRequest = args[0] as AggregateRequest;
242
+ aggregateRequests.push(request);
243
+
244
+ return new Promise<never>((resolve: (value: never) => void): void => {
245
+ const answer: () => void = (): void => {
246
+ resolve({ data: rowsFor(request) } as never);
247
+ };
248
+
249
+ if (answerAtOnce) {
250
+ answer();
251
+ return;
252
+ }
253
+
254
+ pendingAggregates.push({ request: request, answer: answer });
255
+ });
256
+ });
257
+ }
258
+
259
+ interface RuntimeCase {
260
+ Overview: React.FunctionComponent<PageComponentProps>;
261
+ refreshStorageKey: string;
262
+ buildHost: () => DockerHost | PodmanHost;
263
+ }
264
+
265
+ function fillHost<T extends DockerHost | PodmanHost>(host: T): T {
266
+ host._id = HOST_ID;
267
+ host.name = "web-01";
268
+ host.hostIdentifier = "web-01";
269
+ host.otelCollectorStatus = "connected";
270
+ host.osType = "linux";
271
+ host.lastSeenAt = NOW;
272
+ return host;
273
+ }
274
+
275
+ const RUNTIME_CASES: Array<[string, RuntimeCase]> = [
276
+ [
277
+ "Docker",
278
+ {
279
+ Overview: DockerHostOverview,
280
+ refreshStorageKey: "docker-overview-auto-refresh-interval",
281
+ buildHost: (): DockerHost => {
282
+ return fillHost(new DockerHost());
283
+ },
284
+ },
285
+ ],
286
+ [
287
+ "Podman",
288
+ {
289
+ Overview: PodmanHostOverview,
290
+ refreshStorageKey: "podman-overview-auto-refresh-interval",
291
+ buildHost: (): PodmanHost => {
292
+ return fillHost(new PodmanHost());
293
+ },
294
+ },
295
+ ],
296
+ ];
297
+
298
+ /*
299
+ * ---------------------------------------------------------------------------
300
+ * Reading the page
301
+ * ---------------------------------------------------------------------------
302
+ */
303
+
304
+ async function flush(): Promise<void> {
305
+ await act(async () => {
306
+ for (let i: number = 0; i < 20; i++) {
307
+ await Promise.resolve();
308
+ }
309
+ });
310
+ }
311
+
312
+ // Each chart section's heading, and the charts in it.
313
+ const SECTIONS: Array<[string, Array<string>]> = [
314
+ ["Availability", ["Availability"]],
315
+ [
316
+ "Container resource usage",
317
+ ["Avg CPU", "Peak CPU", "Avg Memory", "Peak Memory"],
318
+ ],
319
+ ["Network", ["Network"]],
320
+ ];
321
+
322
+ // A chart section: its heading row (with the zoom hint) and its cards.
323
+ function section(heading: string): HTMLElement {
324
+ return screen
325
+ .getByRole("heading", { level: 2, name: heading })
326
+ .closest('div[class~="group/zoomhint"]') as HTMLElement;
327
+ }
328
+
329
+ function hintOf(heading: string): HTMLElement {
330
+ return within(section(heading)).getByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID);
331
+ }
332
+
333
+ // The card a chart sits in, found by the chart's title.
334
+ function chartCard(title: string): HTMLElement {
335
+ const cards: Array<HTMLElement> = screen
336
+ .getAllByText(title, { selector: "span" })
337
+ .map((span: HTMLElement): HTMLElement | null => {
338
+ return span.closest("div.rounded-xl");
339
+ })
340
+ .filter((card: HTMLElement | null): card is HTMLElement => {
341
+ return (
342
+ card !== null &&
343
+ within(card).queryByTestId(CHART_STAND_IN_TEST_ID) !== null
344
+ );
345
+ });
346
+
347
+ expect(cards).toHaveLength(1);
348
+
349
+ return cards[0]!;
350
+ }
351
+
352
+ function chart(title: string): HTMLElement {
353
+ return within(chartCard(title)).getByTestId(CHART_STAND_IN_TEST_ID);
354
+ }
355
+
356
+ function zoomOf(title: string): ChartTimeRangeZoomHandlers {
357
+ return zoomOfChart(chart(title));
358
+ }
359
+
360
+ function windowOf(title: string): string {
361
+ return windowOfChart(chart(title));
362
+ }
363
+
364
+ function tileValue(title: string): string {
365
+ const tile: HTMLElement = screen
366
+ .getAllByRole("button", { name: `About ${title}` })[0]!
367
+ .closest("div.rounded-xl") as HTMLElement;
368
+
369
+ return (tile.querySelector("div.text-2xl")?.textContent || "").trim();
370
+ }
371
+
372
+ function consumerRows(listTitle: string): Array<string> {
373
+ const heading: HTMLElement = screen
374
+ .getAllByRole("button", { name: `About ${listTitle}` })[0]!
375
+ .closest("h2") as HTMLElement;
376
+ let card: HTMLElement | null = heading.parentElement;
377
+
378
+ while (card && !card.querySelector(".divide-y")) {
379
+ card = card.parentElement;
380
+ }
381
+
382
+ const list: HTMLElement = card!.querySelector(".divide-y") as HTMLElement;
383
+
384
+ return Array.from(list.children).map((row: Element): string => {
385
+ return Array.from(row.children)
386
+ .map((part: Element): string => {
387
+ return (part.textContent || "").trim();
388
+ })
389
+ .join(" ");
390
+ });
391
+ }
392
+
393
+ function pickerLabel(): string {
394
+ return (
395
+ screen.getByTestId("telemetry-time-range-picker-button").textContent || ""
396
+ ).trim();
397
+ }
398
+
399
+ function customLabel(start: Date, end: Date): string {
400
+ return getTimeRangeButtonLabel({
401
+ range: TimeRange.CUSTOM,
402
+ startAndEndDate: new InBetween<Date>(start, end),
403
+ });
404
+ }
405
+
406
+ function resetButtons(): Array<HTMLElement> {
407
+ return screen.queryAllByTestId(RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID);
408
+ }
409
+
410
+ // Every aggregate asked for since the given count, as [start, end] ISO pairs.
411
+ function windowsAskedSince(count: number): Array<string> {
412
+ return aggregateRequests
413
+ .slice(count)
414
+ .map((request: AggregateRequest): string => {
415
+ return windowText(
416
+ request.aggregateBy.query.time.startValue,
417
+ request.aggregateBy.query.time.endValue,
418
+ );
419
+ });
420
+ }
421
+
422
+ async function dragAcross(
423
+ title: string,
424
+ start: Date,
425
+ end: Date,
426
+ ): Promise<void> {
427
+ dragAcrossChart(chart(title), start, end);
428
+ await flush();
429
+ }
430
+
431
+ async function doubleClick(title: string): Promise<void> {
432
+ doubleClickChart(chart(title));
433
+ await flush();
434
+ }
435
+
436
+ async function renderPage(runtimeCase: RuntimeCase): Promise<void> {
437
+ jest.spyOn(ModelAPI, "getItem").mockImplementation((): Promise<never> => {
438
+ return Promise.resolve(runtimeCase.buildHost() as never);
439
+ });
440
+
441
+ render(<runtimeCase.Overview {...pageProps} />);
442
+ await flush();
443
+ }
444
+
445
+ const FULL_START: Date = at("11:30");
446
+ const FULL_END: Date = NOW;
447
+ const ZOOM_START: Date = at("11:40");
448
+ const ZOOM_END: Date = at("11:50");
449
+
450
+ beforeEach(() => {
451
+ jest.useFakeTimers();
452
+ jest.setSystemTime(NOW);
453
+ aggregateRequests = [];
454
+ pendingAggregates = [];
455
+ answerAtOnce = true;
456
+ resetChartStandIns();
457
+
458
+ jest
459
+ .spyOn(Navigation, "getLastParamAsObjectID")
460
+ .mockImplementation((): ObjectID => {
461
+ return new ObjectID(HOST_ID);
462
+ });
463
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
464
+ serveAnalytics();
465
+ });
466
+
467
+ afterEach(() => {
468
+ cleanup();
469
+ jest.useRealTimers();
470
+ jest.restoreAllMocks();
471
+ window.localStorage.clear();
472
+ });
473
+
474
+ describe.each(RUNTIME_CASES)(
475
+ "%s host overview drag-to-zoom",
476
+ (_name: string, runtimeCase: RuntimeCase) => {
477
+ beforeEach(() => {
478
+ // No countdown ring or refresh timer unless a test asks for one.
479
+ window.localStorage.setItem(runtimeCase.refreshStorageKey, "off");
480
+ });
481
+
482
+ test("all six charts take one and the same page zoom, and name the gesture", async () => {
483
+ await renderPage(runtimeCase);
484
+
485
+ const zooms: Array<ChartTimeRangeZoomHandlers> = CHART_TITLES.map(
486
+ (title: string): ChartTimeRangeZoomHandlers => {
487
+ return zoomOf(title);
488
+ },
489
+ );
490
+
491
+ for (const zoom of zooms) {
492
+ expect(zoom.onTimeRangeSelect).toBeInstanceOf(Function);
493
+ // Nothing to undo yet, so no chart holds its clicks for a reset.
494
+ expect(zoom.onTimeRangeReset).toBeUndefined();
495
+ // One zoom, the page's: a drag on any chart retimes all of them.
496
+ expect(zoom.onTimeRangeSelect).toBe(zooms[0]!.onTimeRangeSelect);
497
+ }
498
+
499
+ for (const [heading, titles] of SECTIONS) {
500
+ const hint: HTMLElement = hintOf(heading);
501
+
502
+ expect(hint).toHaveTextContent("Drag to zoom");
503
+ expect(hint).not.toHaveTextContent("double-click");
504
+ // Shown while the pointer is over the section, on screens that hover.
505
+ expect(hint).toHaveClass(
506
+ "opacity-0",
507
+ "group-hover/zoomhint:opacity-100",
508
+ "max-lg:hidden",
509
+ );
510
+ // In the section's heading row, beside its title...
511
+ expect(hint.parentElement).toContainElement(
512
+ screen.getByRole("heading", { level: 2, name: heading }),
513
+ );
514
+
515
+ /*
516
+ * ...and not in the cards: four of them share a row, and a hint in
517
+ * their narrow headers pushed the titles onto two lines.
518
+ */
519
+ for (const title of titles) {
520
+ expect(section(heading)).toContainElement(chartCard(title));
521
+ expect(chartCard(title)).not.toHaveClass("group");
522
+ expect(
523
+ within(chartCard(title)).queryByTestId(
524
+ TIME_RANGE_ZOOM_HINT_TEST_ID,
525
+ ),
526
+ ).toBeNull();
527
+ }
528
+ }
529
+
530
+ // One hint per chart section; the tiles and lists carry none.
531
+ expect(screen.getAllByTestId(TIME_RANGE_ZOOM_HINT_TEST_ID)).toHaveLength(
532
+ SECTIONS.length,
533
+ );
534
+ expect(pickerLabel()).toBe("Past 30 Minutes");
535
+ expect(resetButtons()).toHaveLength(0);
536
+ });
537
+
538
+ test("before the drag, everything reads the past 30 minutes", async () => {
539
+ await renderPage(runtimeCase);
540
+
541
+ expect(new Set(windowsAskedSince(0))).toEqual(
542
+ new Set([windowText(FULL_START, FULL_END)]),
543
+ );
544
+ expect(aggregateRequests).toHaveLength(6);
545
+
546
+ for (const title of CHART_TITLES) {
547
+ expect(windowOf(title)).toBe(windowText(FULL_START, FULL_END));
548
+ }
549
+
550
+ expect(tileValue("Avg CPU")).toBe("20.0%");
551
+ expect(tileValue("Containers")).toBe("1");
552
+ expect(consumerRows("Top CPU Consumers")).toEqual(["api 20.0%"]);
553
+ expect(screen.queryByText("1 container")).toBeInTheDocument();
554
+ // The two missed heartbeat minutes are in this range.
555
+ expect(screen.queryByText("100.0% uptime")).not.toBeInTheDocument();
556
+ });
557
+
558
+ test("a drag on one chart retimes the whole page: every query, chart, tile, list and the badge", async () => {
559
+ await renderPage(runtimeCase);
560
+ const asked: number = aggregateRequests.length;
561
+
562
+ await dragAcross("Peak Memory", ZOOM_START, ZOOM_END);
563
+
564
+ // All six queries went out again, every one for the dragged window.
565
+ expect(windowsAskedSince(asked)).toEqual(
566
+ new Array(6).fill(windowText(ZOOM_START, ZOOM_END)),
567
+ );
568
+ for (const request of aggregateRequests.slice(asked)) {
569
+ expect(request.aggregateBy.startTimestamp.toISOString()).toBe(
570
+ ZOOM_START.toISOString(),
571
+ );
572
+ expect(request.aggregateBy.endTimestamp.toISOString()).toBe(
573
+ ZOOM_END.toISOString(),
574
+ );
575
+ }
576
+
577
+ // Every chart, not only the one dragged, now draws that window.
578
+ for (const title of CHART_TITLES) {
579
+ expect(windowOf(title)).toBe(windowText(ZOOM_START, ZOOM_END));
580
+ }
581
+
582
+ /*
583
+ * The tiles read the last 5 minutes of the NEW range, 11:45-11:50:
584
+ * (api 80 + batch 50) / 2 = 65 in four slots, api's 80 alone in two,
585
+ * so (4 x 65 + 2 x 80) / 6 = 70. Memory: (4 x 45 + 2 x 30) / 6 = 40.
586
+ */
587
+ expect(tileValue("Avg CPU")).toBe("70.0%");
588
+ expect(tileValue("Peak CPU")).toBe("80.0%");
589
+ expect(tileValue("Avg Memory")).toBe("40.0%");
590
+ expect(tileValue("Containers")).toBe("2");
591
+ expect(screen.getByText("2 containers")).toBeInTheDocument();
592
+ expect(consumerRows("Top CPU Consumers")).toEqual([
593
+ "api 80.0%",
594
+ "batch 50.0%",
595
+ ]);
596
+ expect(consumerRows("Top Memory Consumers")).toEqual([
597
+ "batch 60.0%",
598
+ "api 30.0%",
599
+ ]);
600
+ // No heartbeat was missed between 11:40 and 11:50.
601
+ expect(screen.getByText("100.0% uptime")).toBeInTheDocument();
602
+
603
+ // The picker says where the page is, and offers the way back.
604
+ expect(pickerLabel()).toBe(customLabel(ZOOM_START, ZOOM_END));
605
+ expect(resetButtons()).toHaveLength(1);
606
+ });
607
+
608
+ test("once zoomed, every chart offers the double-click and says so", async () => {
609
+ await renderPage(runtimeCase);
610
+
611
+ await dragAcross("Avg CPU", ZOOM_START, ZOOM_END);
612
+
613
+ for (const title of CHART_TITLES) {
614
+ expect(zoomOf(title).onTimeRangeReset).toBeInstanceOf(Function);
615
+ }
616
+ for (const [heading] of SECTIONS) {
617
+ expect(hintOf(heading)).toHaveTextContent("Double-click to reset");
618
+ }
619
+ });
620
+
621
+ test("a double-click on a DIFFERENT chart puts the original range back", async () => {
622
+ await renderPage(runtimeCase);
623
+
624
+ await dragAcross("Peak CPU", ZOOM_START, ZOOM_END);
625
+ const asked: number = aggregateRequests.length;
626
+
627
+ await doubleClick("Availability");
628
+
629
+ expect(windowsAskedSince(asked)).toEqual(
630
+ new Array(6).fill(windowText(FULL_START, FULL_END)),
631
+ );
632
+ for (const title of CHART_TITLES) {
633
+ expect(windowOf(title)).toBe(windowText(FULL_START, FULL_END));
634
+ expect(zoomOf(title).onTimeRangeReset).toBeUndefined();
635
+ }
636
+ expect(tileValue("Avg CPU")).toBe("20.0%");
637
+ expect(consumerRows("Top CPU Consumers")).toEqual(["api 20.0%"]);
638
+ // Back on the relative preset, so auto-refresh slides again.
639
+ expect(pickerLabel()).toBe("Past 30 Minutes");
640
+ expect(resetButtons()).toHaveLength(0);
641
+ });
642
+
643
+ test("after zooming twice, ONE double-click returns to the original range", async () => {
644
+ await renderPage(runtimeCase);
645
+
646
+ await dragAcross("Avg CPU", at("11:35"), at("11:55"));
647
+ await dragAcross("Avg Memory", ZOOM_START, ZOOM_END);
648
+
649
+ expect(pickerLabel()).toBe(customLabel(ZOOM_START, ZOOM_END));
650
+ const asked: number = aggregateRequests.length;
651
+
652
+ await doubleClick("Network");
653
+
654
+ expect(windowsAskedSince(asked)).toEqual(
655
+ new Array(6).fill(windowText(FULL_START, FULL_END)),
656
+ );
657
+ expect(pickerLabel()).toBe("Past 30 Minutes");
658
+ expect(resetButtons()).toHaveLength(0);
659
+ });
660
+
661
+ test("Reset zoom beside the picker does what a double-click does", async () => {
662
+ await renderPage(runtimeCase);
663
+
664
+ await dragAcross("Network", ZOOM_START, ZOOM_END);
665
+ const asked: number = aggregateRequests.length;
666
+
667
+ fireEvent.click(resetButtons()[0]!);
668
+ await flush();
669
+
670
+ expect(windowsAskedSince(asked)).toEqual(
671
+ new Array(6).fill(windowText(FULL_START, FULL_END)),
672
+ );
673
+ expect(pickerLabel()).toBe("Past 30 Minutes");
674
+ expect(resetButtons()).toHaveLength(0);
675
+ for (const title of CHART_TITLES) {
676
+ expect(windowOf(title)).toBe(windowText(FULL_START, FULL_END));
677
+ }
678
+ });
679
+
680
+ test("the empty Network chart still takes the drag and the double-click", async () => {
681
+ await renderPage(runtimeCase);
682
+
683
+ // No counters were reported, so the chart has nothing to draw...
684
+ expect(chart("Network").getAttribute("data-series")).toBe("");
685
+
686
+ // ...yet it zooms the page, and gets the page back.
687
+ await dragAcross("Network", ZOOM_START, ZOOM_END);
688
+ expect(windowOf("Avg CPU")).toBe(windowText(ZOOM_START, ZOOM_END));
689
+
690
+ await doubleClick("Network");
691
+ expect(windowOf("Avg CPU")).toBe(windowText(FULL_START, FULL_END));
692
+ });
693
+
694
+ test("picking a range in the picker ends the zoom instead of stacking on it", async () => {
695
+ await renderPage(runtimeCase);
696
+
697
+ await dragAcross("Peak Memory", ZOOM_START, ZOOM_END);
698
+
699
+ fireEvent.click(screen.getByTestId("telemetry-time-range-picker-button"));
700
+ const asked: number = aggregateRequests.length;
701
+ fireEvent.click(screen.getByRole("button", { name: "Past 1 Hour" }));
702
+ await flush();
703
+
704
+ expect(windowsAskedSince(asked)).toEqual(
705
+ new Array(6).fill(windowText(at("11:00"), NOW)),
706
+ );
707
+ expect(pickerLabel()).toBe("Past 1 Hour");
708
+ expect(resetButtons()).toHaveLength(0);
709
+ for (const title of CHART_TITLES) {
710
+ expect(zoomOf(title).onTimeRangeReset).toBeUndefined();
711
+ }
712
+
713
+ // And a later zoom returns to the hour just picked, not to 30 minutes.
714
+ await dragAcross("Avg CPU", ZOOM_START, ZOOM_END);
715
+ await doubleClick("Peak CPU");
716
+ expect(pickerLabel()).toBe("Past 1 Hour");
717
+ });
718
+
719
+ test("a drag that runs past the end of the range is cut off at its end", async () => {
720
+ await renderPage(runtimeCase);
721
+ const asked: number = aggregateRequests.length;
722
+
723
+ // The newest bucket is still filling: taken whole it reaches past now.
724
+ await dragAcross("Avg CPU", at("11:55"), at("12:05"));
725
+
726
+ expect(windowsAskedSince(asked)).toEqual(
727
+ new Array(6).fill(windowText(at("11:55"), NOW)),
728
+ );
729
+ });
730
+
731
+ test("a drag right-to-left zooms to the same window", async () => {
732
+ await renderPage(runtimeCase);
733
+ const asked: number = aggregateRequests.length;
734
+
735
+ await dragAcross("Avg CPU", ZOOM_END, ZOOM_START);
736
+
737
+ expect(windowsAskedSince(asked)).toEqual(
738
+ new Array(6).fill(windowText(ZOOM_START, ZOOM_END)),
739
+ );
740
+ });
741
+
742
+ test("a drag that never left its bucket, or a stray double-click, does nothing", async () => {
743
+ await renderPage(runtimeCase);
744
+ const asked: number = aggregateRequests.length;
745
+
746
+ await dragAcross("Avg CPU", ZOOM_START, ZOOM_START);
747
+ await doubleClick("Avg CPU");
748
+
749
+ expect(aggregateRequests).toHaveLength(asked);
750
+ expect(pickerLabel()).toBe("Past 30 Minutes");
751
+ expect(resetButtons()).toHaveLength(0);
752
+ });
753
+
754
+ test("auto-refresh keeps a zoomed window where it is, and slides again after the reset", async () => {
755
+ // The page's default: refresh every 30 seconds.
756
+ window.localStorage.removeItem(runtimeCase.refreshStorageKey);
757
+ await renderPage(runtimeCase);
758
+
759
+ await dragAcross("Avg CPU", ZOOM_START, ZOOM_END);
760
+ let asked: number = aggregateRequests.length;
761
+
762
+ await act(async () => {
763
+ jest.advanceTimersByTime(30_000);
764
+ });
765
+ await flush();
766
+
767
+ // The zoomed window is absolute: a refresh re-reads the same window.
768
+ expect(windowsAskedSince(asked)).toEqual(
769
+ new Array(6).fill(windowText(ZOOM_START, ZOOM_END)),
770
+ );
771
+ expect(pickerLabel()).toBe(customLabel(ZOOM_START, ZOOM_END));
772
+ expect(resetButtons()).toHaveLength(1);
773
+
774
+ asked = aggregateRequests.length;
775
+ await doubleClick("Avg Memory");
776
+
777
+ // Back on "past 30 minutes", resolved against the clock as it is now.
778
+ const later: Date = new Date(NOW.getTime() + 30_000);
779
+ expect(windowsAskedSince(asked)).toEqual(
780
+ new Array(6).fill(
781
+ windowText(new Date(later.getTime() - 30 * 60_000), later),
782
+ ),
783
+ );
784
+ });
785
+
786
+ test("a slow answer for the window just left cannot overwrite the page", async () => {
787
+ await renderPage(runtimeCase);
788
+ answerAtOnce = false;
789
+
790
+ // Zoom, and change your mind before the zoomed data has arrived.
791
+ await dragAcross("Avg CPU", ZOOM_START, ZOOM_END);
792
+ await doubleClick("Peak CPU");
793
+
794
+ const zoomAnswers: Array<PendingAggregate> = pendingAggregates.filter(
795
+ (pending: PendingAggregate): boolean => {
796
+ return (
797
+ pending.request.aggregateBy.query.time.startValue.getTime() ===
798
+ ZOOM_START.getTime()
799
+ );
800
+ },
801
+ );
802
+ const resetAnswers: Array<PendingAggregate> = pendingAggregates.filter(
803
+ (pending: PendingAggregate): boolean => {
804
+ return (
805
+ pending.request.aggregateBy.query.time.startValue.getTime() ===
806
+ FULL_START.getTime()
807
+ );
808
+ },
809
+ );
810
+
811
+ expect(zoomAnswers).toHaveLength(6);
812
+ expect(resetAnswers).toHaveLength(6);
813
+
814
+ // The newer fetch lands first; the one for the zoomed window after it.
815
+ for (const pending of resetAnswers) {
816
+ pending.answer();
817
+ }
818
+ await flush();
819
+ for (const pending of zoomAnswers) {
820
+ pending.answer();
821
+ }
822
+ await flush();
823
+
824
+ expect(pickerLabel()).toBe("Past 30 Minutes");
825
+ for (const title of CHART_TITLES) {
826
+ expect(windowOf(title)).toBe(windowText(FULL_START, FULL_END));
827
+ }
828
+ expect(tileValue("Avg CPU")).toBe("20.0%");
829
+ expect(consumerRows("Top CPU Consumers")).toEqual(["api 20.0%"]);
830
+ });
831
+
832
+ test("the refresh button while zoomed re-reads the zoomed window and keeps the zoom", async () => {
833
+ await renderPage(runtimeCase);
834
+
835
+ await dragAcross("Avg CPU", ZOOM_START, ZOOM_END);
836
+ const asked: number = aggregateRequests.length;
837
+
838
+ fireEvent.click(screen.getByRole("button", { name: "Refresh" }));
839
+ await flush();
840
+
841
+ expect(windowsAskedSince(asked)).toEqual(
842
+ new Array(6).fill(windowText(ZOOM_START, ZOOM_END)),
843
+ );
844
+ expect(resetButtons()).toHaveLength(1);
845
+ expect(zoomOf("Availability").onTimeRangeReset).toBeInstanceOf(Function);
846
+ });
847
+ },
848
+ );