@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,1194 @@
1
+ /** @timezone UTC */
2
+
3
+ import "@testing-library/jest-dom";
4
+ import {
5
+ afterEach,
6
+ beforeEach,
7
+ describe,
8
+ expect,
9
+ jest,
10
+ test,
11
+ } from "@jest/globals";
12
+ import {
13
+ act,
14
+ cleanup,
15
+ fireEvent,
16
+ render,
17
+ RenderResult,
18
+ screen,
19
+ waitFor,
20
+ within,
21
+ } from "@testing-library/react";
22
+ import * as React from "react";
23
+ import getJestMockFunction, { MockFunction } from "../../MockType";
24
+
25
+ /*
26
+ * Issue #4105 on the telemetry snapshot of an incident, an alert or an
27
+ * episode whose monitor watched logs, traces or exceptions. The snapshot's
28
+ * primary signal is then an explorer, and a drag on its volume histogram
29
+ * must zoom the WHOLE snapshot, as a drag on a metric snapshot's chart
30
+ * does: every companion tab (Metrics, and whichever of Logs, Traces and
31
+ * Exceptions the primary is not) queries exactly the dragged window, the
32
+ * badge offers the one "Reset zoom", and a double-click on the histogram,
33
+ * or that Reset zoom, returns everything to the snapshot window. It used to
34
+ * zoom the explorer alone, with a Reset zoom of its own beside its picker,
35
+ * while the other tabs kept showing the whole snapshot.
36
+ *
37
+ * TelemetrySnapshotPanel (the episode pages' snapshot) is rendered with the
38
+ * real companion tabs and the real explorers: the Dashboard ones and the
39
+ * shells they render (the shared LogsViewer and the TelemetryViewer). The
40
+ * APIs are mocked, so every request each tab sends can be read back.
41
+ * Recharts is stood in for so the volume histograms can be dragged, and the
42
+ * companion metric card's charts (MetricCharts) by buttons that call the
43
+ * handlers MetricView hands them.
44
+ */
45
+
46
+ const getListMock: MockFunction = getJestMockFunction();
47
+ const analyticsGetListMock: MockFunction = getJestMockFunction();
48
+ const postMock: MockFunction = getJestMockFunction();
49
+ const fetchResultsMock: MockFunction = getJestMockFunction();
50
+
51
+ interface MockChartsProps {
52
+ onTimeRangeSelect?: ((startTime: Date, endTime: Date) => void) | undefined;
53
+ onTimeRangeReset?: (() => void) | undefined;
54
+ }
55
+
56
+ // A drag on the companion metric card, inside the snapshot window.
57
+ const MOCK_CARD_DRAG: { start: Date; end: Date } = {
58
+ start: new Date("2026-09-14T17:46:00.000Z"),
59
+ end: new Date("2026-09-14T17:48:00.000Z"),
60
+ };
61
+
62
+ jest.mock(
63
+ "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/MetricCharts",
64
+ () => {
65
+ return {
66
+ __esModule: true,
67
+ default: (props: MockChartsProps): React.ReactElement => {
68
+ return (
69
+ <div data-testid="metric-charts">
70
+ <button
71
+ type="button"
72
+ onClick={() => {
73
+ props.onTimeRangeSelect?.(
74
+ MOCK_CARD_DRAG.start,
75
+ MOCK_CARD_DRAG.end,
76
+ );
77
+ }}
78
+ >
79
+ Drag across the metric chart
80
+ </button>
81
+ <button
82
+ type="button"
83
+ onClick={() => {
84
+ props.onTimeRangeReset?.();
85
+ }}
86
+ >
87
+ Double-click the metric chart
88
+ </button>
89
+ </div>
90
+ );
91
+ },
92
+ };
93
+ },
94
+ );
95
+
96
+ jest.mock(
97
+ "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/Utils/Metrics",
98
+ () => {
99
+ return {
100
+ __esModule: true,
101
+ default: {
102
+ fetchResults: (...args: Array<unknown>) => {
103
+ return fetchResultsMock(...args);
104
+ },
105
+ loadAllMetricsTypes: () => {
106
+ return Promise.resolve({ metricTypes: [], telemetryServices: [] });
107
+ },
108
+ getMetricTypes: () => {
109
+ return Promise.resolve([]);
110
+ },
111
+ clearQueryTopNOverridesForScope: () => {
112
+ return undefined;
113
+ },
114
+ },
115
+ };
116
+ },
117
+ );
118
+
119
+ jest.mock(
120
+ "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/Utils/UseEventTimeReferenceLines",
121
+ () => {
122
+ return {
123
+ __esModule: true,
124
+ default: () => {
125
+ return { lines: [], markerCount: 0 };
126
+ },
127
+ };
128
+ },
129
+ );
130
+
131
+ jest.mock(
132
+ "../../../../App/FeatureSet/Dashboard/src/Components/Telemetry/useServiceNames",
133
+ () => {
134
+ return {
135
+ __esModule: true,
136
+ default: () => {
137
+ return {};
138
+ },
139
+ };
140
+ },
141
+ );
142
+
143
+ jest.mock(
144
+ "../../../../App/FeatureSet/Dashboard/src/Components/Telemetry/TelemetrySavedViewsControl",
145
+ () => {
146
+ return {
147
+ __esModule: true,
148
+ default: () => {
149
+ return null;
150
+ },
151
+ };
152
+ },
153
+ );
154
+
155
+ // The companion metric card's picker: shows the card's range.
156
+ jest.mock("../../../UI/Components/Date/RangeStartAndEndDateView", () => {
157
+ return {
158
+ __esModule: true,
159
+ default: (props: {
160
+ dashboardStartAndEndDate: {
161
+ range: string;
162
+ startAndEndDate?: { startValue: Date; endValue: Date };
163
+ };
164
+ }): React.ReactElement => {
165
+ const window: { startValue: Date; endValue: Date } | undefined =
166
+ props.dashboardStartAndEndDate.startAndEndDate;
167
+ return (
168
+ <span data-testid="card-picker">
169
+ {window
170
+ ? `${window.startValue.toISOString()}..${window.endValue.toISOString()}`
171
+ : props.dashboardStartAndEndDate.range}
172
+ </span>
173
+ );
174
+ },
175
+ };
176
+ });
177
+
178
+ jest.mock("../../../UI/Utils/Telemetry/UseTelemetryEntityNames", () => {
179
+ return {
180
+ __esModule: true,
181
+ default: () => {
182
+ return new Map();
183
+ },
184
+ };
185
+ });
186
+
187
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
188
+ return {
189
+ __esModule: true,
190
+ default: {
191
+ getList: (...args: Array<unknown>) => {
192
+ return getListMock(...args);
193
+ },
194
+ count: () => {
195
+ return Promise.resolve(0);
196
+ },
197
+ getCommonHeaders: () => {
198
+ return {};
199
+ },
200
+ },
201
+ };
202
+ });
203
+
204
+ jest.mock("../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI", () => {
205
+ return {
206
+ __esModule: true,
207
+ default: {
208
+ getList: (...args: Array<unknown>) => {
209
+ return analyticsGetListMock(...args);
210
+ },
211
+ },
212
+ };
213
+ });
214
+
215
+ jest.mock("../../../UI/Utils/API/API", () => {
216
+ return {
217
+ __esModule: true,
218
+ default: {
219
+ post: (...args: Array<unknown>) => {
220
+ return postMock(...args);
221
+ },
222
+ getFriendlyMessage: () => {
223
+ return "error";
224
+ },
225
+ getFriendlyErrorMessage: () => {
226
+ return "error";
227
+ },
228
+ },
229
+ };
230
+ });
231
+
232
+ jest.mock("recharts", () => {
233
+ const react: typeof React = jest.requireActual("react") as typeof React;
234
+
235
+ interface StubRow {
236
+ time: string;
237
+ }
238
+
239
+ interface StubChartProps {
240
+ data: Array<StubRow>;
241
+ children?: React.ReactNode;
242
+ onMouseDown?: (state: { activeLabel: string }) => void;
243
+ onMouseMove?: (state: { activeLabel: string }) => void;
244
+ onMouseUp?: (state: { activeLabel: string }) => void;
245
+ }
246
+
247
+ const chart: (props: StubChartProps) => React.ReactElement = (
248
+ props: StubChartProps,
249
+ ): React.ReactElement => {
250
+ return react.createElement(
251
+ "div",
252
+ null,
253
+ props.data.map((row: StubRow) => {
254
+ return react.createElement("div", {
255
+ key: row.time,
256
+ "data-testid": `bucket-${row.time}`,
257
+ onMouseDown: () => {
258
+ props.onMouseDown?.({ activeLabel: row.time });
259
+ },
260
+ onMouseMove: () => {
261
+ props.onMouseMove?.({ activeLabel: row.time });
262
+ },
263
+ onMouseUp: () => {
264
+ props.onMouseUp?.({ activeLabel: row.time });
265
+ },
266
+ });
267
+ }),
268
+ props.children,
269
+ );
270
+ };
271
+
272
+ const nothing: () => null = (): null => {
273
+ return null;
274
+ };
275
+
276
+ return {
277
+ __esModule: true,
278
+ ResponsiveContainer: (props: { children: React.ReactNode }) => {
279
+ return react.createElement("div", null, props.children);
280
+ },
281
+ AreaChart: chart,
282
+ BarChart: chart,
283
+ LineChart: chart,
284
+ Area: nothing,
285
+ Bar: nothing,
286
+ Line: nothing,
287
+ XAxis: nothing,
288
+ YAxis: nothing,
289
+ CartesianGrid: nothing,
290
+ Tooltip: nothing,
291
+ ReferenceArea: nothing,
292
+ };
293
+ });
294
+
295
+ import TelemetrySnapshotPanel from "../../../../App/FeatureSet/Dashboard/src/Components/Telemetry/TelemetrySnapshotPanel";
296
+ import { RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID } from "../../../UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton";
297
+ import { getTimeRangeButtonLabel } from "../../../UI/Components/Date/TimeRangePickerDropdown";
298
+ import ExceptionInstance from "../../../Models/AnalyticsModels/ExceptionInstance";
299
+ import Log from "../../../Models/AnalyticsModels/Log";
300
+ import Metric from "../../../Models/AnalyticsModels/Metric";
301
+ import Span from "../../../Models/AnalyticsModels/Span";
302
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
303
+ import OneUptimeDate from "../../../Types/Date";
304
+ import MetricViewData from "../../../Types/Metrics/MetricViewData";
305
+ import ObjectID from "../../../Types/ObjectID";
306
+ import { TelemetryQuery } from "../../../Types/Telemetry/TelemetryQuery";
307
+ import TelemetryType from "../../../Types/Telemetry/TelemetryType";
308
+ import TimeRange from "../../../Types/Time/TimeRange";
309
+ import ProjectUtil from "../../../UI/Utils/Project";
310
+
311
+ const NOW: Date = new Date("2026-09-14T19:00:00.000Z");
312
+ // The window the monitor evaluated over when it opened the event.
313
+ const SNAPSHOT_START: Date = new Date("2026-09-14T17:45:00.000Z");
314
+ const SNAPSHOT_END: Date = new Date("2026-09-14T18:00:00.000Z");
315
+ // A later evaluation's window.
316
+ const NEXT_START: Date = new Date("2026-09-14T18:30:00.000Z");
317
+ const NEXT_END: Date = new Date("2026-09-14T18:45:00.000Z");
318
+
319
+ const SNAPSHOT_TEXT: string = `${SNAPSHOT_START.toISOString()}..${SNAPSHOT_END.toISOString()}`;
320
+ const NEXT_TEXT: string = `${NEXT_START.toISOString()}..${NEXT_END.toISOString()}`;
321
+
322
+ /*
323
+ * The volume histograms' bars, a minute each (every explorer buckets a
324
+ * fifteen-minute window by the minute). A drag from the first to the last
325
+ * covers 17:50 up to the end of the 17:54 bar.
326
+ */
327
+ const BAR_1750: string = "2026-09-14 17:50:00";
328
+ const BAR_1751: string = "2026-09-14 17:51:00";
329
+ const BAR_1752: string = "2026-09-14 17:52:00";
330
+ const BAR_1754: string = "2026-09-14 17:54:00";
331
+ const BARS: Array<string> = [
332
+ BAR_1750,
333
+ BAR_1751,
334
+ BAR_1752,
335
+ "2026-09-14 17:53:00",
336
+ BAR_1754,
337
+ ];
338
+
339
+ const SLICE_TEXT: string = "2026-09-14T17:50:00.000Z..2026-09-14T17:55:00.000Z";
340
+ // A second drag, from the 17:51 bar through the 17:52 one.
341
+ const NESTED_TEXT: string =
342
+ "2026-09-14T17:51:00.000Z..2026-09-14T17:53:00.000Z";
343
+ const CARD_DRAG_TEXT: string = `${MOCK_CARD_DRAG.start.toISOString()}..${MOCK_CARD_DRAG.end.toISOString()}`;
344
+
345
+ const LOGS_HISTOGRAM: string = "/telemetry/logs/histogram";
346
+ const TRACES_HISTOGRAM: string = "/telemetry/traces/histogram";
347
+ const EXCEPTIONS_HISTOGRAM: string = "/telemetry/exceptions/histogram";
348
+
349
+ function textOf(window: { startValue: Date; endValue: Date }): string {
350
+ return `${OneUptimeDate.fromString(window.startValue).toISOString()}..${OneUptimeDate.fromString(window.endValue).toISOString()}`;
351
+ }
352
+
353
+ function labelOf(text: string): string {
354
+ const [startIso, endIso] = text.split("..") as [string, string];
355
+ return getTimeRangeButtonLabel({
356
+ range: TimeRange.CUSTOM,
357
+ startAndEndDate: new InBetween<Date>(new Date(startIso), new Date(endIso)),
358
+ });
359
+ }
360
+
361
+ // A relative window resolves against the (moving) fake clock: check its length.
362
+ function isPastDay(text: string | undefined): boolean {
363
+ if (!text) {
364
+ return false;
365
+ }
366
+ const [startIso, endIso] = text.split("..") as [string, string];
367
+ return (
368
+ new Date(endIso).getTime() - new Date(startIso).getTime() ===
369
+ 24 * 60 * 60 * 1000
370
+ );
371
+ }
372
+
373
+ /*
374
+ * Where a tab's requests are read back from, one kind of request each (a
375
+ * list, a volume chart, a metric lookup): every window it was asked for,
376
+ * in order.
377
+ */
378
+ type WindowSource = () => Array<string>;
379
+
380
+ function postedWindows(path: string): Array<string> {
381
+ return postMock.mock.calls
382
+ .map((call: Array<unknown>) => {
383
+ return call[0] as {
384
+ url: { toString: () => string };
385
+ data: Record<string, unknown>;
386
+ };
387
+ })
388
+ .filter((args: { url: { toString: () => string } }): boolean => {
389
+ return args.url.toString().includes(path);
390
+ })
391
+ .map((args: { data: Record<string, unknown> }): string => {
392
+ return `${args.data["startTime"]}..${args.data["endTime"]}`;
393
+ });
394
+ }
395
+
396
+ function listedWindows(modelType: unknown, field: string): Array<string> {
397
+ return analyticsGetListMock.mock.calls
398
+ .map((call: Array<unknown>) => {
399
+ return call[0] as { modelType: unknown; query: Record<string, unknown> };
400
+ })
401
+ .filter((request: { modelType: unknown }): boolean => {
402
+ return request.modelType === modelType;
403
+ })
404
+ .map((request: { query: Record<string, unknown> }): string => {
405
+ return textOf(request.query[field] as InBetween<Date>);
406
+ });
407
+ }
408
+
409
+ // The companion metric card's charts.
410
+ function metricChartWindows(): Array<string> {
411
+ return fetchResultsMock.mock.calls.map((call: Array<unknown>): string => {
412
+ const data: MetricViewData = (call[0] as { metricViewData: MetricViewData })
413
+ .metricViewData;
414
+ return textOf(data.startAndEndDate!);
415
+ });
416
+ }
417
+
418
+ const LOG_LIST: WindowSource = (): Array<string> => {
419
+ return listedWindows(Log, "time");
420
+ };
421
+ const LOG_VOLUME: WindowSource = (): Array<string> => {
422
+ return postedWindows(LOGS_HISTOGRAM);
423
+ };
424
+ const SPAN_LIST: WindowSource = (): Array<string> => {
425
+ return listedWindows(Span, "startTime");
426
+ };
427
+ const SPAN_VOLUME: WindowSource = (): Array<string> => {
428
+ return postedWindows(TRACES_HISTOGRAM);
429
+ };
430
+ // The exception groups to list are the ones that occurred in the window.
431
+ const EXCEPTION_SCOPE: WindowSource = (): Array<string> => {
432
+ return listedWindows(ExceptionInstance, "time");
433
+ };
434
+ const EXCEPTION_VOLUME: WindowSource = (): Array<string> => {
435
+ return postedWindows(EXCEPTIONS_HISTOGRAM);
436
+ };
437
+ // Which metrics the companion card charts: the ones seen in the window.
438
+ const METRIC_LOOKUP: WindowSource = (): Array<string> => {
439
+ return listedWindows(Metric, "time");
440
+ };
441
+ const METRIC_CHARTS: WindowSource = (): Array<string> => {
442
+ return metricChartWindows();
443
+ };
444
+
445
+ function last(windows: Array<string>): string | undefined {
446
+ return windows[windows.length - 1];
447
+ }
448
+
449
+ function countsOf(sources: Array<WindowSource>): Array<number> {
450
+ return sources.map((source: WindowSource): number => {
451
+ return source().length;
452
+ });
453
+ }
454
+
455
+ // Every window asked for since the counts were taken, from every source.
456
+ function askedSince(
457
+ sources: Array<WindowSource>,
458
+ counts: Array<number>,
459
+ ): Array<string> {
460
+ const asked: Array<string> = [];
461
+ sources.forEach((source: WindowSource, index: number) => {
462
+ asked.push(...source().slice(counts[index]));
463
+ });
464
+ return asked;
465
+ }
466
+
467
+ // The window each source was last asked for.
468
+ function latestOf(sources: Array<WindowSource>): Array<string | undefined> {
469
+ return sources.map((source: WindowSource): string | undefined => {
470
+ return last(source());
471
+ });
472
+ }
473
+
474
+ async function expectLatest(
475
+ sources: Array<WindowSource>,
476
+ windowText: string,
477
+ ): Promise<void> {
478
+ await waitFor(() => {
479
+ expect(latestOf(sources)).toEqual(
480
+ sources.map((): string => {
481
+ return windowText;
482
+ }),
483
+ );
484
+ });
485
+ }
486
+
487
+ function resetButtons(): Array<HTMLElement> {
488
+ return screen.queryAllByTestId(RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID);
489
+ }
490
+
491
+ function badgeTitle(
492
+ start: Date = SNAPSHOT_START,
493
+ end: Date = SNAPSHOT_END,
494
+ ): string {
495
+ return OneUptimeDate.getInBetweenDatesAsFormattedString(
496
+ new InBetween<Date>(start, end),
497
+ );
498
+ }
499
+
500
+ /*
501
+ * The snapshot badge's Reset zoom: the one right before the badge's window.
502
+ * (The companion metric card's own Reset zoom shares a row with the badge,
503
+ * after the card's picker.)
504
+ */
505
+ function badgeResetButtons(): Array<HTMLElement> {
506
+ return resetButtons().filter((button: HTMLElement): boolean => {
507
+ const next: Element | null = button.nextElementSibling;
508
+ return (
509
+ next !== null &&
510
+ within(next as HTMLElement).queryByText(badgeTitle()) !== null
511
+ );
512
+ });
513
+ }
514
+
515
+ interface CompanionCase {
516
+ tab: string;
517
+ sources: Array<WindowSource>;
518
+ // What the tab's card says it shows.
519
+ describes: (windowName: string) => string;
520
+ }
521
+
522
+ const METRICS_COMPANION: CompanionCase = {
523
+ tab: "Metrics",
524
+ sources: [METRIC_LOOKUP, METRIC_CHARTS],
525
+ describes: (windowName: string): string => {
526
+ return `Metrics in this incident's telemetry scope during ${windowName}.`;
527
+ },
528
+ };
529
+
530
+ const LOGS_COMPANION: CompanionCase = {
531
+ tab: "Logs",
532
+ sources: [LOG_LIST, LOG_VOLUME],
533
+ describes: (windowName: string): string => {
534
+ return `Logs in this incident's telemetry scope during ${windowName}.`;
535
+ },
536
+ };
537
+
538
+ const TRACES_COMPANION: CompanionCase = {
539
+ tab: "Traces",
540
+ sources: [SPAN_LIST, SPAN_VOLUME],
541
+ describes: (windowName: string): string => {
542
+ return `Spans in this incident's telemetry scope during ${windowName}.`;
543
+ },
544
+ };
545
+
546
+ const EXCEPTIONS_COMPANION: CompanionCase = {
547
+ tab: "Exceptions",
548
+ sources: [EXCEPTION_SCOPE, EXCEPTION_VOLUME],
549
+ describes: (windowName: string): string => {
550
+ return `Exceptions in this incident's telemetry scope during ${windowName}.`;
551
+ },
552
+ };
553
+
554
+ interface PrimaryCase {
555
+ name: string;
556
+ tab: string;
557
+ telemetryType: TelemetryType;
558
+ // Where the stored query keeps the snapshot window.
559
+ windowField: string;
560
+ pickerPrefix: string;
561
+ // Where the primary explorer's requests are read back from.
562
+ sources: Array<WindowSource>;
563
+ // Its volume chart's requests (last among its sources to be asked).
564
+ volume: WindowSource;
565
+ companions: Array<CompanionCase>;
566
+ // A companion explorer, to check the companions still zoom on their own.
567
+ companionExplorer: CompanionCase;
568
+ }
569
+
570
+ const PRIMARIES: Array<PrimaryCase> = [
571
+ {
572
+ name: "Logs",
573
+ tab: "Logs",
574
+ telemetryType: TelemetryType.Log,
575
+ windowField: "time",
576
+ pickerPrefix: "log-time-range-picker",
577
+ sources: [LOG_LIST, LOG_VOLUME],
578
+ volume: LOG_VOLUME,
579
+ companions: [TRACES_COMPANION, METRICS_COMPANION, EXCEPTIONS_COMPANION],
580
+ companionExplorer: TRACES_COMPANION,
581
+ },
582
+ {
583
+ name: "Traces",
584
+ tab: "Traces",
585
+ telemetryType: TelemetryType.Trace,
586
+ windowField: "startTime",
587
+ pickerPrefix: "telemetry-time-range-picker",
588
+ sources: [SPAN_LIST, SPAN_VOLUME],
589
+ volume: SPAN_VOLUME,
590
+ companions: [LOGS_COMPANION, METRICS_COMPANION, EXCEPTIONS_COMPANION],
591
+ companionExplorer: LOGS_COMPANION,
592
+ },
593
+ {
594
+ name: "Exceptions",
595
+ tab: "Exceptions",
596
+ telemetryType: TelemetryType.Exception,
597
+ windowField: "time",
598
+ pickerPrefix: "telemetry-time-range-picker",
599
+ sources: [EXCEPTION_SCOPE, EXCEPTION_VOLUME],
600
+ volume: EXCEPTION_VOLUME,
601
+ companions: [LOGS_COMPANION, TRACES_COMPANION, METRICS_COMPANION],
602
+ companionExplorer: TRACES_COMPANION,
603
+ },
604
+ ];
605
+
606
+ // A new window every call, the way the page re-reads the stored snapshot.
607
+ function snapshotWindow(
608
+ start: Date = SNAPSHOT_START,
609
+ end: Date = SNAPSHOT_END,
610
+ ): InBetween<Date> {
611
+ return new InBetween<Date>(
612
+ new Date(start.getTime()),
613
+ new Date(end.getTime()),
614
+ );
615
+ }
616
+
617
+ // The query a logs, traces or exceptions monitor stores on the event.
618
+ function storedQuery(
619
+ primary: PrimaryCase,
620
+ start: Date = SNAPSHOT_START,
621
+ end: Date = SNAPSHOT_END,
622
+ ): TelemetryQuery {
623
+ return {
624
+ telemetryType: primary.telemetryType,
625
+ telemetryQuery: {
626
+ [primary.windowField]: snapshotWindow(start, end),
627
+ } as never,
628
+ metricViewData: null,
629
+ };
630
+ }
631
+
632
+ function panel(
633
+ primary: PrimaryCase,
634
+ start: Date = SNAPSHOT_START,
635
+ end: Date = SNAPSHOT_END,
636
+ ): React.ReactElement {
637
+ return (
638
+ <TelemetrySnapshotPanel
639
+ telemetryQuery={storedQuery(primary, start, end)}
640
+ snapshotWindow={snapshotWindow(start, end)}
641
+ seriesSummary=""
642
+ eventNoun="incident"
643
+ />
644
+ );
645
+ }
646
+
647
+ // An explorer has drawn its volume chart's bars.
648
+ async function volumeChartDrawn(): Promise<void> {
649
+ await waitFor(() => {
650
+ expect(screen.getByTestId(`bucket-${BAR_1750}`)).toBeInTheDocument();
651
+ });
652
+ }
653
+
654
+ async function renderPanel(primary: PrimaryCase): Promise<RenderResult> {
655
+ let rendered: RenderResult | null = null;
656
+ await act(async () => {
657
+ rendered = render(panel(primary));
658
+ });
659
+ await volumeChartDrawn();
660
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
661
+ return rendered!;
662
+ }
663
+
664
+ function dragBars(from: string, to: string): void {
665
+ fireEvent.mouseDown(screen.getByTestId(`bucket-${from}`));
666
+ fireEvent.mouseMove(screen.getByTestId(`bucket-${to}`));
667
+ fireEvent.mouseUp(screen.getByTestId(`bucket-${to}`));
668
+ }
669
+
670
+ // The element a volume histogram takes its double-click on.
671
+ function histogramPlot(): HTMLElement {
672
+ return screen.getByTestId(`bucket-${BAR_1750}`).parentElement!.parentElement!
673
+ .parentElement!;
674
+ }
675
+
676
+ function pickerLabel(primary: PrimaryCase): string {
677
+ return (
678
+ screen.getByTestId(`${primary.pickerPrefix}-button`).textContent || ""
679
+ ).trim();
680
+ }
681
+
682
+ async function openTab(name: string): Promise<void> {
683
+ await act(async () => {
684
+ fireEvent.click(screen.getByRole("tab", { name: name }));
685
+ });
686
+ }
687
+
688
+ // Back on the primary's tab, once its explorer has drawn its bars.
689
+ async function backOnThePrimary(primary: PrimaryCase): Promise<void> {
690
+ await openTab(primary.tab);
691
+ await volumeChartDrawn();
692
+ }
693
+
694
+ /*
695
+ * A drag across the primary's volume chart, and what makes it a zoom of the
696
+ * WHOLE snapshot: the snapshot badge offers the way back, and the primary
697
+ * is on the dragged window.
698
+ */
699
+ async function zoomFromThePrimary(primary: PrimaryCase): Promise<void> {
700
+ dragBars(BAR_1750, BAR_1754);
701
+
702
+ await waitFor(() => {
703
+ expect(badgeResetButtons()).toHaveLength(1);
704
+ });
705
+ await expectLatest(primary.sources, SLICE_TEXT);
706
+ }
707
+
708
+ // Opens a companion tab: every window it asks for until it has asked for each.
709
+ async function windowsAskedForBy(
710
+ companion: CompanionCase,
711
+ ): Promise<Array<string>> {
712
+ const counts: Array<number> = countsOf(companion.sources);
713
+
714
+ await openTab(companion.tab);
715
+
716
+ await waitFor(() => {
717
+ const now: Array<number> = countsOf(companion.sources);
718
+ now.forEach((count: number, index: number) => {
719
+ expect(count).toBeGreaterThan(counts[index]!);
720
+ });
721
+ });
722
+
723
+ return askedSince(companion.sources, counts);
724
+ }
725
+
726
+ async function expectCompanionsOn(
727
+ primary: PrimaryCase,
728
+ windowText: string,
729
+ ): Promise<void> {
730
+ for (const companion of primary.companions) {
731
+ const asked: Array<string> = await windowsAskedForBy(companion);
732
+ expect(asked.length).toBeGreaterThan(0);
733
+ for (const window of asked) {
734
+ expect(`${companion.tab}: ${window}`).toBe(
735
+ `${companion.tab}: ${windowText}`,
736
+ );
737
+ }
738
+ }
739
+ }
740
+
741
+ beforeEach(() => {
742
+ jest.useFakeTimers({ doNotFake: ["nextTick", "setImmediate"] });
743
+ jest.setSystemTime(NOW);
744
+ getListMock.mockReset();
745
+ analyticsGetListMock.mockReset();
746
+ postMock.mockReset();
747
+ fetchResultsMock.mockReset();
748
+ jest
749
+ .spyOn(ProjectUtil, "getCurrentProjectId")
750
+ .mockReturnValue(new ObjectID("11111111-1111-4111-8111-111111111111"));
751
+ getListMock.mockImplementation(async () => {
752
+ return { data: [], count: 0 };
753
+ });
754
+ analyticsGetListMock.mockImplementation(
755
+ async (request: { modelType: unknown }) => {
756
+ // The companion metric card charts the metrics it finds in scope.
757
+ if (request.modelType === Metric) {
758
+ return { data: [{ name: "checkout.latency" }], count: 1 };
759
+ }
760
+ return { data: [], count: 0 };
761
+ },
762
+ );
763
+ postMock.mockImplementation(
764
+ async (args: { url: { toString: () => string } }) => {
765
+ const url: string = args.url.toString();
766
+
767
+ if (url.includes(LOGS_HISTOGRAM)) {
768
+ return {
769
+ data: {
770
+ bucketSizeInMinutes: 1,
771
+ buckets: BARS.map((time: string) => {
772
+ return { time, severity: "Error", count: 2 };
773
+ }),
774
+ },
775
+ };
776
+ }
777
+
778
+ if (
779
+ url.includes(TRACES_HISTOGRAM) ||
780
+ url.includes(EXCEPTIONS_HISTOGRAM)
781
+ ) {
782
+ return {
783
+ data: {
784
+ buckets: BARS.map((time: string) => {
785
+ return { time, series: "ok", count: 3 };
786
+ }),
787
+ },
788
+ };
789
+ }
790
+
791
+ return { data: {} };
792
+ },
793
+ );
794
+ fetchResultsMock.mockImplementation(() => {
795
+ return Promise.resolve([{ data: [], truncated: false }]);
796
+ });
797
+ window.history.replaceState({}, "", "/");
798
+ });
799
+
800
+ afterEach(() => {
801
+ cleanup();
802
+ jest.restoreAllMocks();
803
+ jest.useRealTimers();
804
+ window.history.replaceState({}, "", "/");
805
+ });
806
+
807
+ describe.each(PRIMARIES)(
808
+ "a $name snapshot: a drag on the primary explorer's histogram zooms the whole snapshot",
809
+ (primary: PrimaryCase) => {
810
+ const companionRows: Array<[string, CompanionCase]> =
811
+ primary.companions.map(
812
+ (companion: CompanionCase): [string, CompanionCase] => {
813
+ return [companion.tab, companion];
814
+ },
815
+ );
816
+
817
+ test("before any drag, the primary and every companion tab are on the snapshot window, with nothing to reset", async () => {
818
+ await renderPanel(primary);
819
+
820
+ expect(pickerLabel(primary)).toBe(labelOf(SNAPSHOT_TEXT));
821
+ expect(resetButtons()).toHaveLength(0);
822
+ expect(askedSince(primary.sources, [0, 0])).not.toContain(SLICE_TEXT);
823
+
824
+ await expectCompanionsOn(primary, SNAPSHOT_TEXT);
825
+ });
826
+
827
+ test("the primary explorer ends up listing and charting exactly the dragged window", async () => {
828
+ await renderPanel(primary);
829
+ const counts: Array<number> = countsOf(primary.sources);
830
+
831
+ await zoomFromThePrimary(primary);
832
+
833
+ expect(pickerLabel(primary)).toBe(labelOf(SLICE_TEXT));
834
+ // Every kind of request it makes was sent again, the last for the slice.
835
+ countsOf(primary.sources).forEach((count: number, index: number) => {
836
+ expect(count).toBeGreaterThan(counts[index]!);
837
+ });
838
+ expect(latestOf(primary.sources)).toEqual(
839
+ primary.sources.map((): string => {
840
+ return SLICE_TEXT;
841
+ }),
842
+ );
843
+ });
844
+
845
+ test.each(companionRows)(
846
+ "the %s companion tab, opened after the drag, queries exactly the dragged window",
847
+ async (_tab: string, companion: CompanionCase) => {
848
+ await renderPanel(primary);
849
+ await zoomFromThePrimary(primary);
850
+
851
+ const asked: Array<string> = await windowsAskedForBy(companion);
852
+
853
+ expect(asked.length).toBeGreaterThanOrEqual(companion.sources.length);
854
+ for (const window of asked) {
855
+ expect(window).toBe(SLICE_TEXT);
856
+ }
857
+ // Its card says it shows only part of the snapshot window.
858
+ expect(
859
+ screen.getByText(
860
+ companion.describes("the zoomed part of the snapshot window"),
861
+ ),
862
+ ).toBeInTheDocument();
863
+ },
864
+ );
865
+
866
+ test("the companion metric card's picker is on the dragged window", async () => {
867
+ await renderPanel(primary);
868
+ await zoomFromThePrimary(primary);
869
+
870
+ await openTab("Metrics");
871
+
872
+ await waitFor(() => {
873
+ expect(last(metricChartWindows())).toBe(SLICE_TEXT);
874
+ });
875
+ expect(screen.getByTestId("card-picker")).toHaveTextContent(SLICE_TEXT);
876
+ });
877
+
878
+ test("the badge offers Reset zoom, and it is the only Reset zoom on the card", async () => {
879
+ await renderPanel(primary);
880
+
881
+ await zoomFromThePrimary(primary);
882
+
883
+ const tabPanel: HTMLElement = screen.getByRole("tabpanel");
884
+ const buttons: Array<HTMLElement> = within(tabPanel).queryAllByTestId(
885
+ RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID,
886
+ );
887
+ expect(buttons).toHaveLength(1);
888
+ // Beside the badge, which keeps naming the snapshot window...
889
+ expect(badgeResetButtons()).toEqual(buttons);
890
+ expect(buttons[0]).toBeVisible();
891
+ // ...and none beside the explorer's picker, which shows the slice.
892
+ const picker: HTMLElement = screen.getByTestId(
893
+ `${primary.pickerPrefix}-button`,
894
+ );
895
+ expect(
896
+ within(picker.parentElement!.parentElement!).queryByTestId(
897
+ RESET_TIME_RANGE_ZOOM_BUTTON_TEST_ID,
898
+ ),
899
+ ).toBeNull();
900
+ expect(pickerLabel(primary)).toBe(labelOf(SLICE_TEXT));
901
+ });
902
+
903
+ test("every companion tab's card carries the badge's Reset zoom, the only one there", async () => {
904
+ await renderPanel(primary);
905
+ await zoomFromThePrimary(primary);
906
+
907
+ for (const companion of primary.companions) {
908
+ await windowsAskedForBy(companion);
909
+ expect(badgeResetButtons()).toHaveLength(1);
910
+ expect(resetButtons()).toHaveLength(1);
911
+ }
912
+ });
913
+
914
+ test("the histogram offers the double-click once the snapshot is zoomed", async () => {
915
+ await renderPanel(primary);
916
+
917
+ expect(screen.queryByText("Double-click to reset")).toBeNull();
918
+
919
+ await zoomFromThePrimary(primary);
920
+
921
+ await waitFor(() => {
922
+ expect(screen.getByText("Double-click to reset")).toBeInTheDocument();
923
+ });
924
+ });
925
+
926
+ test("a double-click on the primary's histogram returns everything to the snapshot window", async () => {
927
+ await renderPanel(primary);
928
+ await zoomFromThePrimary(primary);
929
+ await waitFor(() => {
930
+ expect(screen.getByText("Double-click to reset")).toBeInTheDocument();
931
+ });
932
+
933
+ fireEvent.doubleClick(histogramPlot());
934
+
935
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
936
+ expect(resetButtons()).toHaveLength(0);
937
+ expect(pickerLabel(primary)).toBe(labelOf(SNAPSHOT_TEXT));
938
+ expect(screen.queryByText("Double-click to reset")).toBeNull();
939
+
940
+ await expectCompanionsOn(primary, SNAPSHOT_TEXT);
941
+ });
942
+
943
+ test("Reset zoom beside the badge returns everything to the snapshot window", async () => {
944
+ await renderPanel(primary);
945
+ await zoomFromThePrimary(primary);
946
+
947
+ fireEvent.click(badgeResetButtons()[0]!);
948
+
949
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
950
+ expect(resetButtons()).toHaveLength(0);
951
+ expect(pickerLabel(primary)).toBe(labelOf(SNAPSHOT_TEXT));
952
+
953
+ await expectCompanionsOn(primary, SNAPSHOT_TEXT);
954
+ });
955
+
956
+ test("Reset zoom on a companion tab returns the primary to the snapshot window too", async () => {
957
+ await renderPanel(primary);
958
+ await zoomFromThePrimary(primary);
959
+ await windowsAskedForBy(primary.companions[0]!);
960
+
961
+ await act(async () => {
962
+ fireEvent.click(badgeResetButtons()[0]!);
963
+ });
964
+ await backOnThePrimary(primary);
965
+
966
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
967
+ expect(pickerLabel(primary)).toBe(labelOf(SNAPSHOT_TEXT));
968
+ expect(resetButtons()).toHaveLength(0);
969
+ });
970
+
971
+ test("a second drag zooms within the first, and one double-click returns to the snapshot window", async () => {
972
+ await renderPanel(primary);
973
+ await zoomFromThePrimary(primary);
974
+
975
+ dragBars(BAR_1751, BAR_1752);
976
+
977
+ await expectLatest(primary.sources, NESTED_TEXT);
978
+ await expectCompanionsOn(primary, NESTED_TEXT);
979
+ await backOnThePrimary(primary);
980
+ await waitFor(() => {
981
+ expect(screen.getByText("Double-click to reset")).toBeInTheDocument();
982
+ });
983
+
984
+ fireEvent.doubleClick(histogramPlot());
985
+
986
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
987
+ expect(resetButtons()).toHaveLength(0);
988
+ });
989
+
990
+ test("the zoom outlives a switch to another tab: the primary comes back on the slice, still zoomed", async () => {
991
+ await renderPanel(primary);
992
+ await zoomFromThePrimary(primary);
993
+ await windowsAskedForBy(METRICS_COMPANION);
994
+ const counts: Array<number> = countsOf(primary.sources);
995
+
996
+ await backOnThePrimary(primary);
997
+
998
+ // It came back on the slice: the snapshot window was never asked for.
999
+ await waitFor(() => {
1000
+ expect(askedSince(primary.sources, counts).length).toBeGreaterThan(0);
1001
+ });
1002
+ for (const window of askedSince(primary.sources, counts)) {
1003
+ expect(window).toBe(SLICE_TEXT);
1004
+ }
1005
+ expect(pickerLabel(primary)).toBe(labelOf(SLICE_TEXT));
1006
+ expect(badgeResetButtons()).toHaveLength(1);
1007
+ expect(resetButtons()).toHaveLength(1);
1008
+
1009
+ // And a double-click on its histogram still resets the snapshot.
1010
+ await waitFor(() => {
1011
+ expect(screen.getByText("Double-click to reset")).toBeInTheDocument();
1012
+ });
1013
+ fireEvent.doubleClick(histogramPlot());
1014
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
1015
+ expect(resetButtons()).toHaveLength(0);
1016
+ });
1017
+
1018
+ test("a range picked in the primary's own picker takes it its own way; the badge's Reset zoom brings it back", async () => {
1019
+ await renderPanel(primary);
1020
+ await zoomFromThePrimary(primary);
1021
+
1022
+ fireEvent.click(screen.getByTestId(`${primary.pickerPrefix}-button`));
1023
+ fireEvent.click(
1024
+ within(
1025
+ screen.getByTestId(`${primary.pickerPrefix}-dropdown`),
1026
+ ).getByText("Past 1 Day"),
1027
+ );
1028
+
1029
+ await waitFor(() => {
1030
+ expect(isPastDay(last(primary.volume()))).toBe(true);
1031
+ });
1032
+ expect(pickerLabel(primary)).toBe("Past 1 Day");
1033
+ // The snapshot keeps its zoom: the badge still offers the way back.
1034
+ expect(badgeResetButtons()).toHaveLength(1);
1035
+ expect(resetButtons()).toHaveLength(1);
1036
+
1037
+ fireEvent.click(badgeResetButtons()[0]!);
1038
+
1039
+ // Pinned to the snapshot window again, the primary follows it there.
1040
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
1041
+ expect(pickerLabel(primary)).toBe(labelOf(SNAPSHOT_TEXT));
1042
+ expect(resetButtons()).toHaveLength(0);
1043
+ });
1044
+ },
1045
+ );
1046
+
1047
+ describe.each(PRIMARIES)(
1048
+ "a $name snapshot: the page's background refresh and a new snapshot window",
1049
+ (primary: PrimaryCase) => {
1050
+ // The page re-reads the stored snapshot: new objects, the same values.
1051
+ async function refresh(rendered: RenderResult): Promise<void> {
1052
+ await act(async () => {
1053
+ rendered.rerender(panel(primary));
1054
+ });
1055
+ }
1056
+
1057
+ test("a refresh with equal values keeps the zoom: the primary stays on the slice, with the badge's way back", async () => {
1058
+ const rendered: RenderResult = await renderPanel(primary);
1059
+ await zoomFromThePrimary(primary);
1060
+ const counts: Array<number> = countsOf(primary.sources);
1061
+
1062
+ await refresh(rendered);
1063
+ await refresh(rendered);
1064
+
1065
+ expect(pickerLabel(primary)).toBe(labelOf(SLICE_TEXT));
1066
+ expect(badgeResetButtons()).toHaveLength(1);
1067
+ expect(resetButtons()).toHaveLength(1);
1068
+ // Whatever it asked for since, it never went back to the whole window.
1069
+ for (const window of askedSince(primary.sources, counts)) {
1070
+ expect(window).toBe(SLICE_TEXT);
1071
+ }
1072
+ await waitFor(() => {
1073
+ expect(screen.getByText("Double-click to reset")).toBeInTheDocument();
1074
+ });
1075
+ });
1076
+
1077
+ test("after the refresh, a double-click on the histogram still resets everything", async () => {
1078
+ const rendered: RenderResult = await renderPanel(primary);
1079
+ await zoomFromThePrimary(primary);
1080
+ await refresh(rendered);
1081
+
1082
+ await waitFor(() => {
1083
+ expect(screen.getByText("Double-click to reset")).toBeInTheDocument();
1084
+ });
1085
+ fireEvent.doubleClick(histogramPlot());
1086
+
1087
+ await expectLatest(primary.sources, SNAPSHOT_TEXT);
1088
+ expect(resetButtons()).toHaveLength(0);
1089
+ await expectCompanionsOn(primary, SNAPSHOT_TEXT);
1090
+ });
1091
+
1092
+ test("after the refresh, every companion tab is still on the slice", async () => {
1093
+ const rendered: RenderResult = await renderPanel(primary);
1094
+ await zoomFromThePrimary(primary);
1095
+ await refresh(rendered);
1096
+
1097
+ await expectCompanionsOn(primary, SLICE_TEXT);
1098
+ });
1099
+
1100
+ test("a new snapshot window drops the zoom: the primary and every tab move to it, with nothing to reset", async () => {
1101
+ const rendered: RenderResult = await renderPanel(primary);
1102
+ await zoomFromThePrimary(primary);
1103
+
1104
+ await act(async () => {
1105
+ rendered.rerender(panel(primary, NEXT_START, NEXT_END));
1106
+ });
1107
+
1108
+ await expectLatest(primary.sources, NEXT_TEXT);
1109
+ expect(pickerLabel(primary)).toBe(labelOf(NEXT_TEXT));
1110
+ expect(resetButtons()).toHaveLength(0);
1111
+ expect(screen.getByText(badgeTitle(NEXT_START, NEXT_END))).toBeVisible();
1112
+ expect(screen.queryByText("Double-click to reset")).toBeNull();
1113
+
1114
+ await expectCompanionsOn(primary, NEXT_TEXT);
1115
+ });
1116
+ },
1117
+ );
1118
+
1119
+ describe.each(PRIMARIES)(
1120
+ "a $name snapshot: the companion tabs keep zooms of their own",
1121
+ (primary: PrimaryCase) => {
1122
+ test("a drag on a companion explorer zooms that explorer alone; the snapshot is not zoomed", async () => {
1123
+ await renderPanel(primary);
1124
+ const companion: CompanionCase = primary.companionExplorer;
1125
+ await windowsAskedForBy(companion);
1126
+ await volumeChartDrawn();
1127
+
1128
+ dragBars(BAR_1750, BAR_1754);
1129
+
1130
+ await expectLatest(companion.sources, SLICE_TEXT);
1131
+ // Its own way back, beside its own picker; none beside the badge.
1132
+ expect(resetButtons()).toHaveLength(1);
1133
+ expect(badgeResetButtons()).toHaveLength(0);
1134
+
1135
+ // The primary is still on the whole snapshot window.
1136
+ const counts: Array<number> = countsOf(primary.sources);
1137
+ await backOnThePrimary(primary);
1138
+ await waitFor(() => {
1139
+ expect(askedSince(primary.sources, counts).length).toBeGreaterThan(0);
1140
+ });
1141
+ for (const window of askedSince(primary.sources, counts)) {
1142
+ expect(window).toBe(SNAPSHOT_TEXT);
1143
+ }
1144
+ expect(resetButtons()).toHaveLength(0);
1145
+ });
1146
+
1147
+ test("a drag on the companion metric card zooms the card alone; the snapshot is not zoomed", async () => {
1148
+ await renderPanel(primary);
1149
+ await windowsAskedForBy(METRICS_COMPANION);
1150
+ await waitFor(() => {
1151
+ expect(last(metricChartWindows())).toBe(SNAPSHOT_TEXT);
1152
+ });
1153
+
1154
+ fireEvent.click(
1155
+ await screen.findByRole("button", {
1156
+ name: "Drag across the metric chart",
1157
+ }),
1158
+ );
1159
+
1160
+ await waitFor(() => {
1161
+ expect(last(metricChartWindows())).toBe(CARD_DRAG_TEXT);
1162
+ });
1163
+ // The card's own way back; none beside the badge.
1164
+ expect(resetButtons()).toHaveLength(1);
1165
+ expect(badgeResetButtons()).toHaveLength(0);
1166
+
1167
+ await backOnThePrimary(primary);
1168
+ expect(pickerLabel(primary)).toBe(labelOf(SNAPSHOT_TEXT));
1169
+ expect(resetButtons()).toHaveLength(0);
1170
+ });
1171
+
1172
+ test("while the snapshot is zoomed, a companion explorer's own zoom stays its own", async () => {
1173
+ await renderPanel(primary);
1174
+ await zoomFromThePrimary(primary);
1175
+ const companion: CompanionCase = primary.companionExplorer;
1176
+ await windowsAskedForBy(companion);
1177
+ await volumeChartDrawn();
1178
+
1179
+ // Inside the slice, from the 17:51 bar through the 17:52 one.
1180
+ dragBars(BAR_1751, BAR_1752);
1181
+
1182
+ await expectLatest(companion.sources, NESTED_TEXT);
1183
+ // Two zooms now, two ways back: the companion's and the snapshot's.
1184
+ expect(resetButtons()).toHaveLength(2);
1185
+ expect(badgeResetButtons()).toHaveLength(1);
1186
+
1187
+ // The snapshot kept its slice.
1188
+ await backOnThePrimary(primary);
1189
+ await expectLatest(primary.sources, SLICE_TEXT);
1190
+ expect(pickerLabel(primary)).toBe(labelOf(SLICE_TEXT));
1191
+ expect(badgeResetButtons()).toHaveLength(1);
1192
+ });
1193
+ },
1194
+ );