@oneuptime/common 14.0.8 → 14.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (639) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  4. package/Models/DatabaseModels/Project.ts +55 -5
  5. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  6. package/Models/DatabaseModels/RunnerJob.ts +75 -0
  7. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  8. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  9. package/Server/API/AIChatAPI.ts +6 -3
  10. package/Server/API/AIInvestigationAPI.ts +17 -10
  11. package/Server/API/AutoRemediationAPI.ts +207 -25
  12. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  13. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  14. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  15. package/Server/Infrastructure/GlobalCache.ts +32 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/TelemetryIngest.ts +41 -18
  21. package/Server/Services/AIService.ts +70 -1
  22. package/Server/Services/AlertService.ts +352 -11
  23. package/Server/Services/AlertSeverityService.ts +65 -15
  24. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  25. package/Server/Services/IncidentService.ts +280 -104
  26. package/Server/Services/IncidentSeverityService.ts +66 -15
  27. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  28. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  29. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  30. package/Server/Services/KubernetesClusterService.ts +129 -21
  31. package/Server/Services/ProjectService.ts +32 -0
  32. package/Server/Services/ProxmoxResourceService.ts +322 -5
  33. package/Server/Services/RunnerJobService.ts +241 -104
  34. package/Server/Services/RunnerService.ts +1 -1
  35. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  36. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  37. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  38. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  39. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  40. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  41. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
  42. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
  43. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
  44. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  45. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  46. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  47. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
  48. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  49. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  50. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  51. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  52. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  53. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
  54. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  55. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  56. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  57. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  58. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  59. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  60. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  61. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  62. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  63. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  64. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  65. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  66. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  67. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  68. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  69. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  70. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  71. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  72. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
  73. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  74. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  75. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  76. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  77. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  78. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  79. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  80. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  81. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  82. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  83. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  84. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  85. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  86. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  87. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  88. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  89. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  90. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  91. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  92. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  93. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  94. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  95. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  96. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  97. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  98. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  99. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  100. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  101. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  102. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  103. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  104. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  105. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  106. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  107. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  108. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  109. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  110. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  111. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  112. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  113. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  114. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  115. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  116. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  117. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  118. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  119. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  120. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  121. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  122. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  123. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  124. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  125. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  126. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  127. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  128. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  129. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  130. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  131. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  132. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  133. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  134. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  135. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  136. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  137. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  138. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  139. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  140. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  141. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  142. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  143. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  144. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  145. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  146. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  147. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  148. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  149. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  150. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  151. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  152. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  153. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  154. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  155. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  156. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  157. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  158. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  159. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  160. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  161. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  162. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  163. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  164. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  165. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  166. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  167. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  168. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  169. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  170. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  171. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  172. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  173. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  174. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  175. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  176. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  177. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  178. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  179. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  180. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  181. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  182. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  183. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  184. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  185. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  186. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  187. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  188. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  189. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  190. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  191. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  192. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  193. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  194. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  195. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  196. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  197. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  198. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  199. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  200. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  201. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  202. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  203. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  204. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  205. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  206. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  207. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  208. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  209. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  210. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  211. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  212. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  213. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  214. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  215. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  216. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  217. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  218. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  219. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  220. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  221. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  222. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  223. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  224. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  225. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  226. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  227. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
  228. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
  229. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
  230. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  231. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  232. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  233. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  234. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  235. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  236. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  237. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  238. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  239. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  240. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  241. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  242. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  243. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  244. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  245. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  246. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  247. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  248. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  249. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  250. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  251. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  252. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  253. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
  254. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  255. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  256. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  257. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  258. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  259. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  260. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  261. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  262. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  263. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  264. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  265. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  266. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  267. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  268. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  269. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  270. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  271. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  272. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  273. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  274. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  275. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  276. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  277. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  278. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  279. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  280. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  281. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  282. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  283. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  284. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  285. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  286. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
  287. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  288. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  289. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  290. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  291. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  292. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  293. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  294. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  295. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  296. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  297. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  298. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  299. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  300. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  301. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  302. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  303. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  304. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  305. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  306. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  307. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  308. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  309. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  310. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  311. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  312. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  313. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  314. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  315. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  316. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  317. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  318. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  319. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  320. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  321. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  322. package/Tests/UI/Components/Input.test.tsx +80 -1
  323. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  324. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  325. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  326. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  327. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  328. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  329. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  330. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  331. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  332. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  333. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  334. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  335. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  336. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  337. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  338. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  339. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  340. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  341. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  342. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  343. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  344. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  345. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  346. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  347. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  348. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  349. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  350. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  351. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  352. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  353. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  354. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  355. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  356. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  357. package/Types/AI/AIRunType.ts +9 -4
  358. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  359. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
  360. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  361. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  362. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  363. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  364. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  365. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  366. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  367. package/Types/Team/ComplianceRule.ts +434 -0
  368. package/Types/Team/ComplianceRuleType.ts +21 -0
  369. package/Types/Team/TeamComplianceStatus.ts +81 -0
  370. package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
  371. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  372. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  373. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  374. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  375. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  376. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  377. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  378. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  379. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  380. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  381. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  382. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  383. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  384. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  385. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  386. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  387. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  388. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  389. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  390. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  391. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  392. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  393. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  394. package/UI/Components/Input/Input.tsx +90 -57
  395. package/UI/Components/List/ListRow.tsx +8 -74
  396. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  397. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  398. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  399. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  400. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  401. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  402. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  403. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  404. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  405. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  406. package/UI/Components/Table/TableBody.tsx +8 -2
  407. package/UI/Components/Table/TableRow.tsx +20 -140
  408. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  409. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  410. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  411. package/UI/Components/TelemetryViewer/types.ts +2 -0
  412. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  413. package/UI/Components/Toggle/Toggle.tsx +26 -0
  414. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  415. package/UI/Styles/Theme.css +14 -2
  416. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  417. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  418. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  419. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  420. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  421. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  422. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  423. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  424. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  425. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  426. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  427. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  428. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  429. package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
  430. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  431. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  432. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  433. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  434. package/build/dist/Server/API/AIChatAPI.js +6 -1
  435. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  436. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  437. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  438. package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
  439. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  440. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  441. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  442. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  443. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  444. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  445. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  446. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  447. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  448. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  449. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  450. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  451. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  452. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  453. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  454. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  455. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  456. package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
  457. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  458. package/build/dist/Server/Services/AIService.js +59 -1
  459. package/build/dist/Server/Services/AIService.js.map +1 -1
  460. package/build/dist/Server/Services/AlertService.js +248 -13
  461. package/build/dist/Server/Services/AlertService.js.map +1 -1
  462. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  463. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  464. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  465. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  466. package/build/dist/Server/Services/IncidentService.js +199 -72
  467. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  468. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  469. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  470. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  471. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  472. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  473. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  474. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  475. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  476. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  477. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  478. package/build/dist/Server/Services/ProjectService.js +22 -0
  479. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  480. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  481. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  482. package/build/dist/Server/Services/RunnerJobService.js +178 -92
  483. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  484. package/build/dist/Server/Services/RunnerService.js +1 -1
  485. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  486. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  487. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  488. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  489. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  490. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  491. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  492. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  493. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  494. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  495. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  496. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  497. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  498. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
  499. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  500. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
  501. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  502. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
  503. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  504. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  505. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  506. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  507. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  508. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  509. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
  510. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  512. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  513. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  514. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  515. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  516. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  517. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  518. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  519. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  520. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  521. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
  522. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  523. package/build/dist/Types/AI/AIRunType.js +9 -4
  524. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  525. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
  526. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  527. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  528. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  529. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  530. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  531. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  532. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  533. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  534. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  535. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  536. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  537. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  538. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  539. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  540. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  541. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  542. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  543. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  544. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  545. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
  546. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  547. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  548. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  549. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  550. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  551. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  552. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  553. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  554. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  555. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  556. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  557. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  558. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  559. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  560. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  561. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  562. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  563. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  564. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  565. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  566. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  567. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  568. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  569. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  570. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  571. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  572. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  573. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  574. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  575. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  576. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  577. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  578. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  579. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  580. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  581. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  582. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  583. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  584. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  585. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  586. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  587. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  588. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  589. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  590. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  591. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  592. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  593. package/build/dist/UI/Components/Input/Input.js +69 -54
  594. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  595. package/build/dist/UI/Components/List/ListRow.js +4 -39
  596. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  597. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  598. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  600. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  601. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  602. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  603. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  604. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  605. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  606. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  607. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  608. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  609. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  610. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  611. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  612. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  613. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  614. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  615. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  616. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  617. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  618. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  619. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  620. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  621. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  622. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  626. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  627. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  628. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  629. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  630. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  631. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  632. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  633. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  634. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  635. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  636. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  637. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  638. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  639. package/package.json +1 -1
