@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,928 @@
|
|
|
1
|
+
/** @timezone UTC */
|
|
2
|
+
|
|
3
|
+
import "@testing-library/jest-dom";
|
|
4
|
+
import {
|
|
5
|
+
afterEach,
|
|
6
|
+
beforeEach,
|
|
7
|
+
describe,
|
|
8
|
+
expect,
|
|
9
|
+
jest,
|
|
10
|
+
test,
|
|
11
|
+
} from "@jest/globals";
|
|
12
|
+
import {
|
|
13
|
+
act,
|
|
14
|
+
cleanup,
|
|
15
|
+
fireEvent,
|
|
16
|
+
render,
|
|
17
|
+
screen,
|
|
18
|
+
} from "@testing-library/react";
|
|
19
|
+
import React, {
|
|
20
|
+
FunctionComponent,
|
|
21
|
+
ReactElement,
|
|
22
|
+
useCallback,
|
|
23
|
+
useEffect,
|
|
24
|
+
useRef,
|
|
25
|
+
useState,
|
|
26
|
+
} from "react";
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* Issue #4116, on the explorers the customer reported it from. A drag across
|
|
30
|
+
* an explorer's volume histogram zooms the explorer, which at once asks for
|
|
31
|
+
* the zoomed window's bars - and a reader who wants the old window back
|
|
32
|
+
* double-clicks at about the moment those bars land. Both shells the
|
|
33
|
+
* explorers are built on are mounted here: TelemetryViewer (Traces,
|
|
34
|
+
* Exceptions, Security events) and Common's LogsViewer (Logs). Each sits
|
|
35
|
+
* under a host that owns its window the way the Dashboard's TracesViewer and
|
|
36
|
+
* DashboardLogsViewer do: it asks for a window's bars whenever its window
|
|
37
|
+
* changes and keeps the bars it has until the answer comes, and the test
|
|
38
|
+
* decides when that is. Recharts is real; only ResponsiveContainer is stood
|
|
39
|
+
* in for, and the pointer goes to the plot at the x recharts drew a bar's
|
|
40
|
+
* tick label at.
|
|
41
|
+
*
|
|
42
|
+
* When new bars come in, recharts re-keys every bar and drops a band a
|
|
43
|
+
* click painted over a bar the new window does not have. Chrome sends no
|
|
44
|
+
* click and no dblclick for a press whose node left the page before its
|
|
45
|
+
* release (measured in Chrome with a Playwright harness), so the events
|
|
46
|
+
* fired below are the ones Chrome sent:
|
|
47
|
+
*
|
|
48
|
+
* - The answer lands during the second press: no click, no dblclick. The
|
|
49
|
+
* explorer stayed zoomed, and that release, read against the new bars,
|
|
50
|
+
* was taken for a drag from an old bar to a new one, so the explorer
|
|
51
|
+
* jumped to a window nobody picked. The second press is now the zoom-out
|
|
52
|
+
* itself, and with no dblclick to do it, it happens on the next task.
|
|
53
|
+
* Failed before the fix.
|
|
54
|
+
* - The answer lands during the first press: that release, read the same
|
|
55
|
+
* way, zoomed in once more before the dblclick zoomed out. A press whose
|
|
56
|
+
* bar changed under a pointer that did not leave it now zooms nowhere.
|
|
57
|
+
* Failed before the fix.
|
|
58
|
+
* - The answer lands between the clicks, or before the double-click: the
|
|
59
|
+
* dblclick arrives and the explorer zooms out once, as it did before.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
* The logs viewer loads services and log attributes on mount; nothing here
|
|
64
|
+
* depends on what comes back.
|
|
65
|
+
*/
|
|
66
|
+
jest.mock("../../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
67
|
+
return {
|
|
68
|
+
__esModule: true,
|
|
69
|
+
default: {
|
|
70
|
+
getList: (): Promise<unknown> => {
|
|
71
|
+
return Promise.resolve({ data: [], count: 0, skip: 0, limit: 0 });
|
|
72
|
+
},
|
|
73
|
+
getCommonHeaders: (): Record<string, string> => {
|
|
74
|
+
return {};
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
jest.mock("../../../../UI/Utils/API/API", () => {
|
|
81
|
+
return {
|
|
82
|
+
__esModule: true,
|
|
83
|
+
default: {
|
|
84
|
+
post: (): Promise<unknown> => {
|
|
85
|
+
return Promise.resolve({ data: {} });
|
|
86
|
+
},
|
|
87
|
+
getFriendlyErrorMessage: (error: Error): string => {
|
|
88
|
+
return error.message;
|
|
89
|
+
},
|
|
90
|
+
getFriendlyMessage: (error: Error): string => {
|
|
91
|
+
return error.message;
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* The real one loads the browser's OpenTelemetry set-up, whose zone.js
|
|
99
|
+
* patches Promise for the whole file.
|
|
100
|
+
*/
|
|
101
|
+
jest.mock("../../../../UI/Utils/Project", () => {
|
|
102
|
+
return {
|
|
103
|
+
__esModule: true,
|
|
104
|
+
default: {
|
|
105
|
+
getCurrentProjectId: (): null => {
|
|
106
|
+
return null;
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
jest.mock("recharts", () => {
|
|
113
|
+
const actual: Record<string, unknown> = jest.requireActual(
|
|
114
|
+
"recharts",
|
|
115
|
+
) as Record<string, unknown>;
|
|
116
|
+
const react: typeof React = jest.requireActual("react") as typeof React;
|
|
117
|
+
return {
|
|
118
|
+
...actual,
|
|
119
|
+
ResponsiveContainer: (props: {
|
|
120
|
+
children: React.ReactElement;
|
|
121
|
+
}): React.ReactElement => {
|
|
122
|
+
return react.cloneElement(props.children, { width: 600, height: 300 });
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Imported after the mocks so the viewers pick them up.
|
|
128
|
+
import TelemetryViewer from "../../../../UI/Components/TelemetryViewer/TelemetryViewer";
|
|
129
|
+
import {
|
|
130
|
+
HistogramBucket as TelemetryHistogramBucket,
|
|
131
|
+
HistogramSeriesOption,
|
|
132
|
+
} from "../../../../UI/Components/TelemetryViewer/types";
|
|
133
|
+
import LogsViewer, {
|
|
134
|
+
ComponentProps as LogsViewerProps,
|
|
135
|
+
} from "../../../../UI/Components/LogsViewer/LogsViewer";
|
|
136
|
+
import { HistogramBucket as LogsHistogramBucket } from "../../../../UI/Components/LogsViewer/types";
|
|
137
|
+
import useLogsHistogram, {
|
|
138
|
+
LogsHistogramData,
|
|
139
|
+
LogsHistogramState,
|
|
140
|
+
} from "../../../../UI/Components/LogsViewer/useLogsHistogram";
|
|
141
|
+
import { DOUBLE_CLICK_DISAMBIGUATION_MS } from "../../../../UI/Components/Charts/ChartLibrary/Utils/DoubleClick";
|
|
142
|
+
import InBetween from "../../../../Types/BaseDatabase/InBetween";
|
|
143
|
+
import LogSeverity from "../../../../Types/Log/LogSeverity";
|
|
144
|
+
import RangeStartAndEndDateTime, {
|
|
145
|
+
RangeStartAndEndDateTimeUtil,
|
|
146
|
+
} from "../../../../Types/Time/RangeStartAndEndDateTime";
|
|
147
|
+
import TimeRange from "../../../../Types/Time/TimeRange";
|
|
148
|
+
|
|
149
|
+
const MINUTE_MS: number = 60 * 1000;
|
|
150
|
+
// One animation frame, as fake timers run them.
|
|
151
|
+
const FRAME_MS: number = 16;
|
|
152
|
+
// The explorers open on the past hour: 11:00 to 12:00.
|
|
153
|
+
const NOW: Date = new Date("2026-09-28T12:00:00.000Z");
|
|
154
|
+
// Inside the plot, low enough to be over every bar.
|
|
155
|
+
const PLOT_Y: number = 200;
|
|
156
|
+
|
|
157
|
+
const PAST_HOUR: RangeStartAndEndDateTime = { range: TimeRange.PAST_ONE_HOUR };
|
|
158
|
+
const RESET_HINT: string = "Double-click to reset";
|
|
159
|
+
const HOST_WINDOW_TEST_ID: string = "host-window";
|
|
160
|
+
const TICK_LABEL_SELECTOR: string =
|
|
161
|
+
".recharts-xAxis-tick-labels text.recharts-cartesian-axis-tick-value";
|
|
162
|
+
|
|
163
|
+
// The hour, in five-minute bars.
|
|
164
|
+
const HOUR_BARS: Array<string> = [
|
|
165
|
+
"11:00",
|
|
166
|
+
"11:05",
|
|
167
|
+
"11:10",
|
|
168
|
+
"11:15",
|
|
169
|
+
"11:20",
|
|
170
|
+
"11:25",
|
|
171
|
+
"11:30",
|
|
172
|
+
"11:35",
|
|
173
|
+
"11:40",
|
|
174
|
+
"11:45",
|
|
175
|
+
"11:50",
|
|
176
|
+
"11:55",
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
/*
|
|
180
|
+
* The drag runs from the hour's 11:10 bar to its 11:15 bar, and zooms
|
|
181
|
+
* through the end of the last one. The double-click lands where the drag
|
|
182
|
+
* let go.
|
|
183
|
+
*/
|
|
184
|
+
const DRAG_FROM: string = "11:10";
|
|
185
|
+
const DRAG_TO: string = "11:15";
|
|
186
|
+
const ZOOMED_WINDOW: string =
|
|
187
|
+
"2026-09-28T11:10:00.000Z..2026-09-28T11:20:00.000Z";
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
* The zoomed ten minutes, in two-minute bars. The hour's 11:15 bar, where
|
|
191
|
+
* the double-click lands, is not one of them, so a click on it that zoomed
|
|
192
|
+
* in after all would show up as a zoom into 11:15..11:20.
|
|
193
|
+
*/
|
|
194
|
+
const ZOOMED_BARS: Array<string> = [
|
|
195
|
+
"11:10",
|
|
196
|
+
"11:12",
|
|
197
|
+
"11:14",
|
|
198
|
+
"11:16",
|
|
199
|
+
"11:18",
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const ZOOM_CALL: string = `select ${ZOOMED_WINDOW}`;
|
|
203
|
+
const RESET_CALL: string = `change ${TimeRange.PAST_ONE_HOUR}`;
|
|
204
|
+
|
|
205
|
+
// "Past 1 Hour", or a custom window as start..end.
|
|
206
|
+
function describeRange(range: RangeStartAndEndDateTime): string {
|
|
207
|
+
if (range.range !== TimeRange.CUSTOM || !range.startAndEndDate) {
|
|
208
|
+
return range.range;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return `${new Date(range.startAndEndDate.startValue).toISOString()}..${new Date(
|
|
212
|
+
range.startAndEndDate.endValue,
|
|
213
|
+
).toISOString()}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/*
|
|
217
|
+
* The explorers' histogram endpoint, answering only when the test says so.
|
|
218
|
+
* It buckets a window into about a dozen bars, never narrower than two
|
|
219
|
+
* minutes, and labels them the way ClickHouse does.
|
|
220
|
+
*/
|
|
221
|
+
interface HistogramAnswer {
|
|
222
|
+
bucketStartsMs: Array<number>;
|
|
223
|
+
bucketIntervalMs: number;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
interface HistogramRequest {
|
|
227
|
+
// The host's window when it asked.
|
|
228
|
+
window: string;
|
|
229
|
+
answer: HistogramAnswer;
|
|
230
|
+
resolve: (answer: HistogramAnswer) => void;
|
|
231
|
+
isAnswered: boolean;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const histogramRequests: Array<HistogramRequest> = [];
|
|
235
|
+
|
|
236
|
+
function answerFor(range: RangeStartAndEndDateTime): HistogramAnswer {
|
|
237
|
+
const window: InBetween<Date> =
|
|
238
|
+
RangeStartAndEndDateTimeUtil.getStartAndEndDate(range);
|
|
239
|
+
const startMs: number = new Date(window.startValue).getTime();
|
|
240
|
+
const endMs: number = new Date(window.endValue).getTime();
|
|
241
|
+
const bucketIntervalMs: number =
|
|
242
|
+
Math.max(2, Math.round((endMs - startMs) / MINUTE_MS / 12)) * MINUTE_MS;
|
|
243
|
+
const firstBucketMs: number =
|
|
244
|
+
Math.floor(startMs / bucketIntervalMs) * bucketIntervalMs;
|
|
245
|
+
const count: number = Math.round((endMs - startMs) / bucketIntervalMs);
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
bucketStartsMs: Array.from(
|
|
249
|
+
{ length: count },
|
|
250
|
+
(_: unknown, index: number): number => {
|
|
251
|
+
return firstBucketMs + index * bucketIntervalMs;
|
|
252
|
+
},
|
|
253
|
+
),
|
|
254
|
+
bucketIntervalMs: bucketIntervalMs,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function askForHistogram(
|
|
259
|
+
range: RangeStartAndEndDateTime,
|
|
260
|
+
): Promise<HistogramAnswer> {
|
|
261
|
+
return new Promise<HistogramAnswer>(
|
|
262
|
+
(resolve: (answer: HistogramAnswer) => void): void => {
|
|
263
|
+
histogramRequests.push({
|
|
264
|
+
window: describeRange(range),
|
|
265
|
+
answer: answerFor(range),
|
|
266
|
+
resolve: resolve,
|
|
267
|
+
isAnswered: false,
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// The answer still held back comes in, and the host re-renders with it.
|
|
274
|
+
async function deliverHeldAnswer(): Promise<void> {
|
|
275
|
+
const held: Array<HistogramRequest> = histogramRequests.filter(
|
|
276
|
+
(request: HistogramRequest): boolean => {
|
|
277
|
+
return !request.isAnswered;
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
const request: HistogramRequest | undefined = held[held.length - 1];
|
|
281
|
+
|
|
282
|
+
if (!request) {
|
|
283
|
+
throw new Error("no histogram answer is held back");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await act(async () => {
|
|
287
|
+
request.isAnswered = true;
|
|
288
|
+
request.resolve(request.answer);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function requestedWindows(): Array<string> {
|
|
293
|
+
return histogramRequests.map((request: HistogramRequest): string => {
|
|
294
|
+
return request.window;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function heldWindows(): Array<string> {
|
|
299
|
+
return histogramRequests
|
|
300
|
+
.filter((request: HistogramRequest): boolean => {
|
|
301
|
+
return !request.isAnswered;
|
|
302
|
+
})
|
|
303
|
+
.map((request: HistogramRequest): string => {
|
|
304
|
+
return request.window;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function pad(value: number): string {
|
|
309
|
+
return String(value).padStart(2, "0");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ClickHouse's DateTime rendering: "2026-09-28 11:10:00".
|
|
313
|
+
function bucketLabel(startMs: number): string {
|
|
314
|
+
const date: Date = new Date(startMs);
|
|
315
|
+
return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(
|
|
316
|
+
date.getUTCDate(),
|
|
317
|
+
)} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:00`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function countAt(startMs: number): number {
|
|
321
|
+
return 20 + (Math.floor(startMs / MINUTE_MS) % 10);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// What each host was asked to do, in order.
|
|
325
|
+
const hostCalls: Array<string> = [];
|
|
326
|
+
|
|
327
|
+
// The windows the host was asked to zoom to.
|
|
328
|
+
function zoomCalls(): Array<string> {
|
|
329
|
+
return hostCalls.filter((call: string): boolean => {
|
|
330
|
+
return call.startsWith("select ");
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
interface HostWindow {
|
|
335
|
+
timeRange: RangeStartAndEndDateTime;
|
|
336
|
+
onHistogramTimeRangeSelect: (startTime: Date, endTime: Date) => void;
|
|
337
|
+
onTimeRangeChange: (timeRange: RangeStartAndEndDateTime) => void;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/*
|
|
341
|
+
* The window an explorer's host owns, as TracesViewer and
|
|
342
|
+
* DashboardLogsViewer own theirs: a drag on the histogram becomes a custom
|
|
343
|
+
* window, and any other range (the picker, the way back out of a zoom) is
|
|
344
|
+
* applied as it comes.
|
|
345
|
+
*/
|
|
346
|
+
function useHostWindow(): HostWindow {
|
|
347
|
+
const [timeRange, setTimeRange] =
|
|
348
|
+
useState<RangeStartAndEndDateTime>(PAST_HOUR);
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
timeRange: timeRange,
|
|
352
|
+
onHistogramTimeRangeSelect: (startTime: Date, endTime: Date): void => {
|
|
353
|
+
const zoomed: RangeStartAndEndDateTime = {
|
|
354
|
+
range: TimeRange.CUSTOM,
|
|
355
|
+
startAndEndDate: new InBetween<Date>(startTime, endTime),
|
|
356
|
+
};
|
|
357
|
+
hostCalls.push(`select ${describeRange(zoomed)}`);
|
|
358
|
+
setTimeRange(zoomed);
|
|
359
|
+
},
|
|
360
|
+
onTimeRangeChange: (next: RangeStartAndEndDateTime): void => {
|
|
361
|
+
hostCalls.push(`change ${describeRange(next)}`);
|
|
362
|
+
setTimeRange(next);
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
interface TraceRow {
|
|
368
|
+
id: string;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const TRACE_SERIES: Array<HistogramSeriesOption> = [
|
|
372
|
+
{ key: "ok", label: "Ok", color: "#10b981" },
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
* The traces explorer's host, as TracesViewer is: it asks for the new
|
|
377
|
+
* window's bars on every window, keeps the bars it has while it waits, and
|
|
378
|
+
* drops an answer to a window it has since left.
|
|
379
|
+
*/
|
|
380
|
+
const TracesExplorerHost: FunctionComponent = (): ReactElement => {
|
|
381
|
+
const host: HostWindow = useHostWindow();
|
|
382
|
+
const [buckets, setBuckets] = useState<Array<TelemetryHistogramBucket>>([]);
|
|
383
|
+
const [bucketIntervalMs, setBucketIntervalMs] = useState<number | undefined>(
|
|
384
|
+
undefined,
|
|
385
|
+
);
|
|
386
|
+
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
387
|
+
const latestRequest: React.MutableRefObject<number> = useRef<number>(0);
|
|
388
|
+
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
latestRequest.current += 1;
|
|
391
|
+
const request: number = latestRequest.current;
|
|
392
|
+
setIsLoading(true);
|
|
393
|
+
|
|
394
|
+
askForHistogram(host.timeRange).then((answer: HistogramAnswer): void => {
|
|
395
|
+
if (request !== latestRequest.current) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
setBuckets(
|
|
400
|
+
answer.bucketStartsMs.map(
|
|
401
|
+
(startMs: number): TelemetryHistogramBucket => {
|
|
402
|
+
return {
|
|
403
|
+
time: bucketLabel(startMs),
|
|
404
|
+
series: "ok",
|
|
405
|
+
count: countAt(startMs),
|
|
406
|
+
};
|
|
407
|
+
},
|
|
408
|
+
),
|
|
409
|
+
);
|
|
410
|
+
setBucketIntervalMs(answer.bucketIntervalMs);
|
|
411
|
+
setIsLoading(false);
|
|
412
|
+
});
|
|
413
|
+
}, [host.timeRange]);
|
|
414
|
+
|
|
415
|
+
return (
|
|
416
|
+
<div>
|
|
417
|
+
<span data-testid={HOST_WINDOW_TEST_ID}>
|
|
418
|
+
{describeRange(host.timeRange)}
|
|
419
|
+
</span>
|
|
420
|
+
<TelemetryViewer<TraceRow>
|
|
421
|
+
items={[]}
|
|
422
|
+
isLoading={false}
|
|
423
|
+
renderRow={(row: TraceRow): ReactElement => {
|
|
424
|
+
return <span>{row.id}</span>;
|
|
425
|
+
}}
|
|
426
|
+
getRowKey={(row: TraceRow): string => {
|
|
427
|
+
return row.id;
|
|
428
|
+
}}
|
|
429
|
+
searchValue=""
|
|
430
|
+
onSearchChange={(): void => {}}
|
|
431
|
+
onSearchSubmit={(): void => {}}
|
|
432
|
+
timeRange={host.timeRange}
|
|
433
|
+
onTimeRangeChange={host.onTimeRangeChange}
|
|
434
|
+
page={1}
|
|
435
|
+
pageSize={50}
|
|
436
|
+
totalCount={0}
|
|
437
|
+
onPageChange={(): void => {}}
|
|
438
|
+
onPageSizeChange={(): void => {}}
|
|
439
|
+
showHistogram={true}
|
|
440
|
+
histogramBuckets={buckets}
|
|
441
|
+
histogramSeries={TRACE_SERIES}
|
|
442
|
+
histogramTitle="Traces over time"
|
|
443
|
+
histogramLoading={isLoading}
|
|
444
|
+
histogramBucketIntervalMs={bucketIntervalMs}
|
|
445
|
+
onHistogramTimeRangeSelect={host.onHistogramTimeRangeSelect}
|
|
446
|
+
/>
|
|
447
|
+
</div>
|
|
448
|
+
);
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const NO_LOG_FILTER: LogsViewerProps["filterData"] = {};
|
|
452
|
+
|
|
453
|
+
/*
|
|
454
|
+
* The logs explorer's host, as DashboardLogsViewer is: its bars come
|
|
455
|
+
* through useLogsHistogram, which keeps the bars it has while a new window
|
|
456
|
+
* loads and drops an answer to a window it has since left.
|
|
457
|
+
*/
|
|
458
|
+
const LogsExplorerHost: FunctionComponent = (): ReactElement => {
|
|
459
|
+
const host: HostWindow = useHostWindow();
|
|
460
|
+
const timeRange: RangeStartAndEndDateTime = host.timeRange;
|
|
461
|
+
|
|
462
|
+
const fetchBuckets: () => Promise<LogsHistogramData> =
|
|
463
|
+
useCallback(async (): Promise<LogsHistogramData> => {
|
|
464
|
+
const answer: HistogramAnswer = await askForHistogram(timeRange);
|
|
465
|
+
return {
|
|
466
|
+
buckets: answer.bucketStartsMs.map(
|
|
467
|
+
(startMs: number): LogsHistogramBucket => {
|
|
468
|
+
return {
|
|
469
|
+
time: bucketLabel(startMs),
|
|
470
|
+
severity: LogSeverity.Error,
|
|
471
|
+
count: countAt(startMs),
|
|
472
|
+
};
|
|
473
|
+
},
|
|
474
|
+
),
|
|
475
|
+
bucketIntervalMs: answer.bucketIntervalMs,
|
|
476
|
+
};
|
|
477
|
+
}, [timeRange]);
|
|
478
|
+
|
|
479
|
+
const histogram: LogsHistogramState = useLogsHistogram(fetchBuckets);
|
|
480
|
+
|
|
481
|
+
return (
|
|
482
|
+
<div>
|
|
483
|
+
<span data-testid={HOST_WINDOW_TEST_ID}>{describeRange(timeRange)}</span>
|
|
484
|
+
<LogsViewer
|
|
485
|
+
logs={[]}
|
|
486
|
+
isLoading={false}
|
|
487
|
+
filterData={NO_LOG_FILTER}
|
|
488
|
+
onFilterChanged={(): void => {}}
|
|
489
|
+
histogramBuckets={histogram.buckets}
|
|
490
|
+
histogramLoading={histogram.isLoading}
|
|
491
|
+
histogramBucketIntervalMs={histogram.bucketIntervalMs}
|
|
492
|
+
onHistogramTimeRangeSelect={host.onHistogramTimeRangeSelect}
|
|
493
|
+
timeRange={timeRange}
|
|
494
|
+
onTimeRangeChange={host.onTimeRangeChange}
|
|
495
|
+
/>
|
|
496
|
+
</div>
|
|
497
|
+
);
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
interface ExplorerUnderTest {
|
|
501
|
+
name: string;
|
|
502
|
+
host: FunctionComponent;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const EXPLORERS: Array<ExplorerUnderTest> = [
|
|
506
|
+
{
|
|
507
|
+
name: "traces explorer (TelemetryViewer)",
|
|
508
|
+
host: TracesExplorerHost,
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "logs explorer (LogsViewer)",
|
|
512
|
+
host: LogsExplorerHost,
|
|
513
|
+
},
|
|
514
|
+
];
|
|
515
|
+
|
|
516
|
+
function hostWindow(): string {
|
|
517
|
+
return screen.getByTestId(HOST_WINDOW_TEST_ID).textContent || "";
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function chartWrapper(): Element {
|
|
521
|
+
const wrapper: Element | null = document.querySelector(".recharts-wrapper");
|
|
522
|
+
if (!wrapper) {
|
|
523
|
+
throw new Error("the explorer drew no chart");
|
|
524
|
+
}
|
|
525
|
+
return wrapper;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function plotSurface(): Element {
|
|
529
|
+
const surface: Element | null = chartWrapper().querySelector(
|
|
530
|
+
"svg.recharts-surface",
|
|
531
|
+
);
|
|
532
|
+
if (!surface) {
|
|
533
|
+
throw new Error("the chart drew no plot surface");
|
|
534
|
+
}
|
|
535
|
+
return surface;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// The bars on the chart, by their tick labels: "11:15 AM" (or "11:15") -> "11:15".
|
|
539
|
+
function barLabels(): Array<string> {
|
|
540
|
+
return Array.from(chartWrapper().querySelectorAll(TICK_LABEL_SELECTOR)).map(
|
|
541
|
+
(element: Element): string => {
|
|
542
|
+
const text: string = element.textContent || "";
|
|
543
|
+
const time: RegExpMatchArray | null = text.match(/^\d{1,2}:\d{2}/);
|
|
544
|
+
return time ? time[0] : text;
|
|
545
|
+
},
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// The pointer x that lands on a bar: the centre of its tick label.
|
|
550
|
+
function tickX(label: string): number {
|
|
551
|
+
const tick: Element | undefined = Array.from(
|
|
552
|
+
chartWrapper().querySelectorAll(TICK_LABEL_SELECTOR),
|
|
553
|
+
).find((element: Element): boolean => {
|
|
554
|
+
return (element.textContent || "").startsWith(label);
|
|
555
|
+
});
|
|
556
|
+
if (!tick) {
|
|
557
|
+
throw new Error(`the chart drew no tick label "${label}"`);
|
|
558
|
+
}
|
|
559
|
+
return Number(tick.getAttribute("x"));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function drawnBand(): Element | null {
|
|
563
|
+
return chartWrapper().querySelector(".recharts-reference-area-rect");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function covers(element: Element, x: number, y: number): boolean {
|
|
567
|
+
const left: number = Number(element.getAttribute("x"));
|
|
568
|
+
const top: number = Number(element.getAttribute("y"));
|
|
569
|
+
const width: number = Number(element.getAttribute("width"));
|
|
570
|
+
const height: number = Number(element.getAttribute("height"));
|
|
571
|
+
return x >= left && x <= left + width && y >= top && y <= top + height;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/*
|
|
575
|
+
* The node under the pointer, as the browser hit-tests it: a band a click
|
|
576
|
+
* painted lies over the bars, and the bars over the plot.
|
|
577
|
+
*/
|
|
578
|
+
function nodeAt(x: number): Element {
|
|
579
|
+
const band: Element | null = drawnBand();
|
|
580
|
+
if (band && covers(band, x, PLOT_Y)) {
|
|
581
|
+
return band;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const bar: Element | undefined = Array.from(
|
|
585
|
+
chartWrapper().querySelectorAll(".recharts-bar-rectangle path"),
|
|
586
|
+
).find((element: Element): boolean => {
|
|
587
|
+
return covers(element, x, PLOT_Y);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
return bar || plotSurface();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function commonAncestor(first: Element, second: Element): Element {
|
|
594
|
+
let node: Element | null = first;
|
|
595
|
+
while (node && !node.contains(second)) {
|
|
596
|
+
node = node.parentElement;
|
|
597
|
+
}
|
|
598
|
+
return node || document.body;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function pointerAt(
|
|
602
|
+
x: number,
|
|
603
|
+
init: Record<string, number>,
|
|
604
|
+
): Record<string, number> {
|
|
605
|
+
return { clientX: x, clientY: PLOT_Y, ...init };
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function nextFrame(): void {
|
|
609
|
+
act(() => {
|
|
610
|
+
jest.advanceTimersByTime(FRAME_MS);
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Lets the page's next task run: fake timers run what was set for 0 ms.
|
|
615
|
+
function nextTask(): void {
|
|
616
|
+
act(() => {
|
|
617
|
+
jest.advanceTimersByTime(0);
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Long past the wait a single click on a zoomed chart makes before zooming in.
|
|
622
|
+
function outlastDoubleClickWindow(): void {
|
|
623
|
+
act(() => {
|
|
624
|
+
jest.advanceTimersByTime(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// The pointer comes to rest at x.
|
|
629
|
+
function restAt(x: number): void {
|
|
630
|
+
fireEvent.mouseMove(nodeAt(x), pointerAt(x, { buttons: 0 }));
|
|
631
|
+
nextFrame();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// The pointer moves to x with the button held down.
|
|
635
|
+
function moveHeldTo(x: number): void {
|
|
636
|
+
fireEvent.mouseMove(nodeAt(x), pointerAt(x, { buttons: 1 }));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/*
|
|
640
|
+
* A press at x, `detail` being the browser's click count (2 for the second
|
|
641
|
+
* press of a double-click). Returns the node the press landed on.
|
|
642
|
+
*/
|
|
643
|
+
function pressAt(x: number, detail: number): Element {
|
|
644
|
+
const node: Element = nodeAt(x);
|
|
645
|
+
fireEvent.mouseDown(
|
|
646
|
+
node,
|
|
647
|
+
pointerAt(x, { button: 0, buttons: 1, detail: detail }),
|
|
648
|
+
);
|
|
649
|
+
return node;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function releaseAt(x: number, detail: number): Element {
|
|
653
|
+
const node: Element = nodeAt(x);
|
|
654
|
+
fireEvent.mouseUp(
|
|
655
|
+
node,
|
|
656
|
+
pointerAt(x, { button: 0, buttons: 0, detail: detail }),
|
|
657
|
+
);
|
|
658
|
+
return node;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/*
|
|
662
|
+
* The click the browser sends after a release, to the nearest node holding
|
|
663
|
+
* both the node the press landed on and the one the release did. Returns
|
|
664
|
+
* where it went.
|
|
665
|
+
*/
|
|
666
|
+
function clickAfter(
|
|
667
|
+
pressed: Element,
|
|
668
|
+
released: Element,
|
|
669
|
+
x: number,
|
|
670
|
+
detail: number,
|
|
671
|
+
): Element {
|
|
672
|
+
const target: Element = commonAncestor(pressed, released);
|
|
673
|
+
fireEvent.click(target, pointerAt(x, { button: 0, detail: detail }));
|
|
674
|
+
return target;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// A click at x that nothing disturbs.
|
|
678
|
+
function clickAt(x: number, detail: number): Element {
|
|
679
|
+
const pressed: Element = pressAt(x, detail);
|
|
680
|
+
return clickAfter(pressed, releaseAt(x, detail), x, detail);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// A second click at x that nothing disturbs, and the dblclick that follows it.
|
|
684
|
+
function secondClickAndDoubleClickAt(x: number): void {
|
|
685
|
+
const clicked: Element = clickAt(x, 2);
|
|
686
|
+
fireEvent.doubleClick(clicked, pointerAt(x, { button: 0, detail: 2 }));
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// A drag from one bar to another, let go over the chart.
|
|
690
|
+
function dragAcross(fromX: number, toX: number): void {
|
|
691
|
+
restAt(fromX);
|
|
692
|
+
pressAt(fromX, 1);
|
|
693
|
+
nextFrame();
|
|
694
|
+
moveHeldTo(toX);
|
|
695
|
+
nextFrame();
|
|
696
|
+
releaseAt(toX, 1);
|
|
697
|
+
fireEvent.click(plotSurface(), pointerAt(toX, { button: 0, detail: 1 }));
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function openExplorer(explorer: ExplorerUnderTest): Promise<void> {
|
|
701
|
+
const Host: FunctionComponent = explorer.host;
|
|
702
|
+
|
|
703
|
+
await act(async () => {
|
|
704
|
+
render(<Host />);
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
expect(heldWindows()).toEqual([TimeRange.PAST_ONE_HOUR]);
|
|
708
|
+
await deliverHeldAnswer();
|
|
709
|
+
expect(barLabels()).toEqual(HOUR_BARS);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/*
|
|
713
|
+
* Opens the explorer on the hour and drags across two of its bars. The
|
|
714
|
+
* host moves to the ten minutes the drag covered and asks for their bars,
|
|
715
|
+
* which the test holds back: the chart keeps drawing the hour's bars, and
|
|
716
|
+
* offers the double-click that undoes the zoom. Returns where the drag let
|
|
717
|
+
* go, which is where the double-click lands.
|
|
718
|
+
*/
|
|
719
|
+
async function openAndZoomByDragging(
|
|
720
|
+
explorer: ExplorerUnderTest,
|
|
721
|
+
): Promise<number> {
|
|
722
|
+
await openExplorer(explorer);
|
|
723
|
+
|
|
724
|
+
const spot: number = tickX(DRAG_TO);
|
|
725
|
+
dragAcross(tickX(DRAG_FROM), spot);
|
|
726
|
+
|
|
727
|
+
expect(hostCalls).toEqual([ZOOM_CALL]);
|
|
728
|
+
expect(hostWindow()).toBe(ZOOMED_WINDOW);
|
|
729
|
+
expect(requestedWindows()).toEqual([TimeRange.PAST_ONE_HOUR, ZOOMED_WINDOW]);
|
|
730
|
+
expect(heldWindows()).toEqual([ZOOMED_WINDOW]);
|
|
731
|
+
expect(barLabels()).toEqual(HOUR_BARS);
|
|
732
|
+
expect(screen.getByText(RESET_HINT)).toBeInTheDocument();
|
|
733
|
+
|
|
734
|
+
return spot;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// The explorer is back on the hour, by the one way back, and asked for it again.
|
|
738
|
+
function expectBackOnTheHourOnce(): void {
|
|
739
|
+
expect(hostWindow()).toBe(TimeRange.PAST_ONE_HOUR);
|
|
740
|
+
expect(hostCalls).toEqual([ZOOM_CALL, RESET_CALL]);
|
|
741
|
+
expect(requestedWindows()).toEqual([
|
|
742
|
+
TimeRange.PAST_ONE_HOUR,
|
|
743
|
+
ZOOMED_WINDOW,
|
|
744
|
+
TimeRange.PAST_ONE_HOUR,
|
|
745
|
+
]);
|
|
746
|
+
expect(screen.queryByText(RESET_HINT)).toBeNull();
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
beforeEach(() => {
|
|
750
|
+
/*
|
|
751
|
+
* Installed AT the fixed time, not moved there with setSystemTime: fake
|
|
752
|
+
* timers count animation frames from the moment they are installed.
|
|
753
|
+
*/
|
|
754
|
+
jest.useFakeTimers({ now: NOW });
|
|
755
|
+
histogramRequests.splice(0, histogramRequests.length);
|
|
756
|
+
hostCalls.splice(0, hostCalls.length);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
afterEach(() => {
|
|
760
|
+
cleanup();
|
|
761
|
+
jest.useRealTimers();
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
for (const explorer of EXPLORERS) {
|
|
765
|
+
describe(`the ${explorer.name}: a double-click right after a drag-zoom, while the zoomed bars are on their way`, () => {
|
|
766
|
+
test("the drag zooms the host to the bars it covered, and the chart keeps the hour's bars while that answer is held back", async () => {
|
|
767
|
+
await openAndZoomByDragging(explorer);
|
|
768
|
+
|
|
769
|
+
// Nothing else happens while the answer is out.
|
|
770
|
+
outlastDoubleClickWindow();
|
|
771
|
+
expect(hostCalls).toEqual([ZOOM_CALL]);
|
|
772
|
+
expect(barLabels()).toEqual(HOUR_BARS);
|
|
773
|
+
|
|
774
|
+
await deliverHeldAnswer();
|
|
775
|
+
expect(barLabels()).toEqual(ZOOMED_BARS);
|
|
776
|
+
expect(hostWindow()).toBe(ZOOMED_WINDOW);
|
|
777
|
+
expect(screen.getByText(RESET_HINT)).toBeInTheDocument();
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
// (i) Failed before the fix: a zoom to 11:15..11:18, and still zoomed.
|
|
781
|
+
test("the answer lands during the second press, which then gets no click and no dblclick: the next task puts the host back on the hour, and nothing zooms in", async () => {
|
|
782
|
+
const spot: number = await openAndZoomByDragging(explorer);
|
|
783
|
+
|
|
784
|
+
restAt(spot);
|
|
785
|
+
clickAt(spot, 1);
|
|
786
|
+
|
|
787
|
+
/*
|
|
788
|
+
* On a zoomed chart the first click paints its bar while it waits out
|
|
789
|
+
* the double-click window, so the second press lands on that band.
|
|
790
|
+
*/
|
|
791
|
+
const band: Element | null = drawnBand();
|
|
792
|
+
expect(band).not.toBeNull();
|
|
793
|
+
const secondPress: Element = pressAt(spot, 2);
|
|
794
|
+
expect(secondPress).toBe(band);
|
|
795
|
+
|
|
796
|
+
await deliverHeldAnswer();
|
|
797
|
+
|
|
798
|
+
/*
|
|
799
|
+
* The zoomed minutes are drawn, and the band the press landed on is
|
|
800
|
+
* gone with the hour's bars: Chrome sends this press no click and no
|
|
801
|
+
* dblclick, and none is fired.
|
|
802
|
+
*/
|
|
803
|
+
expect(barLabels()).toEqual(ZOOMED_BARS);
|
|
804
|
+
expect(secondPress.isConnected).toBe(false);
|
|
805
|
+
releaseAt(spot, 2);
|
|
806
|
+
expect(zoomCalls()).toEqual([ZOOM_CALL]);
|
|
807
|
+
|
|
808
|
+
nextTask();
|
|
809
|
+
|
|
810
|
+
expectBackOnTheHourOnce();
|
|
811
|
+
|
|
812
|
+
// Nor does the first click's zoom into 11:15 come later.
|
|
813
|
+
outlastDoubleClickWindow();
|
|
814
|
+
expect(hostCalls).toEqual([ZOOM_CALL, RESET_CALL]);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// (ii) Failed before the fix: a zoom to 11:15..11:18 before the zoom-out.
|
|
818
|
+
test("the answer lands during the first press: that release zooms nowhere, and the dblclick puts the host back on the hour once", async () => {
|
|
819
|
+
const spot: number = await openAndZoomByDragging(explorer);
|
|
820
|
+
|
|
821
|
+
restAt(spot);
|
|
822
|
+
const firstPress: Element = pressAt(spot, 1);
|
|
823
|
+
expect(firstPress.closest(".recharts-bar-rectangle")).not.toBeNull();
|
|
824
|
+
|
|
825
|
+
await deliverHeldAnswer();
|
|
826
|
+
|
|
827
|
+
/*
|
|
828
|
+
* The bar the press landed on is gone: Chrome sends this press no
|
|
829
|
+
* click. Its release is over a bar of the new window, at a pointer
|
|
830
|
+
* that never moved.
|
|
831
|
+
*/
|
|
832
|
+
expect(barLabels()).toEqual(ZOOMED_BARS);
|
|
833
|
+
expect(firstPress.isConnected).toBe(false);
|
|
834
|
+
releaseAt(spot, 1);
|
|
835
|
+
expect(hostCalls).toEqual([ZOOM_CALL]);
|
|
836
|
+
|
|
837
|
+
secondClickAndDoubleClickAt(spot);
|
|
838
|
+
outlastDoubleClickWindow();
|
|
839
|
+
|
|
840
|
+
expectBackOnTheHourOnce();
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
// (ii) Failed before the fix: the drift read as a drag to 11:12..11:17.
|
|
844
|
+
test("the answer lands during the first press while the hand drifts a couple of pixels: still no zoom, and the dblclick puts the host back on the hour once", async () => {
|
|
845
|
+
const spot: number = await openAndZoomByDragging(explorer);
|
|
846
|
+
|
|
847
|
+
restAt(spot);
|
|
848
|
+
pressAt(spot, 1);
|
|
849
|
+
await deliverHeldAnswer();
|
|
850
|
+
expect(barLabels()).toEqual(ZOOMED_BARS);
|
|
851
|
+
|
|
852
|
+
/*
|
|
853
|
+
* A hand on a mouse is never quite still. The pointer drifts two
|
|
854
|
+
* pixels, and recharts reads a bar of the new window under it: that
|
|
855
|
+
* is not a drag, and paints nothing.
|
|
856
|
+
*/
|
|
857
|
+
const drifted: number = spot + 2;
|
|
858
|
+
moveHeldTo(drifted);
|
|
859
|
+
expect(drawnBand()).toBeNull();
|
|
860
|
+
releaseAt(drifted, 1);
|
|
861
|
+
expect(hostCalls).toEqual([ZOOM_CALL]);
|
|
862
|
+
|
|
863
|
+
secondClickAndDoubleClickAt(drifted);
|
|
864
|
+
outlastDoubleClickWindow();
|
|
865
|
+
|
|
866
|
+
expectBackOnTheHourOnce();
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
// Failed before the fix: a zoom to 11:15..11:18 at the release.
|
|
870
|
+
test("the answer lands during the first press and no second click follows: that click zooms nowhere, and the explorer stays zoomed", async () => {
|
|
871
|
+
const spot: number = await openAndZoomByDragging(explorer);
|
|
872
|
+
|
|
873
|
+
restAt(spot);
|
|
874
|
+
const pressed: Element = pressAt(spot, 1);
|
|
875
|
+
await deliverHeldAnswer();
|
|
876
|
+
expect(pressed.isConnected).toBe(false);
|
|
877
|
+
releaseAt(spot, 1);
|
|
878
|
+
|
|
879
|
+
/*
|
|
880
|
+
* The bar it pressed is no longer on the chart, so there is nothing
|
|
881
|
+
* to zoom into, then or once the double-click window has passed.
|
|
882
|
+
*/
|
|
883
|
+
outlastDoubleClickWindow();
|
|
884
|
+
expect(hostCalls).toEqual([ZOOM_CALL]);
|
|
885
|
+
expect(hostWindow()).toBe(ZOOMED_WINDOW);
|
|
886
|
+
expect(drawnBand()).toBeNull();
|
|
887
|
+
expect(screen.getByText(RESET_HINT)).toBeInTheDocument();
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
// (iv) As before the fix; pinned so the new zoom-out path keeps it.
|
|
891
|
+
test("the answer lands between the two clicks: the dblclick puts the host back on the hour once, and the first click never zooms in", async () => {
|
|
892
|
+
const spot: number = await openAndZoomByDragging(explorer);
|
|
893
|
+
|
|
894
|
+
restAt(spot);
|
|
895
|
+
clickAt(spot, 1);
|
|
896
|
+
expect(drawnBand()).not.toBeNull();
|
|
897
|
+
|
|
898
|
+
await deliverHeldAnswer();
|
|
899
|
+
expect(barLabels()).toEqual(ZOOMED_BARS);
|
|
900
|
+
|
|
901
|
+
secondClickAndDoubleClickAt(spot);
|
|
902
|
+
outlastDoubleClickWindow();
|
|
903
|
+
|
|
904
|
+
expectBackOnTheHourOnce();
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
// (iii) As before the fix; pinned so the new zoom-out path keeps it.
|
|
908
|
+
test("the answer lands before the double-click: the dblclick puts the host back on the hour once", async () => {
|
|
909
|
+
const spot: number = await openAndZoomByDragging(explorer);
|
|
910
|
+
|
|
911
|
+
await deliverHeldAnswer();
|
|
912
|
+
expect(barLabels()).toEqual(ZOOMED_BARS);
|
|
913
|
+
|
|
914
|
+
restAt(spot);
|
|
915
|
+
clickAt(spot, 1);
|
|
916
|
+
secondClickAndDoubleClickAt(spot);
|
|
917
|
+
|
|
918
|
+
/*
|
|
919
|
+
* The dblclick zoomed out at once. The reset the second press armed
|
|
920
|
+
* for the next task stood down, and neither click zooms in later.
|
|
921
|
+
*/
|
|
922
|
+
expectBackOnTheHourOnce();
|
|
923
|
+
nextTask();
|
|
924
|
+
outlastDoubleClickWindow();
|
|
925
|
+
expect(hostCalls).toEqual([ZOOM_CALL, RESET_CALL]);
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
}
|