@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,1025 @@
1
+ /** @timezone UTC */
2
+ import "@testing-library/jest-dom";
3
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
4
+ import {
5
+ RenderResult,
6
+ act,
7
+ cleanup,
8
+ fireEvent,
9
+ render,
10
+ } from "@testing-library/react";
11
+ import React from "react";
12
+ import getJestMockFunction, { MockFunction } from "../../../MockType";
13
+
14
+ /*
15
+ * Issue #4116: a double-click resets a zoomed chart even when the browser
16
+ * never sends the chart its dblclick.
17
+ *
18
+ * Right after a drag zooms a page, its charts refetch, and the reader
19
+ * double-clicks to undo the zoom at exactly that moment. When the new rows
20
+ * land during the double-click's second press, recharts can draw the node
21
+ * that press landed on afresh (it re-keys a line's path), and Chrome then
22
+ * sends the press no click and no dblclick. The line, area and bar charts
23
+ * used to reset on the dblclick alone, so the page stayed zoomed - and a
24
+ * moment later the first click, held back to tell it apart from a
25
+ * double-click, acted on top.
26
+ *
27
+ * The chart now takes the second press itself as the reset: its
28
+ * MouseEvent.detail is 2 whatever becomes of the node it landed on, and its
29
+ * release arms the reset for the next task. A dblclick that does arrive
30
+ * comes before that task, resets, and stands the fallback down.
31
+ *
32
+ * Driven here through the line, area and bar charts a page renders, on REAL
33
+ * recharts: only ResponsiveContainer is stood in for (it measures a parent
34
+ * jsdom reports as 0x0), as in ChartRangeSelectionRealRecharts.test.tsx,
35
+ * and the pointer goes to the x recharts drew each bucket's tick label at.
36
+ * jsdom drops no event, so each test fires exactly what Chrome delivers;
37
+ * for a double-click that lost its dblclick that is mousedown, mouseup and
38
+ * click with detail 1, then mousedown and mouseup with detail 2, and
39
+ * nothing more. Pinned, for each chart:
40
+ *
41
+ * - such a double-click resets once, on the next task (not during the
42
+ * release), and the first click's held-back action never runs - also
43
+ * when the second click arrives without its dblclick, and when the
44
+ * second release lands off the chart;
45
+ * - one that keeps its dblclick resets exactly once, from the dblclick;
46
+ * - a single click still acts, once the double-click window has passed;
47
+ * - with nothing to reset, the second press is an ordinary press and the
48
+ * clicks act at once, as they always have;
49
+ * - a chart handed only a reset (it offers no drag) resets the same way -
50
+ * the bar chart from a bar or from its bare plot;
51
+ * - new rows landing between the second press and its release neither
52
+ * stop the reset nor turn the press into a drag across old and new
53
+ * buckets - including on a page zoomed a moment before.
54
+ *
55
+ * A plain click on a line or area chart's plot reports the bucket under it
56
+ * (onBucketClick). The bar chart's wrapper takes no bucket-click handler,
57
+ * so its gestures land on a bar, whose click highlights it: that is the
58
+ * plot click there is to hold back.
59
+ */
60
+
61
+ jest.mock("recharts", () => {
62
+ const actual: Record<string, any> = jest.requireActual("recharts");
63
+ const react: typeof React = jest.requireActual("react") as typeof React;
64
+ return {
65
+ ...actual,
66
+ ResponsiveContainer: ({ children }: { children: React.ReactElement }) => {
67
+ return react.cloneElement(children, { width: 600, height: 300 });
68
+ },
69
+ };
70
+ });
71
+
72
+ import AreaChartElement from "../../../../UI/Components/Charts/Area/AreaChart";
73
+ import BarChartElement from "../../../../UI/Components/Charts/Bar/BarChart";
74
+ import LineChartElement from "../../../../UI/Components/Charts/Line/LineChart";
75
+ import { DOUBLE_CLICK_DISAMBIGUATION_MS } from "../../../../UI/Components/Charts/ChartLibrary/Utils/DoubleClick";
76
+ import { TimeRangeZoomScope } from "../../../../UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext";
77
+ import ChartCurve from "../../../../UI/Components/Charts/Types/ChartCurve";
78
+ import DataPoint from "../../../../UI/Components/Charts/Types/DataPoint";
79
+ import SeriesPoint from "../../../../UI/Components/Charts/Types/SeriesPoints";
80
+ import {
81
+ XAxis as ChartXAxis,
82
+ XAxisAggregateType,
83
+ } from "../../../../UI/Components/Charts/Types/XAxis/XAxis";
84
+ import XAxisPrecision from "../../../../UI/Components/Charts/Types/XAxis/XAxisPrecision";
85
+ import XAxisType from "../../../../UI/Components/Charts/Types/XAxis/XAxisType";
86
+ import YAxis, {
87
+ YAxisPrecision,
88
+ } from "../../../../UI/Components/Charts/Types/YAxis/YAxis";
89
+ import YAxisType from "../../../../UI/Components/Charts/Types/YAxis/YAxisType";
90
+ import InBetween from "../../../../Types/BaseDatabase/InBetween";
91
+ import RangeStartAndEndDateTime from "../../../../Types/Time/RangeStartAndEndDateTime";
92
+ import TimeRange from "../../../../Types/Time/TimeRange";
93
+
94
+ const MINUTE_MS: number = 60 * 1000;
95
+ // One animation frame, as fake timers run them.
96
+ const FRAME_MS: number = 16;
97
+ // Every chart here draws one-minute buckets from 10:00 on.
98
+ const FIRST_BUCKET_MS: number = Date.parse("2026-09-28T10:00:00.000Z");
99
+ // Inside the plot: below its top edge and above the x-axis.
100
+ const PLOT_Y: number = 120;
101
+ // Somewhere on the page, well away from the chart.
102
+ const OFF_CHART: { clientX: number; clientY: number } = {
103
+ clientX: 900,
104
+ clientY: 520,
105
+ };
106
+
107
+ type SelectHandler = (startTime: Date, endTime: Date) => void;
108
+
109
+ type BucketClickHandler = (
110
+ bucketStart: Date,
111
+ bucketEnd: Date,
112
+ valuesAtBucket: Record<string, number | string>,
113
+ ) => void;
114
+
115
+ interface ChartHandlers {
116
+ onTimeRangeSelect?: SelectHandler | undefined;
117
+ onTimeRangeReset?: (() => void) | undefined;
118
+ onBucketClick?: BucketClickHandler | undefined;
119
+ }
120
+
121
+ // The rows a chart draws: one-minute buckets from 10:00 on.
122
+ interface ChartRows {
123
+ buckets: number;
124
+ series: Array<SeriesPoint>;
125
+ xAxis: ChartXAxis;
126
+ }
127
+
128
+ function rowsOf(buckets: number): ChartRows {
129
+ const data: Array<DataPoint> = [];
130
+ for (let index: number = 0; index < buckets; index++) {
131
+ data.push({
132
+ x: new Date(FIRST_BUCKET_MS + index * MINUTE_MS),
133
+ y: 40 + index,
134
+ });
135
+ }
136
+
137
+ return {
138
+ buckets: buckets,
139
+ series: [{ seriesName: "CPU", data: data }],
140
+ xAxis: {
141
+ legend: "Time",
142
+ options: {
143
+ type: XAxisType.Time,
144
+ min: new Date(FIRST_BUCKET_MS),
145
+ max: new Date(FIRST_BUCKET_MS + buckets * MINUTE_MS),
146
+ aggregateType: XAxisAggregateType.Average,
147
+ precision: XAxisPrecision.EVERY_MINUTE,
148
+ },
149
+ },
150
+ };
151
+ }
152
+
153
+ /*
154
+ * Built once, so a page that re-renders hands its chart the same rows and
155
+ * the chart redraws only when a test lands new ones.
156
+ */
157
+ // A zoomed chart: 10:00 to 10:09.
158
+ const TEN_BUCKETS: ChartRows = rowsOf(10);
159
+ // The page before that zoom: 10:00 to 10:29.
160
+ const THIRTY_BUCKETS: ChartRows = rowsOf(30);
161
+
162
+ // The page range the rows cover.
163
+ function rangeOf(rows: ChartRows): RangeStartAndEndDateTime {
164
+ return {
165
+ range: TimeRange.CUSTOM,
166
+ startAndEndDate: new InBetween<Date>(
167
+ new Date(FIRST_BUCKET_MS),
168
+ new Date(FIRST_BUCKET_MS + rows.buckets * MINUTE_MS),
169
+ ),
170
+ };
171
+ }
172
+
173
+ const Y_AXIS: YAxis = {
174
+ legend: "%",
175
+ options: {
176
+ type: YAxisType.Number,
177
+ min: 0,
178
+ max: 200,
179
+ precision: YAxisPrecision.NoDecimals,
180
+ formatter: (value: number): string => {
181
+ return `${value}%`;
182
+ },
183
+ },
184
+ };
185
+
186
+ let rendered: RenderResult | null = null;
187
+
188
+ function show(chart: React.ReactElement): void {
189
+ rendered = render(chart);
190
+ }
191
+
192
+ function renderResult(): RenderResult {
193
+ if (!rendered) {
194
+ throw new Error("no chart rendered");
195
+ }
196
+ return rendered;
197
+ }
198
+
199
+ function chartContainer(): HTMLElement {
200
+ return renderResult().container;
201
+ }
202
+
203
+ // The element recharts listens on: every event on the chart bubbles to it.
204
+ function chartWrapper(): Element {
205
+ const wrapper: Element | null =
206
+ chartContainer().querySelector(".recharts-wrapper");
207
+ if (!wrapper) {
208
+ throw new Error("recharts drew no chart");
209
+ }
210
+ return wrapper;
211
+ }
212
+
213
+ /*
214
+ * The pointer x that lands on a bucket: where recharts drew its tick label,
215
+ * the centre of the bucket on both kinds of axis. jsdom has no layout, so
216
+ * the chart's box sits at 0,0 and a clientX is an x inside the chart.
217
+ */
218
+ function tickX(label: string): number {
219
+ const tick: Element | undefined = Array.from(
220
+ chartContainer().querySelectorAll(
221
+ ".recharts-xAxis-tick-labels text.recharts-cartesian-axis-tick-value",
222
+ ),
223
+ ).find((element: Element): boolean => {
224
+ return element.textContent === label;
225
+ });
226
+ if (!tick) {
227
+ throw new Error(`recharts drew no tick label "${label}"`);
228
+ }
229
+ return Number(tick.getAttribute("x"));
230
+ }
231
+
232
+ function spotOf(label: string): { clientX: number; clientY: number } {
233
+ return { clientX: tickX(label), clientY: PLOT_Y };
234
+ }
235
+
236
+ // "10:06" -> the instant that minute starts, as the handlers report it.
237
+ function at(label: string): string {
238
+ return `2026-09-28T${label}:00.000Z`;
239
+ }
240
+
241
+ // "10:06" -> 6: the row, and the bar, that draws that minute.
242
+ function rowIndexOf(label: string): number {
243
+ return (Date.parse(at(label)) - FIRST_BUCKET_MS) / MINUTE_MS;
244
+ }
245
+
246
+ function bars(): Array<Element> {
247
+ return Array.from(
248
+ chartContainer().querySelectorAll(".recharts-bar-rectangle path"),
249
+ );
250
+ }
251
+
252
+ function isDimmed(bar: Element): boolean {
253
+ return (
254
+ bar.getAttribute("opacity") === "0.3" ||
255
+ bar.getAttribute("fill-opacity") === "0.3"
256
+ );
257
+ }
258
+
259
+ // Where the pointer is over a bucket: the element a press there lands on.
260
+ type PointerTarget = (label: string) => Element;
261
+
262
+ // The plot itself, off every line, area and bar.
263
+ const THE_PLOT: PointerTarget = (): Element => {
264
+ return chartWrapper();
265
+ };
266
+
267
+ // The bar drawn for the bucket; bars are drawn in row order.
268
+ const THE_BAR: PointerTarget = (label: string): Element => {
269
+ const bar: Element | undefined = bars()[rowIndexOf(label)];
270
+ if (!bar) {
271
+ throw new Error(`recharts drew no bar for "${label}"`);
272
+ }
273
+ return bar;
274
+ };
275
+
276
+ interface ChartUnderTest {
277
+ name: string;
278
+ kind: "line" | "area" | "bar";
279
+ // Where its gestures land: see the note at the top of the file.
280
+ target: PointerTarget;
281
+ render: (rows: ChartRows, handlers: ChartHandlers) => React.ReactElement;
282
+ }
283
+
284
+ const LINE_CHART: ChartUnderTest = {
285
+ name: "line",
286
+ kind: "line",
287
+ target: THE_PLOT,
288
+ render: (rows: ChartRows, handlers: ChartHandlers): React.ReactElement => {
289
+ return (
290
+ <LineChartElement
291
+ data={rows.series}
292
+ xAxis={rows.xAxis}
293
+ yAxis={Y_AXIS}
294
+ curve={ChartCurve.LINEAR}
295
+ heightInPx={300}
296
+ showLegend={false}
297
+ sync={false}
298
+ syncid="reset-without-dblclick-line"
299
+ onTimeRangeSelect={handlers.onTimeRangeSelect}
300
+ onTimeRangeReset={handlers.onTimeRangeReset}
301
+ onBucketClick={handlers.onBucketClick}
302
+ />
303
+ );
304
+ },
305
+ };
306
+
307
+ const AREA_CHART: ChartUnderTest = {
308
+ name: "area",
309
+ kind: "area",
310
+ target: THE_PLOT,
311
+ render: (rows: ChartRows, handlers: ChartHandlers): React.ReactElement => {
312
+ return (
313
+ <AreaChartElement
314
+ data={rows.series}
315
+ xAxis={rows.xAxis}
316
+ yAxis={Y_AXIS}
317
+ curve={ChartCurve.LINEAR}
318
+ heightInPx={300}
319
+ showLegend={false}
320
+ sync={false}
321
+ syncid="reset-without-dblclick-area"
322
+ onTimeRangeSelect={handlers.onTimeRangeSelect}
323
+ onTimeRangeReset={handlers.onTimeRangeReset}
324
+ onBucketClick={handlers.onBucketClick}
325
+ />
326
+ );
327
+ },
328
+ };
329
+
330
+ const BAR_CHART: ChartUnderTest = {
331
+ name: "bar",
332
+ kind: "bar",
333
+ target: THE_BAR,
334
+ render: (rows: ChartRows, handlers: ChartHandlers): React.ReactElement => {
335
+ return (
336
+ <BarChartElement
337
+ data={rows.series}
338
+ xAxis={rows.xAxis}
339
+ yAxis={Y_AXIS}
340
+ heightInPx={300}
341
+ showLegend={false}
342
+ sync={false}
343
+ syncid="reset-without-dblclick-bar"
344
+ onTimeRangeSelect={handlers.onTimeRangeSelect}
345
+ onTimeRangeReset={handlers.onTimeRangeReset}
346
+ />
347
+ );
348
+ },
349
+ };
350
+
351
+ const CHARTS: Array<ChartUnderTest> = [LINE_CHART, AREA_CHART, BAR_CHART];
352
+
353
+ // Lets one animation frame pass: fake timers run the frame's callbacks.
354
+ function nextFrame(): void {
355
+ act(() => {
356
+ jest.advanceTimersByTime(FRAME_MS);
357
+ });
358
+ }
359
+
360
+ /*
361
+ * Lets the task the events arrived in end: whatever was set for 0ms runs,
362
+ * nothing set for later does. In Chrome the dblclick, when it comes at all,
363
+ * comes before this.
364
+ */
365
+ function nextTask(): void {
366
+ act(() => {
367
+ jest.advanceTimersByTime(0);
368
+ });
369
+ }
370
+
371
+ function waitOutTheDoubleClickWindow(): void {
372
+ act(() => {
373
+ jest.advanceTimersByTime(DOUBLE_CLICK_DISAMBIGUATION_MS);
374
+ });
375
+ }
376
+
377
+ // Well past every timer a gesture here sets.
378
+ function waitWellPastTheDoubleClickWindow(): void {
379
+ act(() => {
380
+ jest.advanceTimersByTime(DOUBLE_CLICK_DISAMBIGUATION_MS * 4);
381
+ });
382
+ }
383
+
384
+ /*
385
+ * The pointer comes to rest over a bucket. A chart that offers no drag
386
+ * works out the bucket under the pointer a frame after it moves, so let
387
+ * that frame pass, as a hand arriving there does.
388
+ */
389
+ function restOn(label: string): void {
390
+ fireEvent.mouseMove(chartWrapper(), { ...spotOf(label), buttons: 0 });
391
+ nextFrame();
392
+ }
393
+
394
+ // The pointer moves over a bucket with the main button held.
395
+ function dragTo(label: string): void {
396
+ fireEvent.mouseMove(chartWrapper(), { ...spotOf(label), buttons: 1 });
397
+ }
398
+
399
+ // clickCount is MouseEvent.detail: 2 on both halves of the second click.
400
+ function press(target: PointerTarget, label: string, clickCount: number): void {
401
+ fireEvent.mouseDown(target(label), {
402
+ ...spotOf(label),
403
+ button: 0,
404
+ buttons: 1,
405
+ detail: clickCount,
406
+ });
407
+ }
408
+
409
+ function release(
410
+ target: PointerTarget,
411
+ label: string,
412
+ clickCount: number,
413
+ ): void {
414
+ fireEvent.mouseUp(target(label), {
415
+ ...spotOf(label),
416
+ button: 0,
417
+ buttons: 0,
418
+ detail: clickCount,
419
+ });
420
+ }
421
+
422
+ function click(target: PointerTarget, label: string, clickCount: number): void {
423
+ fireEvent.click(target(label), {
424
+ ...spotOf(label),
425
+ button: 0,
426
+ detail: clickCount,
427
+ });
428
+ }
429
+
430
+ function doubleClick(target: PointerTarget, label: string): void {
431
+ fireEvent.doubleClick(target(label), {
432
+ ...spotOf(label),
433
+ button: 0,
434
+ detail: 2,
435
+ });
436
+ }
437
+
438
+ // All Chrome delivers for the first click of a double-click.
439
+ function firstClickOf(target: PointerTarget, label: string): void {
440
+ press(target, label, 1);
441
+ release(target, label, 1);
442
+ click(target, label, 1);
443
+ }
444
+
445
+ /*
446
+ * The second click of a double-click whose node left the page during its
447
+ * press: Chrome delivers the press and the release, counted (detail 2), and
448
+ * neither its click nor the dblclick.
449
+ */
450
+ function secondPressAndReleaseOf(target: PointerTarget, label: string): void {
451
+ press(target, label, 2);
452
+ release(target, label, 2);
453
+ }
454
+
455
+ /*
456
+ * The pointer leaves the chart with the button held and lets go on the
457
+ * page: only the page hears that release, and the click and the dblclick
458
+ * that follow it go to the page too.
459
+ */
460
+ function releaseOnThePage(clickCount: number): void {
461
+ fireEvent.mouseLeave(chartWrapper(), {
462
+ relatedTarget: document.body,
463
+ ...OFF_CHART,
464
+ buttons: 1,
465
+ });
466
+ fireEvent.mouseMove(document.body, { ...OFF_CHART, buttons: 1 });
467
+ fireEvent.mouseUp(document.body, {
468
+ ...OFF_CHART,
469
+ button: 0,
470
+ buttons: 0,
471
+ detail: clickCount,
472
+ });
473
+ }
474
+
475
+ // The live drag-selection band recharts drew, if any.
476
+ function drawnBand(): Element | null {
477
+ return chartContainer().querySelector(".recharts-reference-area-rect");
478
+ }
479
+
480
+ // Every bucket a chart reported a click on, oldest first.
481
+ function bucketClicks(onBucketClick: MockFunction): Array<[string, string]> {
482
+ return onBucketClick.mock.calls.map(
483
+ (call: Array<unknown>): [string, string] => {
484
+ return [(call[0] as Date).toISOString(), (call[1] as Date).toISOString()];
485
+ },
486
+ );
487
+ }
488
+
489
+ // The one-minute bucket that starts at the label.
490
+ function bucketAt(label: string): [string, string] {
491
+ const start: number = Date.parse(at(label));
492
+ return [
493
+ new Date(start).toISOString(),
494
+ new Date(start + MINUTE_MS).toISOString(),
495
+ ];
496
+ }
497
+
498
+ /*
499
+ * How many plot clicks on the bucket at `label` have acted. A line or an
500
+ * area chart reports each one as a bucket click. On the bar chart a click
501
+ * on a bar highlights it (dimming every other bar) and a second one clears
502
+ * that again, so there only an odd or an even count shows.
503
+ */
504
+ function expectClicksActed(
505
+ chart: ChartUnderTest,
506
+ onBucketClick: MockFunction,
507
+ label: string,
508
+ count: number,
509
+ ): void {
510
+ if (chart.kind !== "bar") {
511
+ const expected: Array<[string, string]> = [];
512
+ for (let index: number = 0; index < count; index++) {
513
+ expected.push(bucketAt(label));
514
+ }
515
+ expect(bucketClicks(onBucketClick)).toEqual(expected);
516
+ return;
517
+ }
518
+
519
+ const dimmed: number = bars().filter(isDimmed).length;
520
+ if (count % 2 === 0) {
521
+ expect(dimmed).toBe(0);
522
+ return;
523
+ }
524
+ expect(bars().length).toBeGreaterThan(5);
525
+ expect(dimmed).toBe(bars().length - 1);
526
+ expect(isDimmed(THE_BAR(label))).toBe(false);
527
+ }
528
+
529
+ // Every range a page was set to, oldest first.
530
+ function pageRanges(onTimeRangeChange: MockFunction): Array<[string, string]> {
531
+ return onTimeRangeChange.mock.calls.map(
532
+ (call: Array<unknown>): [string, string] => {
533
+ const range: RangeStartAndEndDateTime =
534
+ call[0] as RangeStartAndEndDateTime;
535
+ return [
536
+ range.startAndEndDate!.startValue.toISOString(),
537
+ range.startAndEndDate!.endValue.toISOString(),
538
+ ];
539
+ },
540
+ );
541
+ }
542
+
543
+ // "10:02", "10:06" -> the page range [10:02, 10:06).
544
+ function pageRange(from: string, to: string): [string, string] {
545
+ return [at(from), at(to)];
546
+ }
547
+
548
+ interface ZoomablePageProps {
549
+ chart: ChartUnderTest;
550
+ // The page's range before any zoom.
551
+ range: RangeStartAndEndDateTime;
552
+ /*
553
+ * The rows the page's latest fetch returned. A page refetches when its
554
+ * range changes, so these lag its range until the test lands them.
555
+ */
556
+ rows: ChartRows;
557
+ onBucketClick?: BucketClickHandler | undefined;
558
+ // Called with every range the page is set to.
559
+ onTimeRangeChange: (timeRange: RangeStartAndEndDateTime) => void;
560
+ }
561
+
562
+ /*
563
+ * A page shaped like the explorers and the overviews: it owns its range,
564
+ * offers every chart below a zoom over it (TimeRangeZoomScope), and hands
565
+ * its chart no zoom handler of its own.
566
+ */
567
+ const ZoomablePage: React.FunctionComponent<ZoomablePageProps> = (
568
+ props: ZoomablePageProps,
569
+ ): React.ReactElement => {
570
+ const [timeRange, setTimeRange] = React.useState<RangeStartAndEndDateTime>(
571
+ props.range,
572
+ );
573
+
574
+ return (
575
+ <TimeRangeZoomScope
576
+ timeRange={timeRange}
577
+ onTimeRangeChange={(next: RangeStartAndEndDateTime): void => {
578
+ props.onTimeRangeChange(next);
579
+ setTimeRange(next);
580
+ }}
581
+ >
582
+ {props.chart.render(props.rows, { onBucketClick: props.onBucketClick })}
583
+ </TimeRangeZoomScope>
584
+ );
585
+ };
586
+
587
+ interface Spies {
588
+ onTimeRangeSelect: MockFunction;
589
+ onTimeRangeReset: MockFunction;
590
+ onBucketClick: MockFunction;
591
+ }
592
+
593
+ function newSpies(): Spies {
594
+ return {
595
+ onTimeRangeSelect: getJestMockFunction(),
596
+ onTimeRangeReset: getJestMockFunction(),
597
+ onBucketClick: getJestMockFunction(),
598
+ };
599
+ }
600
+
601
+ // What a zoomed chart is offered: always the reset.
602
+ interface ResetOffer {
603
+ name: string;
604
+ // Whether a drag can zoom it further too.
605
+ canDrag: boolean;
606
+ }
607
+
608
+ const ZOOMED_WITH_DRAG: ResetOffer = {
609
+ name: "zoomed, where a drag can zoom it further",
610
+ canDrag: true,
611
+ };
612
+
613
+ /*
614
+ * A host that resets but does not zoom: a chart that cannot start a zoom
615
+ * must still end one, such as the zoom another chart on the page made.
616
+ */
617
+ const ZOOMED_RESET_ONLY: ResetOffer = {
618
+ name: "with only a reset on offer",
619
+ canDrag: false,
620
+ };
621
+
622
+ const RESET_OFFERS: Array<ResetOffer> = [ZOOMED_WITH_DRAG, ZOOMED_RESET_ONLY];
623
+
624
+ function handlersFor(offer: ResetOffer, spies: Spies): ChartHandlers {
625
+ return {
626
+ onTimeRangeSelect: offer.canDrag ? spies.onTimeRangeSelect : undefined,
627
+ onTimeRangeReset: spies.onTimeRangeReset,
628
+ onBucketClick: spies.onBucketClick,
629
+ };
630
+ }
631
+
632
+ beforeEach(() => {
633
+ /*
634
+ * Installed AT the fixed time rather than moved there with setSystemTime:
635
+ * fake timers count animation frames from the moment they are installed.
636
+ */
637
+ jest.useFakeTimers({ now: new Date("2026-09-28T10:45:00.000Z") });
638
+ });
639
+
640
+ afterEach(() => {
641
+ cleanup();
642
+ rendered = null;
643
+ jest.useRealTimers();
644
+ });
645
+
646
+ for (const chart of CHARTS) {
647
+ describe(`the ${chart.name} chart, through real recharts`, () => {
648
+ for (const offer of RESET_OFFERS) {
649
+ describe(offer.name, () => {
650
+ test("a double-click whose dblclick never arrives resets once, on the next task, and its first click never acts", () => {
651
+ const spies: Spies = newSpies();
652
+ show(chart.render(TEN_BUCKETS, handlersFor(offer, spies)));
653
+
654
+ restOn("10:04");
655
+ firstClickOf(chart.target, "10:04");
656
+ secondPressAndReleaseOf(chart.target, "10:04");
657
+
658
+ // Not during the release: a dblclick may still be on its way.
659
+ expect(spies.onTimeRangeReset).not.toHaveBeenCalled();
660
+ nextTask();
661
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
662
+
663
+ waitWellPastTheDoubleClickWindow();
664
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
665
+ // The click it held back for the double-click window is dropped.
666
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 0);
667
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
668
+ expect(drawnBand()).toBeNull();
669
+ });
670
+
671
+ test("a double-click whose second click arrives but its dblclick does not resets once, and neither click acts", () => {
672
+ const spies: Spies = newSpies();
673
+ show(chart.render(TEN_BUCKETS, handlersFor(offer, spies)));
674
+
675
+ restOn("10:04");
676
+ firstClickOf(chart.target, "10:04");
677
+ secondPressAndReleaseOf(chart.target, "10:04");
678
+ click(chart.target, "10:04", 2);
679
+ expect(spies.onTimeRangeReset).not.toHaveBeenCalled();
680
+
681
+ nextTask();
682
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
683
+
684
+ waitWellPastTheDoubleClickWindow();
685
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
686
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 0);
687
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
688
+ });
689
+
690
+ test("a double-click that keeps its dblclick resets exactly once, from the dblclick, and neither click acts", () => {
691
+ const spies: Spies = newSpies();
692
+ show(chart.render(TEN_BUCKETS, handlersFor(offer, spies)));
693
+
694
+ restOn("10:04");
695
+ firstClickOf(chart.target, "10:04");
696
+ secondPressAndReleaseOf(chart.target, "10:04");
697
+ click(chart.target, "10:04", 2);
698
+ expect(spies.onTimeRangeReset).not.toHaveBeenCalled();
699
+
700
+ doubleClick(chart.target, "10:04");
701
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
702
+
703
+ // The reset the release armed was stood down: no second one.
704
+ nextTask();
705
+ waitWellPastTheDoubleClickWindow();
706
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
707
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 0);
708
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
709
+ expect(drawnBand()).toBeNull();
710
+ });
711
+
712
+ test("a double-click whose second release lands off the chart resets once, on the next task", () => {
713
+ const spies: Spies = newSpies();
714
+ show(chart.render(TEN_BUCKETS, handlersFor(offer, spies)));
715
+
716
+ restOn("10:04");
717
+ firstClickOf(chart.target, "10:04");
718
+ press(chart.target, "10:04", 2);
719
+ // The chart hears no release, no click and no dblclick.
720
+ releaseOnThePage(2);
721
+ expect(spies.onTimeRangeReset).not.toHaveBeenCalled();
722
+
723
+ nextTask();
724
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
725
+
726
+ waitWellPastTheDoubleClickWindow();
727
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
728
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 0);
729
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
730
+ expect(drawnBand()).toBeNull();
731
+ });
732
+
733
+ test("a single click still acts, once the double-click window has passed, and resets nothing", () => {
734
+ const spies: Spies = newSpies();
735
+ show(chart.render(TEN_BUCKETS, handlersFor(offer, spies)));
736
+
737
+ restOn("10:04");
738
+ firstClickOf(chart.target, "10:04");
739
+ nextTask();
740
+ // Held open: a second click may be coming.
741
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 0);
742
+
743
+ waitOutTheDoubleClickWindow();
744
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 1);
745
+ if (chart.kind !== "bar") {
746
+ expect(spies.onBucketClick.mock.calls[0]![2]).toMatchObject({
747
+ CPU: 44,
748
+ });
749
+ }
750
+
751
+ waitWellPastTheDoubleClickWindow();
752
+ expectClicksActed(chart, spies.onBucketClick, "10:04", 1);
753
+ expect(spies.onTimeRangeReset).not.toHaveBeenCalled();
754
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
755
+ });
756
+
757
+ if (!offer.canDrag) {
758
+ test("it offers no drag: the plot keeps recharts' own cursor, not the crosshair", () => {
759
+ show(chart.render(TEN_BUCKETS, handlersFor(offer, newSpies())));
760
+
761
+ expect(chartWrapper()).toHaveStyle({ cursor: "default" });
762
+ });
763
+ }
764
+ });
765
+ }
766
+
767
+ describe("with nothing to reset: on a page that offers a zoom, before any zoom", () => {
768
+ function showPage(
769
+ onTimeRangeChange: MockFunction,
770
+ onBucketClick: MockFunction,
771
+ ): void {
772
+ show(
773
+ <ZoomablePage
774
+ chart={chart}
775
+ range={rangeOf(TEN_BUCKETS)}
776
+ rows={TEN_BUCKETS}
777
+ onBucketClick={onBucketClick}
778
+ onTimeRangeChange={onTimeRangeChange}
779
+ />,
780
+ );
781
+ }
782
+
783
+ test("a double-click is two clicks that each act at once, and the page's range stays put", () => {
784
+ const onTimeRangeChange: MockFunction = getJestMockFunction();
785
+ const onBucketClick: MockFunction = getJestMockFunction();
786
+ showPage(onTimeRangeChange, onBucketClick);
787
+
788
+ restOn("10:04");
789
+ firstClickOf(chart.target, "10:04");
790
+ expectClicksActed(chart, onBucketClick, "10:04", 1);
791
+
792
+ secondPressAndReleaseOf(chart.target, "10:04");
793
+ click(chart.target, "10:04", 2);
794
+ expectClicksActed(chart, onBucketClick, "10:04", 2);
795
+
796
+ doubleClick(chart.target, "10:04");
797
+ nextTask();
798
+ waitWellPastTheDoubleClickWindow();
799
+
800
+ expectClicksActed(chart, onBucketClick, "10:04", 2);
801
+ expect(onTimeRangeChange).not.toHaveBeenCalled();
802
+ expect(drawnBand()).toBeNull();
803
+ });
804
+
805
+ test("a double-click whose dblclick never arrives: its first click acted at once, and its second press does nothing", () => {
806
+ const onTimeRangeChange: MockFunction = getJestMockFunction();
807
+ const onBucketClick: MockFunction = getJestMockFunction();
808
+ showPage(onTimeRangeChange, onBucketClick);
809
+
810
+ restOn("10:04");
811
+ firstClickOf(chart.target, "10:04");
812
+ expectClicksActed(chart, onBucketClick, "10:04", 1);
813
+
814
+ secondPressAndReleaseOf(chart.target, "10:04");
815
+ nextTask();
816
+ waitWellPastTheDoubleClickWindow();
817
+
818
+ expectClicksActed(chart, onBucketClick, "10:04", 1);
819
+ expect(onTimeRangeChange).not.toHaveBeenCalled();
820
+ expect(drawnBand()).toBeNull();
821
+ });
822
+
823
+ test("the second press of a double-click is an ordinary press: dragged across the plot, it zooms the page", () => {
824
+ const onTimeRangeChange: MockFunction = getJestMockFunction();
825
+ showPage(onTimeRangeChange, getJestMockFunction());
826
+
827
+ restOn("10:02");
828
+ firstClickOf(chart.target, "10:02");
829
+ press(chart.target, "10:02", 2);
830
+ dragTo("10:05");
831
+ release(chart.target, "10:05", 2);
832
+
833
+ expect(pageRanges(onTimeRangeChange)).toEqual([
834
+ pageRange("10:02", "10:06"),
835
+ ]);
836
+ });
837
+ });
838
+
839
+ describe("when new rows land during the double-click's second press", () => {
840
+ test("the double-click still resets, once, on the next task: nothing is selected and neither click acts", () => {
841
+ const spies: Spies = newSpies();
842
+ const handlers: ChartHandlers = handlersFor(ZOOMED_WITH_DRAG, spies);
843
+ show(chart.render(THIRTY_BUCKETS, handlers));
844
+
845
+ restOn("10:06");
846
+ firstClickOf(chart.target, "10:06");
847
+ const pressedAt: { clientX: number; clientY: number } = spotOf("10:06");
848
+ press(chart.target, "10:06", 2);
849
+ // The refetch lands: ten buckets now, drawn across the same pixels.
850
+ renderResult().rerender(chart.render(TEN_BUCKETS, handlers));
851
+ /*
852
+ * The button comes up where it went down, over whatever the new
853
+ * rows drew there. The node the press landed on is gone, so Chrome
854
+ * sends no click and no dblclick.
855
+ */
856
+ fireEvent.mouseUp(chartWrapper(), {
857
+ ...pressedAt,
858
+ button: 0,
859
+ buttons: 0,
860
+ detail: 2,
861
+ });
862
+
863
+ expect(spies.onTimeRangeReset).not.toHaveBeenCalled();
864
+ nextTask();
865
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
866
+
867
+ waitWellPastTheDoubleClickWindow();
868
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
869
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
870
+ expect(drawnBand()).toBeNull();
871
+ expectClicksActed(chart, spies.onBucketClick, "10:06", 0);
872
+ });
873
+
874
+ /*
875
+ * A hand is never perfectly still. Once the new rows are in, a
876
+ * one-pixel twitch has recharts work out the bucket under the pointer
877
+ * from them: 10:02, not the 10:06 pressed. Read as a drag, the press
878
+ * would zoom to a window running from a bucket of the old rows to one
879
+ * of the new - the bogus zoom of #4116.
880
+ */
881
+ test("a twitch of the pointer after they land makes no drag from an old bucket to a new one, and the double-click still resets once", () => {
882
+ const spies: Spies = newSpies();
883
+ const handlers: ChartHandlers = handlersFor(ZOOMED_WITH_DRAG, spies);
884
+ show(chart.render(THIRTY_BUCKETS, handlers));
885
+
886
+ restOn("10:06");
887
+ firstClickOf(chart.target, "10:06");
888
+ const twitchedTo: { clientX: number; clientY: number } = {
889
+ clientX: tickX("10:06") + 1,
890
+ clientY: PLOT_Y,
891
+ };
892
+ press(chart.target, "10:06", 2);
893
+ renderResult().rerender(chart.render(TEN_BUCKETS, handlers));
894
+ fireEvent.mouseMove(chartWrapper(), { ...twitchedTo, buttons: 1 });
895
+ expect(drawnBand()).toBeNull();
896
+ fireEvent.mouseUp(chartWrapper(), {
897
+ ...twitchedTo,
898
+ button: 0,
899
+ buttons: 0,
900
+ detail: 2,
901
+ });
902
+
903
+ nextTask();
904
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
905
+
906
+ waitWellPastTheDoubleClickWindow();
907
+ expect(spies.onTimeRangeReset).toHaveBeenCalledTimes(1);
908
+ expect(spies.onTimeRangeSelect).not.toHaveBeenCalled();
909
+ expect(drawnBand()).toBeNull();
910
+ expectClicksActed(chart, spies.onBucketClick, "10:06", 0);
911
+ });
912
+
913
+ /*
914
+ * The report itself: a drag zooms the page, and the reader
915
+ * double-clicks straight away to undo it, while the page is still
916
+ * fetching the zoomed window. Its rows land during the second press.
917
+ */
918
+ test("on a page zoomed a moment before, a double-click whose second press sees the zoomed rows land puts the page back where it was", () => {
919
+ const onTimeRangeChange: MockFunction = getJestMockFunction();
920
+ // What the page fetches for the zoom: 10:00 to 10:08.
921
+ const zoomedRows: ChartRows = rowsOf(9);
922
+ const page: (rows: ChartRows) => React.ReactElement = (
923
+ rows: ChartRows,
924
+ ): React.ReactElement => {
925
+ return (
926
+ <ZoomablePage
927
+ chart={chart}
928
+ range={rangeOf(THIRTY_BUCKETS)}
929
+ rows={rows}
930
+ onTimeRangeChange={onTimeRangeChange}
931
+ />
932
+ );
933
+ };
934
+ show(page(THIRTY_BUCKETS));
935
+
936
+ /*
937
+ * A drag across 10:00 to 10:08 zooms the page to those nine
938
+ * minutes. (Thirty buckets leave room to label every other one.)
939
+ */
940
+ restOn("10:00");
941
+ press(chart.target, "10:00", 1);
942
+ dragTo("10:04");
943
+ dragTo("10:08");
944
+ release(chart.target, "10:08", 1);
945
+ // The browser's click after a drag goes to what both ends share.
946
+ fireEvent.click(chartWrapper(), {
947
+ ...spotOf("10:08"),
948
+ button: 0,
949
+ detail: 1,
950
+ });
951
+ expect(pageRanges(onTimeRangeChange)).toEqual([
952
+ pageRange("10:00", "10:09"),
953
+ ]);
954
+
955
+ // The zoomed rows are still on their way when the reader double-clicks.
956
+ act(() => {
957
+ jest.advanceTimersByTime(400);
958
+ });
959
+ restOn("10:06");
960
+ firstClickOf(chart.target, "10:06");
961
+ const pressedAt: { clientX: number; clientY: number } = spotOf("10:06");
962
+ press(chart.target, "10:06", 2);
963
+ renderResult().rerender(page(zoomedRows));
964
+ fireEvent.mouseUp(chartWrapper(), {
965
+ ...pressedAt,
966
+ button: 0,
967
+ buttons: 0,
968
+ detail: 2,
969
+ });
970
+ nextTask();
971
+
972
+ // Back on 10:00 to 10:30, and zoomed nowhere else on the way.
973
+ expect(pageRanges(onTimeRangeChange)).toEqual([
974
+ pageRange("10:00", "10:09"),
975
+ pageRange("10:00", "10:30"),
976
+ ]);
977
+ waitWellPastTheDoubleClickWindow();
978
+ expect(onTimeRangeChange).toHaveBeenCalledTimes(2);
979
+ expect(drawnBand()).toBeNull();
980
+ });
981
+ });
982
+ });
983
+ }
984
+
985
+ /*
986
+ * A bar chart handed only a reset cannot drag, and before this fix nothing
987
+ * on it saw a press at all: only the dblclick reset it. A double-click on
988
+ * its bare plot - between or above the bars - must end the zoom as surely
989
+ * as one on a bar.
990
+ */
991
+ describe("the bar chart with only a reset on offer, double-clicked on its bare plot", () => {
992
+ test("a double-click whose dblclick never arrives still resets it, once, on the next task", () => {
993
+ const onTimeRangeReset: MockFunction = getJestMockFunction();
994
+ show(BAR_CHART.render(TEN_BUCKETS, { onTimeRangeReset: onTimeRangeReset }));
995
+ expect(chartWrapper()).toHaveStyle({ cursor: "default" });
996
+
997
+ restOn("10:04");
998
+ firstClickOf(THE_PLOT, "10:04");
999
+ secondPressAndReleaseOf(THE_PLOT, "10:04");
1000
+ expect(onTimeRangeReset).not.toHaveBeenCalled();
1001
+
1002
+ nextTask();
1003
+ expect(onTimeRangeReset).toHaveBeenCalledTimes(1);
1004
+
1005
+ waitWellPastTheDoubleClickWindow();
1006
+ expect(onTimeRangeReset).toHaveBeenCalledTimes(1);
1007
+ expect(bars().filter(isDimmed)).toHaveLength(0);
1008
+ });
1009
+
1010
+ test("a double-click that keeps its dblclick resets it exactly once", () => {
1011
+ const onTimeRangeReset: MockFunction = getJestMockFunction();
1012
+ show(BAR_CHART.render(TEN_BUCKETS, { onTimeRangeReset: onTimeRangeReset }));
1013
+
1014
+ restOn("10:04");
1015
+ firstClickOf(THE_PLOT, "10:04");
1016
+ secondPressAndReleaseOf(THE_PLOT, "10:04");
1017
+ click(THE_PLOT, "10:04", 2);
1018
+ doubleClick(THE_PLOT, "10:04");
1019
+ expect(onTimeRangeReset).toHaveBeenCalledTimes(1);
1020
+
1021
+ nextTask();
1022
+ waitWellPastTheDoubleClickWindow();
1023
+ expect(onTimeRangeReset).toHaveBeenCalledTimes(1);
1024
+ });
1025
+ });