@@ -0,0 +1,967 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ render,
14
+ RenderResult,
15
+ screen,
16
+ } from "@testing-library/react";
17
+ import * as React from "react";
18
+ import { MemoryRouter } from "react-router-dom";
19
+ import getJestMockFunction, { MockFunction } from "../../MockType";
20
+
21
+ /*
22
+ * OpenTelemetry's Unset span status (0) is the default: instrumentation sets
23
+ * Error when an operation fails and leaves successful spans Unset. The trace
24
+ * rows and the span panel painted Unset grey, and the status dot in the span
25
+ * tables gave Unset and Ok the same brand green, so a healthy service read
26
+ * as "mostly unknown" (#4118).
27
+ *
28
+ * This renders the REAL TraceRow, SpanDetailsPanel and SpanStatusElement and
29
+ * asserts what a user sees and a screen reader announces for each status: 0,
30
+ * 1, 2, a missing status and a code the UI does not know. The last two read
31
+ * as Unset, the rule the server uses when it buckets the Traces chart.
32
+ *
33
+ * "Unset (no error)" speaks about the status field, and recording an
34
+ * exception does not change a span's status. So where exceptions are in
35
+ * view the status is named alone, "Unset": in the span panel of a span that
36
+ * carries exception events, and in SpanStatusElement's plainLabel, which the
37
+ * exception tables use because each of their rows is an exception.
38
+ */
39
+
40
+ const analyticsGetListMock: MockFunction = getJestMockFunction();
41
+ const apiPostMock: MockFunction = getJestMockFunction();
42
+ const getCurrentProjectIdMock: MockFunction = getJestMockFunction();
43
+
44
+ /*
45
+ * The arrow wrappers are load bearing: jest.mock is hoisted above the
46
+ * compiled requires, so the mock variables above are still unassigned when
47
+ * the factories run.
48
+ */
49
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
50
+ return {
51
+ __esModule: true,
52
+ default: {
53
+ getList: async () => {
54
+ return { data: [], count: 0 };
55
+ },
56
+ getCommonHeaders: () => {
57
+ return {};
58
+ },
59
+ },
60
+ };
61
+ });
62
+
63
+ jest.mock("../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI", () => {
64
+ return {
65
+ __esModule: true,
66
+ default: {
67
+ getList: (...args: Array<any>) => {
68
+ return analyticsGetListMock(...args);
69
+ },
70
+ },
71
+ };
72
+ });
73
+
74
+ jest.mock("../../../UI/Utils/API/API", () => {
75
+ return {
76
+ __esModule: true,
77
+ default: {
78
+ post: (...args: Array<any>) => {
79
+ return apiPostMock(...args);
80
+ },
81
+ getFriendlyMessage: () => {
82
+ return "error";
83
+ },
84
+ },
85
+ };
86
+ });
87
+
88
+ jest.mock("../../../UI/Utils/Project", () => {
89
+ return {
90
+ __esModule: true,
91
+ default: {
92
+ getCurrentProjectId: (...args: Array<any>) => {
93
+ return getCurrentProjectIdMock(...args);
94
+ },
95
+ },
96
+ };
97
+ });
98
+
99
+ import TraceRow from "../../../../App/FeatureSet/Dashboard/src/Components/Traces/TraceRow";
100
+ import SpanDetailsPanel from "../../../../App/FeatureSet/Dashboard/src/Components/Traces/SpanDetailsPanel";
101
+ import SpanStatusElement, {
102
+ ComponentProps as SpanStatusElementProps,
103
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Span/SpanStatusElement";
104
+ import Span, {
105
+ SpanEvent,
106
+ SpanStatus,
107
+ } from "../../../Models/AnalyticsModels/Span";
108
+ import ObjectID from "../../../Types/ObjectID";
109
+
110
+ const PROJECT_ID: string = "11111111-1111-4111-8111-111111111111";
111
+ const ENTITY_ID: string = "22222222-2222-4222-8222-222222222222";
112
+ const TRACE_ID: string = "4bf92f3577b34da6a3ce929d0e0e4736";
113
+ const SPAN_ID: string = "a1b2c3d4e5f60718";
114
+ const MAX_DURATION_NANO: number = 4_000_000;
115
+
116
+ /*
117
+ * What each status must look like, spelled out here rather than read back
118
+ * from SpanStatusPresentation, so the tests pin what the user sees. The
119
+ * rgb() values are how jsdom reports the inline hex colors (#10b981,
120
+ * #0891b2, #ef4444).
121
+ */
122
+ interface ExpectedStatus {
123
+ label: string;
124
+ displayLabel: string;
125
+ description: string;
126
+ rgb: string;
127
+ dotClassName: string;
128
+ ringClassName: string;
129
+ barClassName: string;
130
+ barTrackClassName: string;
131
+ pillClassNames: Array<string>;
132
+ }
133
+
134
+ const UNSET: ExpectedStatus = {
135
+ label: "Unset",
136
+ displayLabel: "Unset (no error)",
137
+ description:
138
+ "No error status was set. Unset is the OpenTelemetry default for spans that finish without one.",
139
+ rgb: "rgb(16, 185, 129)",
140
+ dotClassName: "bg-emerald-500",
141
+ ringClassName: "ring-emerald-200",
142
+ barClassName: "bg-emerald-500",
143
+ barTrackClassName: "bg-gray-100",
144
+ pillClassNames: ["bg-emerald-50", "text-emerald-700"],
145
+ };
146
+
147
+ /*
148
+ * Cyan rather than a darker green: #047857 sat next to Error's red for
149
+ * protanopes, and Ok meets Error in the Traces chart wherever a bucket has
150
+ * no Unset spans.
151
+ */
152
+ const OK: ExpectedStatus = {
153
+ label: "Ok",
154
+ displayLabel: "Ok",
155
+ description:
156
+ "Explicitly marked successful by the application or a trace pipeline.",
157
+ rgb: "rgb(8, 145, 178)",
158
+ dotClassName: "bg-cyan-600",
159
+ ringClassName: "ring-cyan-200",
160
+ barClassName: "bg-cyan-600",
161
+ barTrackClassName: "bg-gray-100",
162
+ pillClassNames: ["bg-cyan-50", "text-cyan-800"],
163
+ };
164
+
165
+ const ERROR: ExpectedStatus = {
166
+ label: "Error",
167
+ displayLabel: "Error",
168
+ description: "The operation failed: the span's status is Error.",
169
+ rgb: "rgb(239, 68, 68)",
170
+ dotClassName: "bg-red-500",
171
+ ringClassName: "ring-red-100",
172
+ barClassName: "bg-red-500",
173
+ barTrackClassName: "bg-red-50",
174
+ pillClassNames: ["bg-red-50", "text-red-700"],
175
+ };
176
+
177
+ interface StatusCase {
178
+ name: string;
179
+ statusCode: SpanStatus | number | undefined;
180
+ expected: ExpectedStatus;
181
+ }
182
+
183
+ const STATUS_CASES: Array<StatusCase> = [
184
+ { name: "Unset (0)", statusCode: SpanStatus.Unset, expected: UNSET },
185
+ { name: "Ok (1)", statusCode: SpanStatus.Ok, expected: OK },
186
+ { name: "Error (2)", statusCode: SpanStatus.Error, expected: ERROR },
187
+ { name: "a missing status", statusCode: undefined, expected: UNSET },
188
+ { name: "an unknown code (5)", statusCode: 5, expected: UNSET },
189
+ ];
190
+
191
+ const NON_ERROR_CASES: Array<StatusCase> = STATUS_CASES.filter(
192
+ (statusCase: StatusCase): boolean => {
193
+ return statusCase.expected !== ERROR;
194
+ },
195
+ );
196
+
197
+ type BuildSpanFunction = (
198
+ statusCode: SpanStatus | number | undefined,
199
+ statusMessage?: string | undefined,
200
+ ) => Span;
201
+
202
+ const buildSpan: BuildSpanFunction = (
203
+ statusCode: SpanStatus | number | undefined,
204
+ statusMessage?: string | undefined,
205
+ ): Span => {
206
+ const span: Span = new Span();
207
+ span.name = "GET /api/orders/:id";
208
+ span.traceId = TRACE_ID;
209
+ span.spanId = SPAN_ID;
210
+ span.primaryEntityId = new ObjectID(ENTITY_ID);
211
+ span.startTime = new Date("2026-09-28T10:00:00.000Z");
212
+ span.durationUnixNano = MAX_DURATION_NANO / 2;
213
+ if (statusCode !== undefined) {
214
+ span.statusCode = statusCode as SpanStatus;
215
+ }
216
+ if (statusMessage !== undefined) {
217
+ span.statusMessage = statusMessage;
218
+ }
219
+ return span;
220
+ };
221
+
222
+ const GREY_CLASS: RegExp = /^(bg|ring|text)-gray-/;
223
+
224
+ // Every grey background, ring or text class on an element and its subtree.
225
+ const greyClassTokens: (root: Element) => Array<string> = (
226
+ root: Element,
227
+ ): Array<string> => {
228
+ const tokens: Array<string> = [];
229
+ for (const element of [root, ...Array.from(root.querySelectorAll("*"))]) {
230
+ for (const token of Array.from(element.classList)) {
231
+ if (GREY_CLASS.test(token)) {
232
+ tokens.push(token);
233
+ }
234
+ }
235
+ }
236
+ return tokens;
237
+ };
238
+
239
+ beforeEach(() => {
240
+ getCurrentProjectIdMock.mockReturnValue(new ObjectID(PROJECT_ID));
241
+ apiPostMock.mockImplementation(async () => {
242
+ return { data: {} };
243
+ });
244
+ // The span panel's lazy full-span read: nothing more to add by default.
245
+ analyticsGetListMock.mockImplementation(async () => {
246
+ return { data: [], count: 0 };
247
+ });
248
+ });
249
+
250
+ afterEach(() => {
251
+ cleanup();
252
+ jest.clearAllMocks();
253
+ });
254
+
255
+ describe("TraceRow status", () => {
256
+ interface RenderedRow {
257
+ indicator: HTMLElement;
258
+ pulse: HTMLElement;
259
+ dot: HTMLElement;
260
+ barTrack: HTMLElement;
261
+ bar: HTMLElement;
262
+ }
263
+
264
+ const renderRow: (span: Span) => RenderedRow = (span: Span): RenderedRow => {
265
+ const result: RenderResult = render(
266
+ <TraceRow span={span} maxDurationNano={MAX_DURATION_NANO} />,
267
+ );
268
+
269
+ /*
270
+ * Found by its label rather than its role, so the color assertions
271
+ * below run (and fail) on a row that has lost role="img" too; the role
272
+ * is asserted on its own.
273
+ */
274
+ const indicator: HTMLElement | null =
275
+ result.container.querySelector<HTMLElement>('[aria-label^="Status:"]');
276
+ expect(indicator).not.toBeNull();
277
+ // The pulsing halo, then the solid dot.
278
+ const circles: Array<HTMLElement> = Array.from(
279
+ indicator!.querySelectorAll("span"),
280
+ );
281
+ expect(circles).toHaveLength(2);
282
+
283
+ const barTrack: HTMLElement | null =
284
+ result.container.querySelector<HTMLElement>("div.h-1.w-24");
285
+ expect(barTrack).not.toBeNull();
286
+ const bar: HTMLElement | null = barTrack!.querySelector("div");
287
+ expect(bar).not.toBeNull();
288
+
289
+ return {
290
+ indicator: indicator!,
291
+ pulse: circles[0]!,
292
+ dot: circles[1]!,
293
+ barTrack: barTrack!,
294
+ bar: bar!,
295
+ };
296
+ };
297
+
298
+ test("REGRESSION: an Unset span's dot and duration bar are green, not grey", () => {
299
+ const row: RenderedRow = renderRow(buildSpan(SpanStatus.Unset));
300
+
301
+ expect(row.dot).toHaveClass("bg-emerald-500", "ring-emerald-200");
302
+ expect(row.pulse).toHaveClass("bg-emerald-500");
303
+ expect(row.bar).toHaveClass("bg-emerald-500");
304
+
305
+ // The old theme: bg-gray-300 dot, ring-gray-100 ring, bg-gray-400 bar.
306
+ expect(greyClassTokens(row.indicator)).toEqual([]);
307
+ expect(row.bar).not.toHaveClass("bg-gray-400");
308
+ });
309
+
310
+ test("REGRESSION: the status dot is an image a screen reader announces as 'Status: Unset (no error)'", () => {
311
+ renderRow(buildSpan(SpanStatus.Unset));
312
+
313
+ /*
314
+ * The old dot was a plain span: its aria-label ("Status: Unset") was
315
+ * never announced, and getByRole("img") found nothing.
316
+ */
317
+ expect(
318
+ screen.getByRole("img", { name: "Status: Unset (no error)" }),
319
+ ).toHaveAttribute("title", `Unset: ${UNSET.description}`);
320
+ });
321
+
322
+ test.each(STATUS_CASES)(
323
+ "$name: the dot, its tooltip and the duration bar read as $expected.displayLabel",
324
+ (statusCase: StatusCase) => {
325
+ const expected: ExpectedStatus = statusCase.expected;
326
+ const row: RenderedRow = renderRow(buildSpan(statusCase.statusCode));
327
+
328
+ expect(
329
+ screen.getByRole("img", { name: `Status: ${expected.displayLabel}` }),
330
+ ).toBe(row.indicator);
331
+ // The title names the stored status and explains it.
332
+ expect(row.indicator).toHaveAttribute(
333
+ "title",
334
+ `${expected.label}: ${expected.description}`,
335
+ );
336
+
337
+ expect(row.dot).toHaveClass(
338
+ expected.dotClassName,
339
+ "ring-2",
340
+ expected.ringClassName,
341
+ );
342
+ expect(row.pulse).toHaveClass(expected.dotClassName);
343
+ // Only an error pulses.
344
+ expect(row.pulse.classList.contains("animate-ping")).toBe(
345
+ expected === ERROR,
346
+ );
347
+
348
+ expect(row.barTrack).toHaveClass(expected.barTrackClassName);
349
+ expect(row.bar).toHaveClass(expected.barClassName);
350
+ // Half the longest span on the page: the bar is this row's duration.
351
+ expect(row.bar.style.width).toBe("50%");
352
+ },
353
+ );
354
+
355
+ test("an Error span also gets the ERROR text pill", () => {
356
+ renderRow(buildSpan(SpanStatus.Error));
357
+
358
+ const pill: HTMLElement = screen.getByText("Error");
359
+ expect(pill).toHaveClass("uppercase", ...ERROR.pillClassNames);
360
+ });
361
+
362
+ test.each(NON_ERROR_CASES)(
363
+ "$name: no text pill, the dot carries the status",
364
+ (statusCase: StatusCase) => {
365
+ renderRow(buildSpan(statusCase.statusCode));
366
+
367
+ for (const text of ["Error", "Ok", "Unset", "Unset (no error)"]) {
368
+ expect(screen.queryByText(text)).not.toBeInTheDocument();
369
+ }
370
+ },
371
+ );
372
+
373
+ /*
374
+ * A span whose statusCode reads back as a string ("0" / "1" / "2"), the
375
+ * form a facet or chart key carries. Span's setter parses a numeric string
376
+ * (statusCode is a Number column), so the string is put on the instance
377
+ * itself, where the setter never sees it.
378
+ */
379
+ const buildSpanWithStatusText: (
380
+ statusCode: string,
381
+ statusMessage: string,
382
+ ) => Span = (statusCode: string, statusMessage: string): Span => {
383
+ const span: Span = buildSpan(undefined, statusMessage);
384
+ Object.defineProperty(span, "statusCode", {
385
+ configurable: true,
386
+ value: statusCode as unknown as SpanStatus,
387
+ });
388
+ // Still a string when the row reads it.
389
+ expect(span.statusCode).toBe(statusCode);
390
+ return span;
391
+ };
392
+
393
+ test('REGRESSION: a status of "2" as a string gets the ERROR pill, the pulse and the red message as well as the red dot', () => {
394
+ const row: RenderedRow = renderRow(
395
+ buildSpanWithStatusText("2", "connection refused"),
396
+ );
397
+
398
+ // The dot already read "2" as Error...
399
+ expect(screen.getByRole("img", { name: "Status: Error" })).toBe(
400
+ row.indicator,
401
+ );
402
+ expect(row.dot).toHaveClass(ERROR.dotClassName, ERROR.ringClassName);
403
+ expect(row.bar).toHaveClass(ERROR.barClassName);
404
+
405
+ /*
406
+ * ...but the row decided "error" with a strict === against the number 2,
407
+ * so the pulse, the pill and the message were missing.
408
+ */
409
+ expect(row.pulse).toHaveClass("animate-ping", ERROR.dotClassName);
410
+ expect(screen.getByText("Error")).toHaveClass(
411
+ "uppercase",
412
+ ...ERROR.pillClassNames,
413
+ );
414
+ const message: HTMLElement = screen.getByText("connection refused");
415
+ expect(message).toHaveClass("text-red-500");
416
+ expect(message).toHaveAttribute("title", "connection refused");
417
+ });
418
+
419
+ test.each([
420
+ { statusCode: "1", expected: OK },
421
+ { statusCode: "0", expected: UNSET },
422
+ ])(
423
+ 'a status of "$statusCode" as a string reads as $expected.displayLabel, with no pill, pulse or message',
424
+ (textCase: { statusCode: string; expected: ExpectedStatus }) => {
425
+ const expected: ExpectedStatus = textCase.expected;
426
+ const row: RenderedRow = renderRow(
427
+ buildSpanWithStatusText(
428
+ textCase.statusCode,
429
+ "upstream answered 404 (handled)",
430
+ ),
431
+ );
432
+
433
+ expect(
434
+ screen.getByRole("img", { name: `Status: ${expected.displayLabel}` }),
435
+ ).toBe(row.indicator);
436
+ expect(row.dot).toHaveClass(
437
+ expected.dotClassName,
438
+ expected.ringClassName,
439
+ );
440
+ expect(row.bar).toHaveClass(expected.barClassName);
441
+ expect(row.pulse).not.toHaveClass("animate-ping");
442
+
443
+ for (const text of ["Error", "Ok", "Unset", "Unset (no error)"]) {
444
+ expect(screen.queryByText(text)).not.toBeInTheDocument();
445
+ }
446
+ // The row shows a status message only for an error.
447
+ expect(
448
+ screen.queryByText("upstream answered 404 (handled)"),
449
+ ).not.toBeInTheDocument();
450
+ },
451
+ );
452
+
453
+ /*
454
+ * Recording an exception does not change a span's status. The list query
455
+ * brings hasException along, so the row can say so without "no error".
456
+ */
457
+ test("REGRESSION: an Unset span that recorded an exception is announced as a plain 'Status: Unset'", () => {
458
+ const span: Span = buildSpan(SpanStatus.Unset);
459
+ span.hasException = true;
460
+
461
+ const row: RenderedRow = renderRow(span);
462
+
463
+ expect(screen.getByRole("img", { name: "Status: Unset" })).toBe(
464
+ row.indicator,
465
+ );
466
+ // Only the words change: still Unset's green, tooltip and bar.
467
+ expect(row.indicator).toHaveAttribute(
468
+ "title",
469
+ `Unset: ${UNSET.description}`,
470
+ );
471
+ expect(row.dot).toHaveClass(UNSET.dotClassName, UNSET.ringClassName);
472
+ expect(row.bar).toHaveClass(UNSET.barClassName);
473
+ });
474
+
475
+ test.each([
476
+ { name: "false", hasException: false },
477
+ { name: "missing", hasException: undefined },
478
+ ])(
479
+ "an Unset span whose hasException is $name is still announced as 'Status: Unset (no error)'",
480
+ (exceptionCase: { name: string; hasException: boolean | undefined }) => {
481
+ const span: Span = buildSpan(SpanStatus.Unset);
482
+ if (exceptionCase.hasException !== undefined) {
483
+ span.hasException = exceptionCase.hasException;
484
+ }
485
+
486
+ renderRow(span);
487
+
488
+ expect(
489
+ screen.getByRole("img", { name: "Status: Unset (no error)" }),
490
+ ).toBeInTheDocument();
491
+ },
492
+ );
493
+
494
+ test("an Error span that recorded an exception is announced as 'Status: Error', with its pill", () => {
495
+ const span: Span = buildSpan(SpanStatus.Error);
496
+ span.hasException = true;
497
+
498
+ renderRow(span);
499
+
500
+ expect(
501
+ screen.getByRole("img", { name: "Status: Error" }),
502
+ ).toBeInTheDocument();
503
+ expect(screen.getByText("Error")).toBeInTheDocument();
504
+ });
505
+ });
506
+
507
+ describe("SpanDetailsPanel status", () => {
508
+ const renderPanel: (span: Span) => Promise<void> = async (
509
+ span: Span,
510
+ ): Promise<void> => {
511
+ // The lazy full-span read resolves inside act.
512
+ await act(async () => {
513
+ render(<SpanDetailsPanel span={span} />);
514
+ });
515
+ };
516
+
517
+ // The <dd> next to an Overview row's <dt>.
518
+ const overviewValue: (label: string) => HTMLElement = (
519
+ label: string,
520
+ ): HTMLElement => {
521
+ const term: HTMLElement = screen.getByText(label, { selector: "dt" });
522
+ const value: Element | null = term.nextElementSibling;
523
+ expect(value?.tagName).toBe("DD");
524
+ return value as HTMLElement;
525
+ };
526
+
527
+ const statusPill: () => HTMLElement = (): HTMLElement => {
528
+ return screen.getByTestId("span-details-status");
529
+ };
530
+
531
+ const statusPillDot: () => HTMLElement = (): HTMLElement => {
532
+ const dot: HTMLElement | null = statusPill().querySelector("span");
533
+ expect(dot).not.toBeNull();
534
+ return dot!;
535
+ };
536
+
537
+ /*
538
+ * The box holding a status message, found by the message itself so the
539
+ * color assertions reach it on any version of the panel.
540
+ */
541
+ const statusMessageBox: (message: string) => HTMLElement = (
542
+ message: string,
543
+ ): HTMLElement => {
544
+ const box: HTMLElement = screen.getByText(message);
545
+ expect(box.closest("section")).toHaveTextContent("Status Message");
546
+ return box;
547
+ };
548
+
549
+ test("REGRESSION: an Unset span's pill reads 'Unset (no error)' in green, not a grey 'Unset'", async () => {
550
+ await renderPanel(buildSpan(SpanStatus.Unset));
551
+
552
+ expect(statusPill().textContent).toBe("Unset (no error)");
553
+ expect(statusPill()).toHaveClass("bg-emerald-50", "text-emerald-700");
554
+ // Was the grey #9ca3af, rgb(156, 163, 175).
555
+ expect(statusPillDot().style.backgroundColor).toBe("rgb(16, 185, 129)");
556
+ expect(overviewValue("Status").textContent).toBe("Unset (no error)");
557
+ });
558
+
559
+ test.each(STATUS_CASES)(
560
+ "$name: the header pill and the Overview row read $expected.displayLabel",
561
+ async (statusCase: StatusCase) => {
562
+ const expected: ExpectedStatus = statusCase.expected;
563
+ await renderPanel(buildSpan(statusCase.statusCode));
564
+
565
+ const pill: HTMLElement = statusPill();
566
+ expect(pill.textContent).toBe(expected.displayLabel);
567
+ expect(pill).toHaveClass(...expected.pillClassNames);
568
+ expect(pill).toHaveAttribute("title", expected.description);
569
+
570
+ /*
571
+ * Theme-aware classes color the pill: an inline color (the old
572
+ * `${statusColor}1a` background) is one Theme.css cannot re-color.
573
+ */
574
+ expect(pill.style.color).toBe("");
575
+ expect(pill.style.backgroundColor).toBe("");
576
+
577
+ expect(statusPillDot().style.backgroundColor).toBe(expected.rgb);
578
+ expect(statusPillDot()).toHaveAttribute("aria-hidden", "true");
579
+
580
+ expect(overviewValue("Status").textContent).toBe(expected.displayLabel);
581
+ },
582
+ );
583
+
584
+ test.each(NON_ERROR_CASES)(
585
+ "REGRESSION: $name: a status message sits in a neutral box, not a red one",
586
+ async (statusCase: StatusCase) => {
587
+ await renderPanel(
588
+ buildSpan(statusCase.statusCode, "upstream answered 404 (handled)"),
589
+ );
590
+
591
+ const box: HTMLElement = statusMessageBox(
592
+ "upstream answered 404 (handled)",
593
+ );
594
+ expect(box).toHaveClass("border-gray-200", "bg-gray-50", "text-gray-700");
595
+ expect(box).not.toHaveClass("bg-red-50");
596
+ expect(box).not.toHaveClass("border-red-100");
597
+ expect(box).not.toHaveClass("text-red-700");
598
+ expect(box).toHaveAttribute("data-testid", "span-details-status-message");
599
+ },
600
+ );
601
+
602
+ test("an Error span's status message stays red", async () => {
603
+ await renderPanel(buildSpan(SpanStatus.Error, "connection refused"));
604
+
605
+ const box: HTMLElement = statusMessageBox("connection refused");
606
+ expect(box).toHaveClass("border-red-100", "bg-red-50", "text-red-700");
607
+ expect(box).not.toHaveClass("bg-gray-50");
608
+ expect(box).toHaveAttribute("data-testid", "span-details-status-message");
609
+ });
610
+
611
+ test.each([
612
+ { name: "Unset", statusCode: SpanStatus.Unset, isRed: false },
613
+ { name: "Error", statusCode: SpanStatus.Error, isRed: true },
614
+ ])(
615
+ "a message that only the lazily fetched full span carries follows the same rule ($name)",
616
+ async (fetchCase: {
617
+ name: string;
618
+ statusCode: SpanStatus;
619
+ isRed: boolean;
620
+ }) => {
621
+ analyticsGetListMock.mockImplementation(async () => {
622
+ const fullSpan: Span = new Span();
623
+ fullSpan.statusMessage = "retried after 503";
624
+ return { data: [fullSpan], count: 1 };
625
+ });
626
+
627
+ // The row's light span has no message of its own.
628
+ await renderPanel(buildSpan(fetchCase.statusCode));
629
+
630
+ expect(analyticsGetListMock).toHaveBeenCalled();
631
+ const box: HTMLElement = statusMessageBox("retried after 503");
632
+ expect(box.classList.contains("bg-red-50")).toBe(fetchCase.isRed);
633
+ expect(box.classList.contains("bg-gray-50")).toBe(!fetchCase.isRed);
634
+ },
635
+ );
636
+
637
+ test.each(STATUS_CASES)(
638
+ "$name without a status message shows no message box",
639
+ async (statusCase: StatusCase) => {
640
+ await renderPanel(buildSpan(statusCase.statusCode));
641
+
642
+ expect(
643
+ screen.queryByTestId("span-details-status-message"),
644
+ ).not.toBeInTheDocument();
645
+ expect(screen.queryByText("Status Message")).not.toBeInTheDocument();
646
+ },
647
+ );
648
+
649
+ const EVENT_TIME: Date = new Date("2026-09-28T10:00:00.250Z");
650
+
651
+ // An OpenTelemetry exception event, as the lazily fetched full span has it.
652
+ const exceptionEvent: (message: string) => SpanEvent = (
653
+ message: string,
654
+ ): SpanEvent => {
655
+ return {
656
+ name: "exception",
657
+ time: EVENT_TIME,
658
+ timeUnixNano: EVENT_TIME.getTime() * 1_000_000,
659
+ attributes: {
660
+ "exception.type": "TimeoutError",
661
+ "exception.message": message,
662
+ },
663
+ };
664
+ };
665
+
666
+ /*
667
+ * The lazy full-span read answers with these events. The row's light span
668
+ * never has events of its own.
669
+ */
670
+ const fullSpanWithEvents: (events: Array<SpanEvent>) => void = (
671
+ events: Array<SpanEvent>,
672
+ ): void => {
673
+ analyticsGetListMock.mockImplementation(async () => {
674
+ const fullSpan: Span = new Span();
675
+ fullSpan.events = events;
676
+ return { data: [fullSpan], count: 1 };
677
+ });
678
+ };
679
+
680
+ test("REGRESSION: an Unset span that carries an exception reads a plain 'Unset', not 'Unset (no error)'", async () => {
681
+ fullSpanWithEvents([exceptionEvent("upstream timed out after 30s")]);
682
+
683
+ await renderPanel(buildSpan(SpanStatus.Unset));
684
+
685
+ // The events come from the lazy read, which has to ask for them.
686
+ expect(analyticsGetListMock).toHaveBeenCalledWith(
687
+ expect.objectContaining({
688
+ select: expect.objectContaining({ events: true }),
689
+ }),
690
+ );
691
+
692
+ // The exception is listed with the span...
693
+ expect(screen.getByText("Exceptions (1)")).toBeInTheDocument();
694
+ expect(
695
+ screen.getByText("upstream timed out after 30s"),
696
+ ).toBeInTheDocument();
697
+
698
+ /*
699
+ * ...so "no error" would contradict it. Recording an exception does not
700
+ * change a span's status: the status is named, not explained.
701
+ */
702
+ const pill: HTMLElement = statusPill();
703
+ expect(pill.textContent).toBe("Unset");
704
+ expect(overviewValue("Status").textContent).toBe("Unset");
705
+
706
+ // Only the words change: the pill keeps Unset's green and its tooltip.
707
+ expect(pill).toHaveClass(...UNSET.pillClassNames);
708
+ expect(pill).toHaveAttribute("title", UNSET.description);
709
+ expect(statusPillDot().style.backgroundColor).toBe(UNSET.rgb);
710
+ expect(greyClassTokens(pill)).toEqual([]);
711
+ });
712
+
713
+ test.each(STATUS_CASES)(
714
+ "$name carrying an exception: the header pill and the Overview row read $expected.label",
715
+ async (statusCase: StatusCase) => {
716
+ const expected: ExpectedStatus = statusCase.expected;
717
+ fullSpanWithEvents([exceptionEvent("connection reset by peer")]);
718
+
719
+ await renderPanel(buildSpan(statusCase.statusCode));
720
+
721
+ expect(screen.getByText("connection reset by peer")).toBeInTheDocument();
722
+
723
+ const pill: HTMLElement = statusPill();
724
+ expect(pill.textContent).toBe(expected.label);
725
+ expect(pill).toHaveClass(...expected.pillClassNames);
726
+ expect(pill).toHaveAttribute("title", expected.description);
727
+ expect(statusPillDot().style.backgroundColor).toBe(expected.rgb);
728
+
729
+ expect(overviewValue("Status").textContent).toBe(expected.label);
730
+ },
731
+ );
732
+
733
+ test("an Unset span whose events hold no exception still reads 'Unset (no error)'", async () => {
734
+ fullSpanWithEvents([
735
+ {
736
+ name: "cache.miss",
737
+ time: EVENT_TIME,
738
+ timeUnixNano: EVENT_TIME.getTime() * 1_000_000,
739
+ attributes: { "cache.key": "orders:42" },
740
+ },
741
+ ]);
742
+
743
+ await renderPanel(buildSpan(SpanStatus.Unset));
744
+
745
+ expect(analyticsGetListMock).toHaveBeenCalled();
746
+ expect(screen.queryByText(/^Exceptions \(/)).not.toBeInTheDocument();
747
+
748
+ const pill: HTMLElement = statusPill();
749
+ expect(pill.textContent).toBe("Unset (no error)");
750
+ expect(pill).toHaveClass(...UNSET.pillClassNames);
751
+ expect(overviewValue("Status").textContent).toBe("Unset (no error)");
752
+ });
753
+
754
+ /*
755
+ * The row's light span carries hasException, so the panel names the status
756
+ * plainly from the start — not only once the full span's events arrive.
757
+ */
758
+ test("REGRESSION: an Unset span flagged hasException reads 'Unset' while the full span is still loading", async () => {
759
+ // The lazy read has not answered yet.
760
+ analyticsGetListMock.mockImplementation(() => {
761
+ return new Promise<never>(() => {});
762
+ });
763
+ const span: Span = buildSpan(SpanStatus.Unset);
764
+ span.hasException = true;
765
+
766
+ await renderPanel(span);
767
+
768
+ expect(analyticsGetListMock).toHaveBeenCalled();
769
+ expect(statusPill().textContent).toBe("Unset");
770
+ expect(overviewValue("Status").textContent).toBe("Unset");
771
+ expect(statusPill()).toHaveClass(...UNSET.pillClassNames);
772
+ });
773
+
774
+ test("REGRESSION: an Unset span flagged hasException still reads 'Unset' when the full-span read fails", async () => {
775
+ analyticsGetListMock.mockImplementation(async () => {
776
+ throw new Error("span read failed");
777
+ });
778
+ const span: Span = buildSpan(SpanStatus.Unset);
779
+ span.hasException = true;
780
+
781
+ await renderPanel(span);
782
+
783
+ expect(analyticsGetListMock).toHaveBeenCalled();
784
+ expect(statusPill().textContent).toBe("Unset");
785
+ expect(overviewValue("Status").textContent).toBe("Unset");
786
+ });
787
+
788
+ test("an Unset span flagged hasException false reads 'Unset (no error)'", async () => {
789
+ const span: Span = buildSpan(SpanStatus.Unset);
790
+ span.hasException = false;
791
+
792
+ await renderPanel(span);
793
+
794
+ expect(statusPill().textContent).toBe("Unset (no error)");
795
+ expect(overviewValue("Status").textContent).toBe("Unset (no error)");
796
+ });
797
+ });
798
+
799
+ describe("SpanStatusElement", () => {
800
+ const renderStatusElement: (props: SpanStatusElementProps) => void = (
801
+ props: SpanStatusElementProps,
802
+ ): void => {
803
+ render(
804
+ <MemoryRouter>
805
+ <SpanStatusElement {...props} />
806
+ </MemoryRouter>,
807
+ );
808
+ };
809
+
810
+ const CIRCLE_CASES: Array<StatusCase> = STATUS_CASES.filter(
811
+ (statusCase: StatusCase): boolean => {
812
+ return statusCase.statusCode !== undefined;
813
+ },
814
+ );
815
+
816
+ test.each(CIRCLE_CASES)(
817
+ "$name: a circle named 'Span Status: $expected.displayLabel' in its status color",
818
+ (statusCase: StatusCase) => {
819
+ const expected: ExpectedStatus = statusCase.expected;
820
+ renderStatusElement({
821
+ spanStatusCode: statusCase.statusCode as SpanStatus,
822
+ });
823
+
824
+ const circle: HTMLElement = screen.getByRole("img");
825
+ expect(circle).toHaveAccessibleName(
826
+ `Span Status: ${expected.displayLabel}`,
827
+ );
828
+ expect(circle.style.backgroundColor).toBe(expected.rgb);
829
+ },
830
+ );
831
+
832
+ test("REGRESSION: Unset and Ok get colors of their own, and neither is the old brand green", () => {
833
+ renderStatusElement({ spanStatusCode: SpanStatus.Unset });
834
+ renderStatusElement({ spanStatusCode: SpanStatus.Ok });
835
+
836
+ // A prefix match, so the old "Span Status: Unset" circle is compared too.
837
+ const unset: HTMLElement = screen.getByRole("img", {
838
+ name: /^Span Status: Unset/,
839
+ });
840
+ const ok: HTMLElement = screen.getByRole("img", {
841
+ name: "Span Status: Ok",
842
+ });
843
+
844
+ // Both were BrandColors Green (#2ab57d), rgb(42, 181, 125).
845
+ expect(unset.style.backgroundColor).toBe("rgb(16, 185, 129)");
846
+ expect(ok.style.backgroundColor).toBe("rgb(8, 145, 178)");
847
+ expect(ok.style.backgroundColor).not.toBe(unset.style.backgroundColor);
848
+ });
849
+
850
+ test("REGRESSION: Unset is 0, a falsy value, and still gets its circle", () => {
851
+ expect(SpanStatus.Unset).toBe(0);
852
+
853
+ renderStatusElement({ spanStatusCode: SpanStatus.Unset });
854
+
855
+ expect(screen.getAllByRole("img")).toHaveLength(1);
856
+ expect(
857
+ screen.getByRole("img", { name: "Span Status: Unset (no error)" }),
858
+ ).toBeInTheDocument();
859
+ });
860
+
861
+ test("an Error span's circle is red", () => {
862
+ renderStatusElement({ spanStatusCode: SpanStatus.Error });
863
+
864
+ expect(
865
+ screen.getByRole("img", { name: "Span Status: Error" }).style
866
+ .backgroundColor,
867
+ ).toBe("rgb(239, 68, 68)");
868
+ });
869
+
870
+ test.each(CIRCLE_CASES)(
871
+ "$name with plainLabel: a circle named 'Span Status: $expected.label' in the same color",
872
+ (statusCase: StatusCase) => {
873
+ const expected: ExpectedStatus = statusCase.expected;
874
+ renderStatusElement({
875
+ spanStatusCode: statusCase.statusCode as SpanStatus,
876
+ plainLabel: true,
877
+ });
878
+
879
+ const circle: HTMLElement = screen.getByRole("img");
880
+ expect(circle).toHaveAccessibleName(`Span Status: ${expected.label}`);
881
+ expect(circle.style.backgroundColor).toBe(expected.rgb);
882
+ },
883
+ );
884
+
885
+ test("plainLabel names Unset alone; without it, or set to false, the circle says 'Unset (no error)'", () => {
886
+ /*
887
+ * The exception tables pass plainLabel: each of their rows is an
888
+ * exception, which "no error" would contradict.
889
+ */
890
+ renderStatusElement({ spanStatusCode: SpanStatus.Unset, plainLabel: true });
891
+ renderStatusElement({ spanStatusCode: SpanStatus.Unset });
892
+ renderStatusElement({
893
+ spanStatusCode: SpanStatus.Unset,
894
+ plainLabel: false,
895
+ });
896
+
897
+ const names: Array<string | null> = screen
898
+ .getAllByRole("img")
899
+ .map((circle: HTMLElement): string | null => {
900
+ return circle.getAttribute("aria-label");
901
+ });
902
+ expect(names).toEqual([
903
+ "Span Status: Unset",
904
+ "Span Status: Unset (no error)",
905
+ "Span Status: Unset (no error)",
906
+ ]);
907
+ });
908
+
909
+ test.each([
910
+ { name: "undefined", value: undefined },
911
+ { name: "null", value: null },
912
+ ])(
913
+ "a $name status renders no circle, with or without plainLabel",
914
+ (missingCase: { name: string; value: undefined | null }) => {
915
+ renderStatusElement({ spanStatusCode: missingCase.value });
916
+ renderStatusElement({
917
+ spanStatusCode: missingCase.value,
918
+ plainLabel: true,
919
+ });
920
+
921
+ expect(screen.queryByRole("img")).not.toBeInTheDocument();
922
+ },
923
+ );
924
+
925
+ test("a missing status with a title still links the title to its trace, with no circle", () => {
926
+ renderStatusElement({
927
+ spanStatusCode: undefined,
928
+ title: "GET /api/orders/:id",
929
+ traceId: TRACE_ID,
930
+ plainLabel: true,
931
+ });
932
+
933
+ expect(
934
+ screen.getByRole("link", { name: "GET /api/orders/:id" }),
935
+ ).toHaveAttribute(
936
+ "href",
937
+ `/dashboard/${PROJECT_ID}/traces/view/${TRACE_ID}`,
938
+ );
939
+ expect(screen.queryByRole("img")).not.toBeInTheDocument();
940
+ });
941
+
942
+ test("with a title and a trace id, the title links to that trace", () => {
943
+ renderStatusElement({
944
+ spanStatusCode: SpanStatus.Error,
945
+ title: SPAN_ID,
946
+ traceId: TRACE_ID,
947
+ });
948
+
949
+ expect(screen.getByRole("link", { name: SPAN_ID })).toHaveAttribute(
950
+ "href",
951
+ `/dashboard/${PROJECT_ID}/traces/view/${TRACE_ID}`,
952
+ );
953
+ // The circle still sits beside it.
954
+ expect(
955
+ screen.getByRole("img", { name: "Span Status: Error" }),
956
+ ).toBeInTheDocument();
957
+ });
958
+
959
+ test("an empty title renders the circle alone, with no link", () => {
960
+ renderStatusElement({ spanStatusCode: SpanStatus.Ok, title: "" });
961
+
962
+ expect(screen.queryByRole("link")).not.toBeInTheDocument();
963
+ expect(
964
+ screen.getByRole("img", { name: "Span Status: Ok" }),
965
+ ).toBeInTheDocument();
966
+ });
967
+ });