@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.
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/RunnerJob.ts +75 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +207 -25
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/TelemetryIngest.ts +41 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/IncidentService.ts +280 -104
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/RunnerJobService.ts +241 -104
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +737 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +199 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +178 -92
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,965 @@
|
|
|
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
|
+
* Double-click to undo a zoom on the raw recharts histograms - the
|
|
16
|
+
* telemetry histogram of the traces, exceptions and security explorers,
|
|
17
|
+
* and the logs histogram - when the chart's new bars land in the middle of
|
|
18
|
+
* the double-click (issue #4116). Driven through REAL recharts and the real
|
|
19
|
+
* useHistogramRangeSelection, as HistogramRangeSelectionRealRecharts.test.tsx
|
|
20
|
+
* does: only ResponsiveContainer is stood in for.
|
|
21
|
+
*
|
|
22
|
+
* Right after a drag zooms an explorer, the explorer refetches, and that is
|
|
23
|
+
* exactly when a reader double-clicks to undo the zoom. New bars landing
|
|
24
|
+
* during the double-click's second press take the node that press went
|
|
25
|
+
* down on off the page: recharts re-keys every bar, and drops the band the
|
|
26
|
+
* first click painted. Chrome sends no click and no dblclick for such a
|
|
27
|
+
* press, so the chart never heard the double-click and stayed zoomed.
|
|
28
|
+
* Worse, the hook read the release - whose bar recharts now takes from the
|
|
29
|
+
* NEW bars, at the same place - as a drag from an old bar to a new one, and
|
|
30
|
+
* zoomed into a window spanning both. New bars landing during the FIRST
|
|
31
|
+
* press zoomed the same bogus way.
|
|
32
|
+
*
|
|
33
|
+
* Each case fires what Chrome delivers, and reads the page to show why:
|
|
34
|
+
* after the release of a press whose node the new bars took away, nothing;
|
|
35
|
+
* after one whose node stayed, the click and the dblclick, to that node.
|
|
36
|
+
* (One case has a browser send the lost pair anyway.) The second press of
|
|
37
|
+
* a double-click (MouseEvent.detail 2, the browser's own count) is now the
|
|
38
|
+
* zoom-out whatever becomes of its node, so the chart zooms out exactly
|
|
39
|
+
* once either way - by the next task at the latest - and neither press
|
|
40
|
+
* zooms in.
|
|
41
|
+
*
|
|
42
|
+
* On an unzoomed chart, the clamp case of commit b6ec899df4: a still press
|
|
43
|
+
* on a bar right of where a shorter refetch ends. recharts keeps the index
|
|
44
|
+
* the pointer last hovered and clamps it onto the new last bar, so the
|
|
45
|
+
* release reports another index as well as another bar - what a hand's
|
|
46
|
+
* drift across a bar's edge reports - and the chart zoomed into the bar it
|
|
47
|
+
* pressed, which the refetch had left out. It must zoom nowhere, while a
|
|
48
|
+
* real drift of a few pixels is still a click on the bar pressed. The
|
|
49
|
+
* tests listen in on what recharts hands the hook (listenInOnTheHook), so
|
|
50
|
+
* the clamp is shown at work rather than assumed.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
jest.mock("recharts", () => {
|
|
54
|
+
const actual: Record<string, any> = jest.requireActual("recharts");
|
|
55
|
+
const react: typeof React = jest.requireActual("react") as typeof React;
|
|
56
|
+
return {
|
|
57
|
+
...actual,
|
|
58
|
+
ResponsiveContainer: ({ children }: { children: React.ReactElement }) => {
|
|
59
|
+
return react.cloneElement(children, { width: 600, height: 300 });
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
import LogsHistogram from "../../../../UI/Components/LogsViewer/components/LogsHistogram";
|
|
65
|
+
import { HistogramBucket as LogsHistogramBucket } from "../../../../UI/Components/LogsViewer/types";
|
|
66
|
+
import TelemetryHistogram from "../../../../UI/Components/TelemetryViewer/components/TelemetryHistogram";
|
|
67
|
+
import { HistogramBucket as TelemetryHistogramBucket } from "../../../../UI/Components/TelemetryViewer/types";
|
|
68
|
+
import {
|
|
69
|
+
ChartPointerEvent,
|
|
70
|
+
DOUBLE_CLICK_DISAMBIGUATION_MS,
|
|
71
|
+
} from "../../../../UI/Components/Charts/ChartLibrary/Utils/DoubleClick";
|
|
72
|
+
import * as HistogramRangeSelection from "../../../../UI/Components/Charts/Utils/useHistogramRangeSelection";
|
|
73
|
+
|
|
74
|
+
const MINUTE_MS: number = 60 * 1000;
|
|
75
|
+
// One animation frame, as fake timers run them.
|
|
76
|
+
const FRAME_MS: number = 16;
|
|
77
|
+
/*
|
|
78
|
+
* Low in the plot, inside every bar pressed below, so a press there goes
|
|
79
|
+
* down on a bar (recharts paints the bars over the selection band).
|
|
80
|
+
*/
|
|
81
|
+
const POINTER_Y: number = 250;
|
|
82
|
+
|
|
83
|
+
interface Bar {
|
|
84
|
+
time: string;
|
|
85
|
+
count: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// `count` one-minute bars from `from`, each as tall as `height` says.
|
|
89
|
+
function minuteBars(
|
|
90
|
+
from: string,
|
|
91
|
+
count: number,
|
|
92
|
+
height: (index: number) => number,
|
|
93
|
+
): Array<Bar> {
|
|
94
|
+
const fromMs: number = Date.parse(from);
|
|
95
|
+
return Array.from({ length: count }, (_: unknown, index: number): Bar => {
|
|
96
|
+
return {
|
|
97
|
+
time: new Date(fromMs + index * MINUTE_MS).toISOString(),
|
|
98
|
+
count: height(index),
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// What the zoomed chart shows as the reader double-clicks: 10:00 to 10:09.
|
|
104
|
+
const SHOWN_BARS: Array<Bar> = minuteBars(
|
|
105
|
+
"2026-09-28T10:00:00.000Z",
|
|
106
|
+
10,
|
|
107
|
+
(index: number): number => {
|
|
108
|
+
return 5 + index;
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
* The explorer's refetch, landing in the middle of the double-click: a
|
|
114
|
+
* later hour's bars, 11:00 to 11:09, of other heights.
|
|
115
|
+
*/
|
|
116
|
+
const REFETCHED_BARS: Array<Bar> = minuteBars(
|
|
117
|
+
"2026-09-28T11:00:00.000Z",
|
|
118
|
+
10,
|
|
119
|
+
(index: number): number => {
|
|
120
|
+
return 40 + 3 * index;
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
/*
|
|
125
|
+
* The hour the reader zoomed in from, 09:30 to 10:29, which the zoom-out
|
|
126
|
+
* goes back to: it has the clicked bar, 10:04, in it again.
|
|
127
|
+
*/
|
|
128
|
+
const ZOOMED_FROM_BARS: Array<Bar> = minuteBars(
|
|
129
|
+
"2026-09-28T09:30:00.000Z",
|
|
130
|
+
60,
|
|
131
|
+
(index: number): number => {
|
|
132
|
+
return 3 + (index % 7);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
type SelectHandler = (startTime: Date, endTime: Date) => void;
|
|
137
|
+
|
|
138
|
+
interface HistogramProps {
|
|
139
|
+
bars: Array<Bar>;
|
|
140
|
+
onTimeRangeSelect: SelectHandler;
|
|
141
|
+
// Set only while the chart shows a window dragged out of it.
|
|
142
|
+
onZoomOut?: (() => void) | undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// What a host may hand the chart anew along with its next bars.
|
|
146
|
+
type HostHandlers = Partial<Pick<HistogramProps, "onZoomOut">>;
|
|
147
|
+
|
|
148
|
+
interface HistogramUnderTest {
|
|
149
|
+
name: string;
|
|
150
|
+
render: (props: HistogramProps) => React.ReactElement;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const HISTOGRAMS: Array<HistogramUnderTest> = [
|
|
154
|
+
{
|
|
155
|
+
name: "telemetry",
|
|
156
|
+
render: (props: HistogramProps): React.ReactElement => {
|
|
157
|
+
return (
|
|
158
|
+
<TelemetryHistogram
|
|
159
|
+
buckets={props.bars.map((bar: Bar): TelemetryHistogramBucket => {
|
|
160
|
+
return { time: bar.time, series: "error", count: bar.count };
|
|
161
|
+
})}
|
|
162
|
+
isLoading={false}
|
|
163
|
+
series={[{ key: "error", label: "Error", color: "#dc2626" }]}
|
|
164
|
+
onTimeRangeSelect={props.onTimeRangeSelect}
|
|
165
|
+
onZoomOut={props.onZoomOut}
|
|
166
|
+
bucketIntervalMs={MINUTE_MS}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "logs",
|
|
173
|
+
render: (props: HistogramProps): React.ReactElement => {
|
|
174
|
+
return (
|
|
175
|
+
<LogsHistogram
|
|
176
|
+
buckets={props.bars.map((bar: Bar): LogsHistogramBucket => {
|
|
177
|
+
return { time: bar.time, severity: "Error", count: bar.count };
|
|
178
|
+
})}
|
|
179
|
+
isLoading={false}
|
|
180
|
+
onTimeRangeSelect={props.onTimeRangeSelect}
|
|
181
|
+
onZoomOut={props.onZoomOut}
|
|
182
|
+
bucketIntervalMs={MINUTE_MS}
|
|
183
|
+
/>
|
|
184
|
+
);
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
interface ChartOnScreen {
|
|
190
|
+
histogram: HistogramUnderTest;
|
|
191
|
+
props: HistogramProps;
|
|
192
|
+
rendered: RenderResult;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let onScreen: ChartOnScreen | null = null;
|
|
196
|
+
|
|
197
|
+
function showChart(histogram: HistogramUnderTest, props: HistogramProps): void {
|
|
198
|
+
onScreen = {
|
|
199
|
+
histogram: histogram,
|
|
200
|
+
props: props,
|
|
201
|
+
rendered: render(histogram.render(props)),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function chartOnScreen(): ChartOnScreen {
|
|
206
|
+
if (!onScreen) {
|
|
207
|
+
throw new Error("no histogram rendered");
|
|
208
|
+
}
|
|
209
|
+
return onScreen;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/*
|
|
213
|
+
* New bars land: the host re-renders the same chart with them, and with
|
|
214
|
+
* whatever handlers it holds by then.
|
|
215
|
+
*/
|
|
216
|
+
function landBars(bars: Array<Bar>, handlers: HostHandlers = {}): void {
|
|
217
|
+
const chart: ChartOnScreen = chartOnScreen();
|
|
218
|
+
chart.props = { ...chart.props, ...handlers, bars: bars };
|
|
219
|
+
chart.rendered.rerender(chart.histogram.render(chart.props));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function chartContainer(): HTMLElement {
|
|
223
|
+
return chartOnScreen().rendered.container;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// The chart root, where recharts listens for the pointer.
|
|
227
|
+
function chartWrapper(): Element {
|
|
228
|
+
const wrapper: Element | null =
|
|
229
|
+
chartContainer().querySelector(".recharts-wrapper");
|
|
230
|
+
if (!wrapper) {
|
|
231
|
+
throw new Error("recharts drew no chart");
|
|
232
|
+
}
|
|
233
|
+
return wrapper;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// The box around the plot, which holds the chart's dblclick handler.
|
|
237
|
+
function chartBox(): Element {
|
|
238
|
+
const box: Element | null = chartWrapper().parentElement;
|
|
239
|
+
if (!box) {
|
|
240
|
+
throw new Error("the chart has no box around it");
|
|
241
|
+
}
|
|
242
|
+
return box;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
* The x of a bar: the centre of its tick label. The labels are local times
|
|
247
|
+
* ("10:04 AM", or "10:04" on a 24-hour clock).
|
|
248
|
+
*/
|
|
249
|
+
function tickX(label: string): number {
|
|
250
|
+
const tick: Element | undefined = Array.from(
|
|
251
|
+
chartContainer().querySelectorAll(
|
|
252
|
+
".recharts-xAxis-tick-labels text.recharts-cartesian-axis-tick-value",
|
|
253
|
+
),
|
|
254
|
+
).find((element: Element): boolean => {
|
|
255
|
+
return (element.textContent || "").startsWith(label);
|
|
256
|
+
});
|
|
257
|
+
if (!tick) {
|
|
258
|
+
throw new Error(`recharts drew no tick label "${label}"`);
|
|
259
|
+
}
|
|
260
|
+
return Number(tick.getAttribute("x"));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
interface PointerSpot {
|
|
264
|
+
clientX: number;
|
|
265
|
+
clientY: number;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Where the pointer stays for a whole double-click: on the bar `label`.
|
|
269
|
+
function spotOnBar(label: string): PointerSpot {
|
|
270
|
+
return { clientX: tickX(label), clientY: POINTER_Y };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Every bar recharts drew, as nodes on the page.
|
|
274
|
+
function drawnBars(): Array<Element> {
|
|
275
|
+
return Array.from(
|
|
276
|
+
chartContainer().querySelectorAll(
|
|
277
|
+
".recharts-bar-rectangle path.recharts-rectangle",
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// The selection band, as a node on the page, while one is drawn.
|
|
283
|
+
function drawnBand(): Element | null {
|
|
284
|
+
return chartContainer().querySelector(".recharts-reference-area-rect");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function covers(node: Element, spot: PointerSpot): boolean {
|
|
288
|
+
const left: number = Number(node.getAttribute("x"));
|
|
289
|
+
const top: number = Number(node.getAttribute("y"));
|
|
290
|
+
const width: number = Number(node.getAttribute("width"));
|
|
291
|
+
const height: number = Number(node.getAttribute("height"));
|
|
292
|
+
return (
|
|
293
|
+
spot.clientX >= left &&
|
|
294
|
+
spot.clientX <= left + width &&
|
|
295
|
+
spot.clientY >= top &&
|
|
296
|
+
spot.clientY <= top + height
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// The bar at `spot`: the node a press there goes down on.
|
|
301
|
+
function barAt(spot: PointerSpot): Element {
|
|
302
|
+
const bar: Element | undefined = drawnBars().find(
|
|
303
|
+
(node: Element): boolean => {
|
|
304
|
+
return covers(node, spot);
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
if (!bar) {
|
|
308
|
+
throw new Error(
|
|
309
|
+
`recharts drew no bar at (${spot.clientX}, ${spot.clientY})`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
return bar;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Everything drawn at `spot`: its bar, and the band behind it, if any.
|
|
316
|
+
function nodesAt(spot: PointerSpot): Array<Element> {
|
|
317
|
+
const band: Element | null = drawnBand();
|
|
318
|
+
if (band && covers(band, spot)) {
|
|
319
|
+
return [barAt(spot), band];
|
|
320
|
+
}
|
|
321
|
+
return [barAt(spot)];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function stillOnThePage(nodes: Array<Element>): Array<Element> {
|
|
325
|
+
return nodes.filter((node: Element): boolean => {
|
|
326
|
+
return node.isConnected;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/*
|
|
331
|
+
* The explorer's refetch lands while a press is held at `spot`, and takes
|
|
332
|
+
* what the press went down on off the page - recharts re-keys every bar
|
|
333
|
+
* for the new ones, and a band whose bar is gone is not drawn - while the
|
|
334
|
+
* chart root the release reaches stays. That is what makes Chrome send the
|
|
335
|
+
* press no click and no dblclick.
|
|
336
|
+
*/
|
|
337
|
+
function landBarsUnderThePress(
|
|
338
|
+
spot: PointerSpot,
|
|
339
|
+
bars: Array<Bar>,
|
|
340
|
+
handlers: HostHandlers = {},
|
|
341
|
+
): void {
|
|
342
|
+
const pressedNodes: Array<Element> = nodesAt(spot);
|
|
343
|
+
const barsBefore: Array<Element> = drawnBars();
|
|
344
|
+
const wrapperBefore: Element = chartWrapper();
|
|
345
|
+
|
|
346
|
+
landBars(bars, handlers);
|
|
347
|
+
|
|
348
|
+
expect(stillOnThePage(pressedNodes)).toEqual([]);
|
|
349
|
+
expect(stillOnThePage(barsBefore)).toEqual([]);
|
|
350
|
+
expect(chartWrapper()).toBe(wrapperBefore);
|
|
351
|
+
// A new bar is under the pointer now.
|
|
352
|
+
expect(pressedNodes).not.toContain(barAt(spot));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// The pointer comes to rest at `spot`.
|
|
356
|
+
function restAt(spot: PointerSpot): void {
|
|
357
|
+
fireEvent.mouseMove(chartWrapper(), { ...spot, buttons: 0 });
|
|
358
|
+
nextFrame();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/*
|
|
362
|
+
* The main button goes down at `spot`. `clickCount` is the browser's own
|
|
363
|
+
* count (MouseEvent.detail): 2 for the second press of a double-click.
|
|
364
|
+
*/
|
|
365
|
+
function press(spot: PointerSpot, clickCount: number): void {
|
|
366
|
+
fireEvent.mouseDown(chartWrapper(), {
|
|
367
|
+
...spot,
|
|
368
|
+
button: 0,
|
|
369
|
+
buttons: 1,
|
|
370
|
+
detail: clickCount,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function release(spot: PointerSpot, clickCount: number): void {
|
|
375
|
+
fireEvent.mouseUp(chartWrapper(), {
|
|
376
|
+
...spot,
|
|
377
|
+
button: 0,
|
|
378
|
+
buttons: 0,
|
|
379
|
+
detail: clickCount,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// The click the browser sends after a release, to `target`.
|
|
384
|
+
function browserClick(
|
|
385
|
+
target: Element,
|
|
386
|
+
spot: PointerSpot,
|
|
387
|
+
clickCount: number,
|
|
388
|
+
): void {
|
|
389
|
+
fireEvent.click(target, { ...spot, button: 0, detail: clickCount });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// The dblclick the browser sends straight after a second click.
|
|
393
|
+
function browserDoubleClick(target: Element, spot: PointerSpot): void {
|
|
394
|
+
fireEvent.doubleClick(target, { ...spot, button: 0, detail: 2 });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/*
|
|
398
|
+
* The first click of a double-click, with nothing landing under it: its
|
|
399
|
+
* bar stays on the page, so the browser sends that bar the click.
|
|
400
|
+
*/
|
|
401
|
+
function firstClick(spot: PointerSpot): void {
|
|
402
|
+
const pressedBar: Element = barAt(spot);
|
|
403
|
+
press(spot, 1);
|
|
404
|
+
release(spot, 1);
|
|
405
|
+
expect(pressedBar.isConnected).toBe(true);
|
|
406
|
+
browserClick(pressedBar, spot, 1);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/*
|
|
410
|
+
* The second click of a double-click, with nothing landing under it: its
|
|
411
|
+
* bar stays on the page, so the browser sends that bar the click and the
|
|
412
|
+
* dblclick.
|
|
413
|
+
*/
|
|
414
|
+
function quietSecondClick(spot: PointerSpot): void {
|
|
415
|
+
const pressedBar: Element = barAt(spot);
|
|
416
|
+
press(spot, 2);
|
|
417
|
+
release(spot, 2);
|
|
418
|
+
expect(pressedBar.isConnected).toBe(true);
|
|
419
|
+
browserClick(pressedBar, spot, 2);
|
|
420
|
+
browserDoubleClick(pressedBar, spot);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// One animation frame passes: fake timers run the frame's callbacks.
|
|
424
|
+
function nextFrame(): void {
|
|
425
|
+
act(() => {
|
|
426
|
+
jest.advanceTimersByTime(FRAME_MS);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// The browser moves on to the next task: a timeout set for 0ms runs.
|
|
431
|
+
function nextTask(): void {
|
|
432
|
+
act(() => {
|
|
433
|
+
jest.advanceTimersByTime(0);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Long past the double-click window: a click still waiting to zoom in has.
|
|
438
|
+
function waitOutTheDoubleClickWindow(): void {
|
|
439
|
+
act(() => {
|
|
440
|
+
jest.advanceTimersByTime(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Every zoom-in asked for, as [start, end] instants.
|
|
445
|
+
function selections(onTimeRangeSelect: MockFunction): Array<[string, string]> {
|
|
446
|
+
return onTimeRangeSelect.mock.calls.map(
|
|
447
|
+
(call: Array<unknown>): [string, string] => {
|
|
448
|
+
return [(call[0] as Date).toISOString(), (call[1] as Date).toISOString()];
|
|
449
|
+
},
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
type ChartRootHandlerName = "onMouseDown" | "onMouseMove" | "onMouseUp";
|
|
454
|
+
|
|
455
|
+
// What recharts handed one of the histogram's chart-root handlers.
|
|
456
|
+
interface HandedToTheHook {
|
|
457
|
+
handler: ChartRootHandlerName;
|
|
458
|
+
activeTooltipIndex: string | number | null | undefined;
|
|
459
|
+
activeLabel: string | number | undefined;
|
|
460
|
+
clientX: number | undefined;
|
|
461
|
+
detail: number | undefined;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/*
|
|
465
|
+
* Listens in on what recharts hands the histogram: the host still renders
|
|
466
|
+
* the real useHistogramRangeSelection, whose chart-root handlers are
|
|
467
|
+
* wrapped to note the chart state and the event before passing both on
|
|
468
|
+
* untouched. Installed before the chart renders; afterEach restores it.
|
|
469
|
+
*/
|
|
470
|
+
function listenInOnTheHook(): Array<HandedToTheHook> {
|
|
471
|
+
const handed: Array<HandedToTheHook> = [];
|
|
472
|
+
const realHook: HistogramRangeSelection.UseHistogramRangeSelectionFunction =
|
|
473
|
+
HistogramRangeSelection.default;
|
|
474
|
+
|
|
475
|
+
type ChartRootHandler =
|
|
476
|
+
HistogramRangeSelection.HistogramRangeSelectionState[ChartRootHandlerName];
|
|
477
|
+
|
|
478
|
+
const noting: (
|
|
479
|
+
name: ChartRootHandlerName,
|
|
480
|
+
handler: ChartRootHandler,
|
|
481
|
+
) => ChartRootHandler = (
|
|
482
|
+
name: ChartRootHandlerName,
|
|
483
|
+
handler: ChartRootHandler,
|
|
484
|
+
): ChartRootHandler => {
|
|
485
|
+
return (
|
|
486
|
+
state?: HistogramRangeSelection.HistogramPointerState | null,
|
|
487
|
+
event?: ChartPointerEvent | null,
|
|
488
|
+
): void => {
|
|
489
|
+
handed.push({
|
|
490
|
+
handler: name,
|
|
491
|
+
activeTooltipIndex: state?.activeTooltipIndex,
|
|
492
|
+
activeLabel: state?.activeLabel,
|
|
493
|
+
clientX: event?.clientX,
|
|
494
|
+
detail: event?.detail,
|
|
495
|
+
});
|
|
496
|
+
handler(state, event);
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
jest
|
|
501
|
+
.spyOn(HistogramRangeSelection, "default")
|
|
502
|
+
.mockImplementation(
|
|
503
|
+
(
|
|
504
|
+
options: HistogramRangeSelection.HistogramRangeSelectionOptions,
|
|
505
|
+
): HistogramRangeSelection.HistogramRangeSelectionState => {
|
|
506
|
+
const selection: HistogramRangeSelection.HistogramRangeSelectionState =
|
|
507
|
+
realHook(options);
|
|
508
|
+
return {
|
|
509
|
+
...selection,
|
|
510
|
+
onMouseDown: noting("onMouseDown", selection.onMouseDown),
|
|
511
|
+
onMouseMove: noting("onMouseMove", selection.onMouseMove),
|
|
512
|
+
onMouseUp: noting("onMouseUp", selection.onMouseUp),
|
|
513
|
+
};
|
|
514
|
+
},
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
return handed;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// What the named handler was handed last.
|
|
521
|
+
function lastHanded(
|
|
522
|
+
handed: Array<HandedToTheHook>,
|
|
523
|
+
handler: ChartRootHandlerName,
|
|
524
|
+
): HandedToTheHook {
|
|
525
|
+
const last: HandedToTheHook | undefined = handed
|
|
526
|
+
.filter((entry: HandedToTheHook): boolean => {
|
|
527
|
+
return entry.handler === handler;
|
|
528
|
+
})
|
|
529
|
+
.pop();
|
|
530
|
+
if (!last) {
|
|
531
|
+
throw new Error(`recharts handed ${handler} nothing`);
|
|
532
|
+
}
|
|
533
|
+
return last;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// The index of the drawn bar whose width spans `clientX`, or -1.
|
|
537
|
+
function barIndexAtX(clientX: number): number {
|
|
538
|
+
return drawnBars().findIndex((node: Element): boolean => {
|
|
539
|
+
const left: number = Number(node.getAttribute("x"));
|
|
540
|
+
const width: number = Number(node.getAttribute("width"));
|
|
541
|
+
return clientX >= left && clientX <= left + width;
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/*
|
|
546
|
+
* Where the pointer lands on the drawn bar at `index`, left to right: the
|
|
547
|
+
* middle of its width, in the whole pixels a mouse event reports.
|
|
548
|
+
*/
|
|
549
|
+
function spotOnBarAt(index: number): PointerSpot {
|
|
550
|
+
const bar: Element | undefined = drawnBars()[index];
|
|
551
|
+
if (!bar) {
|
|
552
|
+
throw new Error(`recharts drew no bar at index ${index}`);
|
|
553
|
+
}
|
|
554
|
+
const left: number = Number(bar.getAttribute("x"));
|
|
555
|
+
const width: number = Number(bar.getAttribute("width"));
|
|
556
|
+
return { clientX: Math.round(left + width / 2), clientY: POINTER_Y };
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
beforeEach(() => {
|
|
560
|
+
/*
|
|
561
|
+
* Installed AT the fixed time, not moved there with setSystemTime: fake
|
|
562
|
+
* timers count animation frames from the moment they are installed.
|
|
563
|
+
*/
|
|
564
|
+
jest.useFakeTimers({ now: new Date("2026-09-28T11:30:00.000Z") });
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
afterEach(() => {
|
|
568
|
+
cleanup();
|
|
569
|
+
onScreen = null;
|
|
570
|
+
jest.restoreAllMocks();
|
|
571
|
+
jest.useRealTimers();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
for (const histogram of HISTOGRAMS) {
|
|
575
|
+
describe(`the ${histogram.name} histogram, zoomed, through real recharts`, () => {
|
|
576
|
+
test("new bars landing during the second press: the double-click zooms out by the next task, though the browser sends it no click and no dblclick", () => {
|
|
577
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
578
|
+
const onZoomOut: MockFunction = getJestMockFunction();
|
|
579
|
+
showChart(histogram, {
|
|
580
|
+
bars: SHOWN_BARS,
|
|
581
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
582
|
+
onZoomOut: onZoomOut,
|
|
583
|
+
});
|
|
584
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
585
|
+
|
|
586
|
+
restAt(spot);
|
|
587
|
+
firstClick(spot);
|
|
588
|
+
// Its band waits out the double-click window behind the bar.
|
|
589
|
+
const band: Element | null = drawnBand();
|
|
590
|
+
expect(band).not.toBeNull();
|
|
591
|
+
|
|
592
|
+
press(spot, 2);
|
|
593
|
+
landBarsUnderThePress(spot, REFETCHED_BARS);
|
|
594
|
+
expect(band?.isConnected).toBe(false);
|
|
595
|
+
// The still pointer is on a bar of the new window.
|
|
596
|
+
expect(tickX("11:04")).toBeCloseTo(spot.clientX, 5);
|
|
597
|
+
release(spot, 2);
|
|
598
|
+
// And nothing more: Chrome sends this press no click and no dblclick.
|
|
599
|
+
|
|
600
|
+
nextTask();
|
|
601
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
602
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
603
|
+
|
|
604
|
+
waitOutTheDoubleClickWindow();
|
|
605
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
606
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
test("new bars landing during the second press, and the browser sends the click and dblclick after all: it zooms out exactly once", () => {
|
|
610
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
611
|
+
const onZoomOut: MockFunction = getJestMockFunction();
|
|
612
|
+
showChart(histogram, {
|
|
613
|
+
bars: SHOWN_BARS,
|
|
614
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
615
|
+
onZoomOut: onZoomOut,
|
|
616
|
+
});
|
|
617
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
618
|
+
|
|
619
|
+
restAt(spot);
|
|
620
|
+
firstClick(spot);
|
|
621
|
+
press(spot, 2);
|
|
622
|
+
landBarsUnderThePress(spot, REFETCHED_BARS);
|
|
623
|
+
release(spot, 2);
|
|
624
|
+
/*
|
|
625
|
+
* A browser that sends them anyway sends them to a node still on the
|
|
626
|
+
* page, such as the box around the plot.
|
|
627
|
+
*/
|
|
628
|
+
browserClick(chartBox(), spot, 2);
|
|
629
|
+
browserDoubleClick(chartBox(), spot);
|
|
630
|
+
|
|
631
|
+
nextTask();
|
|
632
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
633
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
634
|
+
|
|
635
|
+
waitOutTheDoubleClickWindow();
|
|
636
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
637
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
test("new bars landing during the first press: neither press zooms in, and the second still zooms out once", () => {
|
|
641
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
642
|
+
const onZoomOut: MockFunction = getJestMockFunction();
|
|
643
|
+
showChart(histogram, {
|
|
644
|
+
bars: SHOWN_BARS,
|
|
645
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
646
|
+
onZoomOut: onZoomOut,
|
|
647
|
+
});
|
|
648
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
649
|
+
|
|
650
|
+
restAt(spot);
|
|
651
|
+
press(spot, 1);
|
|
652
|
+
landBarsUnderThePress(spot, REFETCHED_BARS);
|
|
653
|
+
release(spot, 1);
|
|
654
|
+
// No click follows: the bar the press went down on is gone.
|
|
655
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
656
|
+
// Nor is a bar the reader never pressed marked as about to open.
|
|
657
|
+
expect(drawnBand()).toBeNull();
|
|
658
|
+
|
|
659
|
+
// The second press goes down on a bar of the new window, which stays.
|
|
660
|
+
quietSecondClick(spot);
|
|
661
|
+
|
|
662
|
+
nextTask();
|
|
663
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
664
|
+
|
|
665
|
+
waitOutTheDoubleClickWindow();
|
|
666
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
667
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test("a quiet double-click on a bar zooms out once and zooms into nothing", () => {
|
|
671
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
672
|
+
const onZoomOut: MockFunction = getJestMockFunction();
|
|
673
|
+
showChart(histogram, {
|
|
674
|
+
bars: SHOWN_BARS,
|
|
675
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
676
|
+
onZoomOut: onZoomOut,
|
|
677
|
+
});
|
|
678
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
679
|
+
|
|
680
|
+
restAt(spot);
|
|
681
|
+
firstClick(spot);
|
|
682
|
+
expect(drawnBand()).not.toBeNull();
|
|
683
|
+
quietSecondClick(spot);
|
|
684
|
+
|
|
685
|
+
nextTask();
|
|
686
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
687
|
+
// The zoom-out took the first click's band with it.
|
|
688
|
+
expect(drawnBand()).toBeNull();
|
|
689
|
+
|
|
690
|
+
waitOutTheDoubleClickWindow();
|
|
691
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
692
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test("new bars landing between the two clicks: it zooms out once, and the first click's zoom into a bar that is gone never happens", () => {
|
|
696
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
697
|
+
const onZoomOut: MockFunction = getJestMockFunction();
|
|
698
|
+
showChart(histogram, {
|
|
699
|
+
bars: SHOWN_BARS,
|
|
700
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
701
|
+
onZoomOut: onZoomOut,
|
|
702
|
+
});
|
|
703
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
704
|
+
|
|
705
|
+
restAt(spot);
|
|
706
|
+
firstClick(spot);
|
|
707
|
+
landBars(REFETCHED_BARS);
|
|
708
|
+
// The band went with its bar; the first click still waits to zoom in.
|
|
709
|
+
expect(drawnBand()).toBeNull();
|
|
710
|
+
quietSecondClick(spot);
|
|
711
|
+
|
|
712
|
+
nextTask();
|
|
713
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
714
|
+
|
|
715
|
+
waitOutTheDoubleClickWindow();
|
|
716
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
717
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
/*
|
|
721
|
+
* A host hands the chart a new zoom-out handler with every render, and
|
|
722
|
+
* the one it hands over with the new bars is the one that knows them.
|
|
723
|
+
*/
|
|
724
|
+
test("the zoom-out goes to the handler the host passed with the new bars, not the one it held at the press", () => {
|
|
725
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
726
|
+
const onZoomOutAtThePress: MockFunction = getJestMockFunction();
|
|
727
|
+
const onZoomOutWithTheNewBars: MockFunction = getJestMockFunction();
|
|
728
|
+
showChart(histogram, {
|
|
729
|
+
bars: SHOWN_BARS,
|
|
730
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
731
|
+
onZoomOut: onZoomOutAtThePress,
|
|
732
|
+
});
|
|
733
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
734
|
+
|
|
735
|
+
restAt(spot);
|
|
736
|
+
firstClick(spot);
|
|
737
|
+
press(spot, 2);
|
|
738
|
+
landBarsUnderThePress(spot, REFETCHED_BARS, {
|
|
739
|
+
onZoomOut: onZoomOutWithTheNewBars,
|
|
740
|
+
});
|
|
741
|
+
release(spot, 2);
|
|
742
|
+
|
|
743
|
+
nextTask();
|
|
744
|
+
expect(onZoomOutWithTheNewBars).toHaveBeenCalledTimes(1);
|
|
745
|
+
expect(onZoomOutAtThePress).not.toHaveBeenCalled();
|
|
746
|
+
|
|
747
|
+
waitOutTheDoubleClickWindow();
|
|
748
|
+
expect(onZoomOutWithTheNewBars).toHaveBeenCalledTimes(1);
|
|
749
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
test("the zoom-out takes the first click's band with it: the clicked bar comes back unshaded", () => {
|
|
753
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
754
|
+
const onZoomOut: MockFunction = getJestMockFunction();
|
|
755
|
+
showChart(histogram, {
|
|
756
|
+
bars: SHOWN_BARS,
|
|
757
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
758
|
+
onZoomOut: onZoomOut,
|
|
759
|
+
});
|
|
760
|
+
const spot: PointerSpot = spotOnBar("10:04");
|
|
761
|
+
|
|
762
|
+
restAt(spot);
|
|
763
|
+
firstClick(spot);
|
|
764
|
+
expect(drawnBand()).not.toBeNull();
|
|
765
|
+
press(spot, 2);
|
|
766
|
+
landBarsUnderThePress(spot, REFETCHED_BARS);
|
|
767
|
+
release(spot, 2);
|
|
768
|
+
nextTask();
|
|
769
|
+
expect(onZoomOut).toHaveBeenCalledTimes(1);
|
|
770
|
+
|
|
771
|
+
// Back on the hour it zoomed in from, and no longer zoomed.
|
|
772
|
+
landBars(ZOOMED_FROM_BARS, { onZoomOut: undefined });
|
|
773
|
+
expect(drawnBars()).toHaveLength(ZOOMED_FROM_BARS.length);
|
|
774
|
+
expect(drawnBand()).toBeNull();
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/*
|
|
780
|
+
* An unzoomed chart of thirty one-minute bars, 10:00 to 10:29, and a
|
|
781
|
+
* refetch of only ten, 11:00 to 11:09, landing while a press is held on
|
|
782
|
+
* 10:25 - the 26th bar, right of where the ten new ones end.
|
|
783
|
+
*/
|
|
784
|
+
const HALF_HOUR_BARS: Array<Bar> = minuteBars(
|
|
785
|
+
"2026-09-28T10:00:00.000Z",
|
|
786
|
+
30,
|
|
787
|
+
(index: number): number => {
|
|
788
|
+
return 5 + (index % 4);
|
|
789
|
+
},
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
const SHORTER_REFETCH_BARS: Array<Bar> = minuteBars(
|
|
793
|
+
"2026-09-28T11:00:00.000Z",
|
|
794
|
+
10,
|
|
795
|
+
(index: number): number => {
|
|
796
|
+
return 20 + index;
|
|
797
|
+
},
|
|
798
|
+
);
|
|
799
|
+
|
|
800
|
+
const PRESSED_BAR_INDEX: number = 25;
|
|
801
|
+
const PRESSED_BAR: Bar = HALF_HOUR_BARS[PRESSED_BAR_INDEX]!;
|
|
802
|
+
const REFETCH_LAST_BAR_INDEX: number = SHORTER_REFETCH_BARS.length - 1;
|
|
803
|
+
|
|
804
|
+
/*
|
|
805
|
+
* The clamp case (commit b6ec899df4), through real recharts. recharts
|
|
806
|
+
* works out the bar under the pointer only when the pointer moves, and
|
|
807
|
+
* keeps its index otherwise; past the end of shorter data it clamps that
|
|
808
|
+
* index onto the LAST bar. So the release of a still press on 10:25 reports
|
|
809
|
+
* index 9, 11:09 - another index as well as another bar, exactly what a
|
|
810
|
+
* hand's drift across an edge reports - and the hook took it for one: a
|
|
811
|
+
* click on the bar it pressed, which zoomed an unzoomed explorer into
|
|
812
|
+
* 10:25, a minute the chart no longer shows. The pointer never moved, not
|
|
813
|
+
* by a pixel, and that is what now says the data changed.
|
|
814
|
+
*/
|
|
815
|
+
for (const histogram of HISTOGRAMS) {
|
|
816
|
+
describe(`the ${histogram.name} histogram, not zoomed, through real recharts: a still press on a bar right of where a shorter refetch ends`, () => {
|
|
817
|
+
test("recharts hands the release the refetch's last bar at a clamped index, and the press zooms nowhere", () => {
|
|
818
|
+
const handed: Array<HandedToTheHook> = listenInOnTheHook();
|
|
819
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
820
|
+
showChart(histogram, {
|
|
821
|
+
bars: HALF_HOUR_BARS,
|
|
822
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
823
|
+
});
|
|
824
|
+
expect(drawnBars()).toHaveLength(HALF_HOUR_BARS.length);
|
|
825
|
+
const spot: PointerSpot = spotOnBarAt(PRESSED_BAR_INDEX);
|
|
826
|
+
|
|
827
|
+
restAt(spot);
|
|
828
|
+
press(spot, 1);
|
|
829
|
+
|
|
830
|
+
expect(lastHanded(handed, "onMouseDown")).toEqual({
|
|
831
|
+
handler: "onMouseDown",
|
|
832
|
+
activeTooltipIndex: String(PRESSED_BAR_INDEX),
|
|
833
|
+
activeLabel: PRESSED_BAR.time,
|
|
834
|
+
clientX: spot.clientX,
|
|
835
|
+
detail: 1,
|
|
836
|
+
});
|
|
837
|
+
const handedUpToThePress: number = handed.length;
|
|
838
|
+
|
|
839
|
+
landBars(SHORTER_REFETCH_BARS);
|
|
840
|
+
expect(drawnBars()).toHaveLength(SHORTER_REFETCH_BARS.length);
|
|
841
|
+
release(spot, 1);
|
|
842
|
+
|
|
843
|
+
/*
|
|
844
|
+
* Nothing came between the press and its release - no move at all -
|
|
845
|
+
* and the release, at the very spot of the press, got the refetch's
|
|
846
|
+
* LAST bar at index 9: the stale 25, clamped. Not a bar recharts
|
|
847
|
+
* hit-tested: the one under the pointer now is 11:08.
|
|
848
|
+
*/
|
|
849
|
+
expect(handed.slice(handedUpToThePress)).toEqual([
|
|
850
|
+
{
|
|
851
|
+
handler: "onMouseUp",
|
|
852
|
+
activeTooltipIndex: String(REFETCH_LAST_BAR_INDEX),
|
|
853
|
+
activeLabel: SHORTER_REFETCH_BARS[REFETCH_LAST_BAR_INDEX]!.time,
|
|
854
|
+
clientX: spot.clientX,
|
|
855
|
+
detail: 1,
|
|
856
|
+
},
|
|
857
|
+
]);
|
|
858
|
+
expect(barIndexAtX(spot.clientX)).toBe(REFETCH_LAST_BAR_INDEX - 1);
|
|
859
|
+
|
|
860
|
+
// The bar it pressed is gone: nothing to zoom into, nothing lit.
|
|
861
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
862
|
+
expect(drawnBand()).toBeNull();
|
|
863
|
+
|
|
864
|
+
waitOutTheDoubleClickWindow();
|
|
865
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
866
|
+
expect(drawnBand()).toBeNull();
|
|
867
|
+
|
|
868
|
+
// And the chart is none the worse: a click on a new bar zooms into it.
|
|
869
|
+
const newBarSpot: PointerSpot = spotOnBarAt(4);
|
|
870
|
+
restAt(newBarSpot);
|
|
871
|
+
press(newBarSpot, 1);
|
|
872
|
+
release(newBarSpot, 1);
|
|
873
|
+
expect(selections(onTimeRangeSelect)).toEqual([
|
|
874
|
+
[SHORTER_REFETCH_BARS[4]!.time, SHORTER_REFETCH_BARS[5]!.time],
|
|
875
|
+
]);
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
/*
|
|
879
|
+
* Chrome makes up a mousemove at the pointer's own spot when the page
|
|
880
|
+
* under a still pointer changes. recharts then hit-tests the new bars
|
|
881
|
+
* and reports the one under the pointer - still another index than the
|
|
882
|
+
* press's, and still no movement of the pointer.
|
|
883
|
+
*/
|
|
884
|
+
test("with the move Chrome makes up at the same spot once the new bars are drawn, recharts hit-tests them, and the press still zooms nowhere", () => {
|
|
885
|
+
const handed: Array<HandedToTheHook> = listenInOnTheHook();
|
|
886
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
887
|
+
showChart(histogram, {
|
|
888
|
+
bars: HALF_HOUR_BARS,
|
|
889
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
890
|
+
});
|
|
891
|
+
const spot: PointerSpot = spotOnBarAt(PRESSED_BAR_INDEX);
|
|
892
|
+
|
|
893
|
+
restAt(spot);
|
|
894
|
+
press(spot, 1);
|
|
895
|
+
landBars(SHORTER_REFETCH_BARS);
|
|
896
|
+
fireEvent.mouseMove(chartWrapper(), { ...spot, buttons: 1 });
|
|
897
|
+
|
|
898
|
+
const underThePointer: number = barIndexAtX(spot.clientX);
|
|
899
|
+
expect(underThePointer).toBe(REFETCH_LAST_BAR_INDEX - 1);
|
|
900
|
+
expect(lastHanded(handed, "onMouseMove")).toEqual({
|
|
901
|
+
handler: "onMouseMove",
|
|
902
|
+
activeTooltipIndex: String(underThePointer),
|
|
903
|
+
activeLabel: SHORTER_REFETCH_BARS[underThePointer]!.time,
|
|
904
|
+
clientX: spot.clientX,
|
|
905
|
+
detail: 0,
|
|
906
|
+
});
|
|
907
|
+
// Not a drag: nothing painted.
|
|
908
|
+
expect(drawnBand()).toBeNull();
|
|
909
|
+
|
|
910
|
+
release(spot, 1);
|
|
911
|
+
|
|
912
|
+
expect(lastHanded(handed, "onMouseUp")).toMatchObject({
|
|
913
|
+
activeTooltipIndex: String(underThePointer),
|
|
914
|
+
activeLabel: SHORTER_REFETCH_BARS[underThePointer]!.time,
|
|
915
|
+
clientX: spot.clientX,
|
|
916
|
+
});
|
|
917
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
918
|
+
expect(drawnBand()).toBeNull();
|
|
919
|
+
|
|
920
|
+
waitOutTheDoubleClickWindow();
|
|
921
|
+
expect(selections(onTimeRangeSelect)).toEqual([]);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
/*
|
|
925
|
+
* The rule the clamp case must not break: the pointer really moving
|
|
926
|
+
* two pixels, across the edge between 10:25 and 10:26, changes the
|
|
927
|
+
* index just as the clamp does - and is a hand's drift, still a click
|
|
928
|
+
* on the bar it pressed.
|
|
929
|
+
*/
|
|
930
|
+
test("a press on 10:25 that drifts 2px onto 10:26, with no refetch, is still a click on 10:25", () => {
|
|
931
|
+
const handed: Array<HandedToTheHook> = listenInOnTheHook();
|
|
932
|
+
const onTimeRangeSelect: MockFunction = getJestMockFunction();
|
|
933
|
+
showChart(histogram, {
|
|
934
|
+
bars: HALF_HOUR_BARS,
|
|
935
|
+
onTimeRangeSelect: onTimeRangeSelect,
|
|
936
|
+
});
|
|
937
|
+
// Midway between the two bars' middles: where recharts' 10:25 ends.
|
|
938
|
+
const edgeX: number = Math.floor(
|
|
939
|
+
(spotOnBarAt(PRESSED_BAR_INDEX).clientX +
|
|
940
|
+
spotOnBarAt(PRESSED_BAR_INDEX + 1).clientX) /
|
|
941
|
+
2,
|
|
942
|
+
);
|
|
943
|
+
const pressSpot: PointerSpot = { clientX: edgeX - 1, clientY: POINTER_Y };
|
|
944
|
+
const driftSpot: PointerSpot = { clientX: edgeX + 1, clientY: POINTER_Y };
|
|
945
|
+
|
|
946
|
+
restAt(pressSpot);
|
|
947
|
+
press(pressSpot, 1);
|
|
948
|
+
fireEvent.mouseMove(chartWrapper(), { ...driftSpot, buttons: 1 });
|
|
949
|
+
release(driftSpot, 1);
|
|
950
|
+
|
|
951
|
+
expect(lastHanded(handed, "onMouseDown")).toMatchObject({
|
|
952
|
+
activeTooltipIndex: String(PRESSED_BAR_INDEX),
|
|
953
|
+
activeLabel: PRESSED_BAR.time,
|
|
954
|
+
});
|
|
955
|
+
expect(lastHanded(handed, "onMouseUp")).toMatchObject({
|
|
956
|
+
activeTooltipIndex: String(PRESSED_BAR_INDEX + 1),
|
|
957
|
+
activeLabel: HALF_HOUR_BARS[PRESSED_BAR_INDEX + 1]!.time,
|
|
958
|
+
clientX: driftSpot.clientX,
|
|
959
|
+
});
|
|
960
|
+
expect(selections(onTimeRangeSelect)).toEqual([
|
|
961
|
+
[PRESSED_BAR.time, HALF_HOUR_BARS[PRESSED_BAR_INDEX + 1]!.time],
|
|
962
|
+
]);
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
}
|