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