@oneuptime/common 14.0.1 → 14.0.3

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 (1241) hide show
  1. package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +2 -0
  3. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
  4. package/Models/DatabaseModels/IncidentAlert.ts +540 -0
  5. package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
  6. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
  7. package/Models/DatabaseModels/IncidentFeed.ts +2 -0
  8. package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
  9. package/Models/DatabaseModels/Index.ts +2 -0
  10. package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
  11. package/Models/DatabaseModels/Monitor.ts +48 -0
  12. package/Models/DatabaseModels/Project.ts +60 -0
  13. package/Models/DatabaseModels/RumApplication.ts +52 -1
  14. package/Models/DatabaseModels/Runner.ts +5 -4
  15. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  16. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  17. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  18. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  19. package/Server/API/AIInvestigationAPI.ts +341 -0
  20. package/Server/API/AutoRemediationAPI.ts +184 -2
  21. package/Server/API/CommonAPI.ts +70 -0
  22. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  23. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  24. package/Server/API/MonitorAPI.ts +140 -0
  25. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  26. package/Server/API/SlackAPI.ts +447 -99
  27. package/Server/API/StatusPageAPI.ts +146 -26
  28. package/Server/API/TelemetryAPI.ts +216 -5
  29. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  30. package/Server/API/WorkflowVariableAPI.ts +156 -0
  31. package/Server/Infrastructure/GlobalCache.ts +35 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  40. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  41. package/Server/Middleware/TelemetryIngest.ts +47 -2
  42. package/Server/Middleware/UserAuthorization.ts +56 -2
  43. package/Server/Services/AIAgentService.ts +1 -0
  44. package/Server/Services/AccessTokenService.ts +121 -48
  45. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  46. package/Server/Services/AlertEpisodeService.ts +14 -1
  47. package/Server/Services/AlertService.ts +134 -22
  48. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  49. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  50. package/Server/Services/BillingService.ts +1 -0
  51. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  52. package/Server/Services/IncidentAlertService.ts +1811 -0
  53. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  54. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  55. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  56. package/Server/Services/IncidentEpisodeService.ts +14 -1
  57. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  58. package/Server/Services/IncidentMemberService.ts +21 -0
  59. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  60. package/Server/Services/IncidentService.ts +330 -30
  61. package/Server/Services/IncidentStateTimelineService.ts +39 -0
  62. package/Server/Services/IncidentTemplateService.ts +83 -3
  63. package/Server/Services/Index.ts +2 -0
  64. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  65. package/Server/Services/KubernetesClusterService.ts +1570 -1
  66. package/Server/Services/LogAggregationService.ts +14 -6
  67. package/Server/Services/MonitorService.ts +651 -52
  68. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  71. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  72. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  73. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  74. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  75. package/Server/Services/ProbeService.ts +7 -1
  76. package/Server/Services/ProjectService.ts +11 -1
  77. package/Server/Services/RunbookCredentialService.ts +26 -1
  78. package/Server/Services/RunbookSecretService.ts +36 -0
  79. package/Server/Services/RunnerJobService.ts +941 -23
  80. package/Server/Services/RunnerService.ts +439 -6
  81. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  82. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  83. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  84. package/Server/Services/ServiceLevelObjectiveService.ts +14 -1
  85. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  86. package/Server/Services/StatusPageService.ts +301 -74
  87. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  88. package/Server/Services/TeamMemberService.ts +309 -7
  89. package/Server/Services/TraceAggregationService.ts +10 -6
  90. package/Server/Services/UserEmailService.ts +1 -0
  91. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  92. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  93. package/Server/Services/UserNotificationRuleService.ts +4 -0
  94. package/Server/Services/UserService.ts +122 -1
  95. package/Server/Services/WorkflowVariableService.ts +593 -8
  96. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  97. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  98. package/Server/Types/Database/QueryHelper.ts +22 -0
  99. package/Server/Types/Markdown.ts +502 -27
  100. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  101. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  102. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  103. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  104. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  105. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  106. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  107. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  108. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  109. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  110. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  111. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  112. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  113. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  114. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  115. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  116. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  117. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  118. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  119. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  120. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  121. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  122. package/Server/Utils/PushNotificationUtil.ts +45 -0
  123. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  124. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  125. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
  126. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  127. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  128. package/Server/Utils/StartServer.ts +4 -3
  129. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  130. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  131. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  132. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  133. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  134. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  135. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  136. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  137. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  138. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  139. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  140. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  141. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  142. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  143. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  144. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  145. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  146. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  147. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  148. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  149. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  150. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  151. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  152. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  153. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  154. package/Server/Utils/Workspace/Workspace.ts +171 -54
  155. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  156. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  157. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  158. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  159. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  160. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  161. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  162. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  163. package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
  164. package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
  165. package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
  166. package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
  167. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  168. package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
  169. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  170. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  171. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  172. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  173. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  174. package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
  175. package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
  176. package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
  177. package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
  178. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  179. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  180. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  181. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +238 -0
  182. package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
  183. package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
  184. package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
  185. package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
  186. package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
  187. package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
  188. package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
  189. package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
  190. package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
  191. package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
  192. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  193. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
  194. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  195. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  196. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  197. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  198. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  199. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  200. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  201. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +12 -0
  202. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  203. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  204. package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
  205. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  206. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  207. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  208. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  209. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
  210. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
  211. package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
  212. package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
  213. package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
  214. package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
  215. package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
  216. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  217. package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
  218. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  219. package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
  220. package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
  221. package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
  222. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  223. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  224. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  225. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  226. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  227. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  228. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  229. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  230. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  231. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  232. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  233. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  234. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  235. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  236. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  237. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  238. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  239. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  240. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  241. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  242. package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
  243. package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
  244. package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
  245. package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
  246. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  247. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  248. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  249. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  250. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  251. package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
  252. package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
  253. package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
  254. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  255. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  256. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  257. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  258. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
  259. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
  260. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  261. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  262. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  263. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  264. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  265. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  266. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  267. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  268. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  269. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  270. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  271. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  272. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  273. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  274. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  275. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  276. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  277. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  278. package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
  279. package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
  280. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
  281. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  282. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  283. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
  284. package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
  285. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  286. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  287. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  288. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  289. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  290. package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
  291. package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
  292. package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
  293. package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
  294. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  295. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  296. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  297. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  298. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  299. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  300. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  301. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  302. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  303. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  304. package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
  305. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  306. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  307. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
  308. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  309. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  310. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  311. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  312. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  313. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  314. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  315. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  316. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  317. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  318. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  319. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  320. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  321. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  322. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  323. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  324. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  325. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  326. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  327. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  328. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  329. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  330. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  331. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  332. package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
  333. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  334. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  335. package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
  336. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  337. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  338. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  339. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  340. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  341. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  342. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  343. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  344. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  345. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  346. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  347. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  348. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  349. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  350. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  351. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  352. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  353. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  354. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
  355. package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
  356. package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
  357. package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
  358. package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
  359. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  360. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  361. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  362. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  363. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  364. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  365. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  366. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  367. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  368. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  369. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  370. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  371. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  372. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  373. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  374. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  375. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  376. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  377. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  378. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  379. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  380. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  381. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  382. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  383. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  384. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  385. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  386. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  387. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +9 -1
  388. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  389. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  390. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  391. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  392. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  393. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  394. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  395. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  396. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  397. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +5 -0
  398. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +12 -0
  399. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  400. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  401. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  402. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  403. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  404. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  405. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  406. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  407. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  408. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  409. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  410. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  411. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  412. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  413. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  414. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  415. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  416. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  417. package/Tests/Server/Types/Markdown.test.ts +1033 -0
  418. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  419. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  420. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  421. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  422. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  423. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  424. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  425. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  426. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  427. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  428. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  429. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  430. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  431. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  432. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  433. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  434. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  435. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  436. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  437. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  438. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  439. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  440. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  441. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  442. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +55 -0
  443. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  444. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  445. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  446. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  447. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  448. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  449. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  450. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
  451. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  452. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  453. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  454. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  455. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  456. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  457. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  458. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  459. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  460. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  461. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  462. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  463. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  464. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  465. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  466. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  467. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  468. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  469. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  470. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  471. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  472. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  473. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  474. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  475. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  476. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  477. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  478. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  479. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  480. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  481. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  482. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  483. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  484. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  485. package/Tests/Types/Rum/WebVitals.test.ts +173 -0
  486. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  487. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  488. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  489. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  490. package/Tests/Types/Timezone.test.ts +5 -0
  491. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  492. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  493. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  494. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  495. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  496. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  497. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  498. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  499. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  500. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  501. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  502. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  503. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  504. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  505. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  506. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  507. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  508. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  509. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  510. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  511. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  512. package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
  513. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  514. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  515. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  516. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  517. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  518. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  519. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  520. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  521. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  522. package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
  523. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  524. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  525. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  526. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  527. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
  528. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
  529. package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
  530. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  531. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  532. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  533. package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
  534. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  535. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  536. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  537. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  538. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  539. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  540. package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
  541. package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
  542. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
  543. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
  544. package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
  545. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
  546. package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
  547. package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
  548. package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
  549. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
  550. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
  551. package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
  552. package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
  553. package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
  554. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  555. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  556. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  557. package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
  558. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  559. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  560. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  561. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  562. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  563. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  564. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  565. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  566. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  567. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  568. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  569. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  570. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  571. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  572. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  573. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  574. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  575. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  576. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  577. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  578. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  579. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  580. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  581. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  582. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  583. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  584. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  585. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  586. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  587. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
  588. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  589. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  590. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  591. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  592. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  593. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  594. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  595. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  596. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  597. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  598. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  599. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  600. package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
  601. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  602. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  603. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  604. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  605. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  606. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  607. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  608. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  609. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  610. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  611. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  612. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  613. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  614. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  615. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  616. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  617. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  618. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  619. package/Types/Date.ts +121 -7
  620. package/Types/Email/EmailMessage.ts +8 -0
  621. package/Types/Email/EmailTemplateType.ts +1 -0
  622. package/Types/Exception/ExceptionMessages.ts +1 -0
  623. package/Types/Incident/IncidentAlertLink.ts +38 -0
  624. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  625. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  626. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  627. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  628. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  629. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  630. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  631. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  632. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  633. package/Types/Permission.ts +48 -0
  634. package/Types/Rum/SessionReplay.ts +80 -8
  635. package/Types/Rum/SessionReplayApi.ts +44 -1
  636. package/Types/Rum/UserFlow.ts +187 -0
  637. package/Types/Rum/WebVitals.ts +52 -0
  638. package/Types/Runbook/RunbookStepType.ts +10 -0
  639. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  640. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  641. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  642. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  643. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  644. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  645. package/Types/Timezone.ts +5 -0
  646. package/Types/TimezoneAlias.ts +385 -0
  647. package/Types/Workflow/Templates.ts +3041 -2
  648. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  649. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  650. package/Typings/Index.d.ts +24 -0
  651. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  652. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  653. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  654. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  655. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  656. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  657. package/UI/Components/Calendar/Calendar.css +83 -0
  658. package/UI/Components/Calendar/Calendar.tsx +136 -7
  659. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  660. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  661. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  662. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  663. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  664. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  665. package/UI/Components/Detail/FieldLabel.tsx +8 -0
  666. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  667. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  668. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  669. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  670. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  671. package/UI/Components/InfoCard/InfoCard.tsx +47 -1
  672. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  673. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  674. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  675. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  676. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  677. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  678. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  679. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  680. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  681. package/UI/Components/ModelTable/BaseModelTable.tsx +161 -31
  682. package/UI/Components/ModelTable/Column.ts +5 -0
  683. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  684. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  685. package/UI/Components/Page/ModelPage.tsx +59 -3
  686. package/UI/Components/Table/TableHeader.tsx +47 -2
  687. package/UI/Components/Table/Types/Column.ts +7 -0
  688. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  689. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  690. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  691. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  692. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  693. package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
  694. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  695. package/UI/Utils/PermissionGate.ts +11 -2
  696. package/UI/Utils/Timezone.ts +69 -28
  697. package/Utils/AI/InvestigationReport.ts +195 -25
  698. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  699. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  700. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  701. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  702. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  703. package/Utils/Incident/IncidentFromAlerts.ts +516 -0
  704. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  705. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  706. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  707. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  708. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  709. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  710. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  711. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  712. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  713. package/Utils/Rum/SessionIdentity.ts +44 -0
  714. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  715. package/Utils/Rum/SessionTraceState.ts +212 -0
  716. package/Utils/Rum/UserFlow.ts +1527 -0
  717. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  718. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  719. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  720. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  721. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  722. package/Utils/Uptime/UptimeUtil.ts +122 -38
  723. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  724. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  725. package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
  726. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  727. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  728. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  729. package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
  730. package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
  731. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  732. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  733. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  734. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  735. package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
  736. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  737. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  738. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  739. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  740. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  741. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  742. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  743. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  744. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  745. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  746. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  747. package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
  748. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  749. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  750. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  751. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  752. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  753. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  754. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  755. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  756. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  757. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  758. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  759. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  760. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  761. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  762. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  763. package/build/dist/Server/API/CommonAPI.js +49 -0
  764. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  765. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  766. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  767. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  768. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  769. package/build/dist/Server/API/MonitorAPI.js +107 -1
  770. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  771. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  772. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  773. package/build/dist/Server/API/SlackAPI.js +322 -78
  774. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  775. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  776. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  777. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  778. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  779. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  780. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  781. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  782. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  783. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  784. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  785. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  786. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  787. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  788. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  789. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  790. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  791. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  792. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  793. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  794. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  795. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
  796. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
  797. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
  798. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
  799. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  800. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  801. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  802. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  803. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  804. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  805. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  806. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  807. package/build/dist/Server/Services/AIAgentService.js +1 -0
  808. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  809. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  810. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  811. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  812. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  813. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  814. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  815. package/build/dist/Server/Services/AlertService.js +110 -23
  816. package/build/dist/Server/Services/AlertService.js.map +1 -1
  817. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  818. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  819. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  820. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  821. package/build/dist/Server/Services/BillingService.js +1 -0
  822. package/build/dist/Server/Services/BillingService.js.map +1 -1
  823. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  824. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  825. package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
  826. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
  827. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  828. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  829. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  830. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  831. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  832. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  833. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  834. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  835. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  836. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  837. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  838. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  839. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  840. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  841. package/build/dist/Server/Services/IncidentService.js +263 -33
  842. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  843. package/build/dist/Server/Services/IncidentStateTimelineService.js +33 -0
  844. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  845. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  846. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  847. package/build/dist/Server/Services/Index.js +2 -0
  848. package/build/dist/Server/Services/Index.js.map +1 -1
  849. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  850. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  851. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  852. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  853. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  854. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  855. package/build/dist/Server/Services/MonitorService.js +487 -48
  856. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  857. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  858. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  859. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  860. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  861. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  862. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  863. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  864. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  865. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  866. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  867. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  868. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  869. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  870. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  871. package/build/dist/Server/Services/ProbeService.js +7 -1
  872. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  873. package/build/dist/Server/Services/ProjectService.js +11 -1
  874. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  875. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  876. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  877. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  878. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  879. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  880. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  881. package/build/dist/Server/Services/RunnerService.js +344 -5
  882. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  883. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  884. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  885. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  886. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  887. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  888. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  889. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +12 -1
  890. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  891. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  892. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  893. package/build/dist/Server/Services/StatusPageService.js +230 -32
  894. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  895. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  896. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  897. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  898. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  899. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  900. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  901. package/build/dist/Server/Services/UserEmailService.js +1 -0
  902. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  903. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  904. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  905. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  906. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  907. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  908. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  909. package/build/dist/Server/Services/UserService.js +107 -1
  910. package/build/dist/Server/Services/UserService.js.map +1 -1
  911. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  912. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  913. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  914. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  915. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  916. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  917. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  918. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  919. package/build/dist/Server/Types/Markdown.js +394 -27
  920. package/build/dist/Server/Types/Markdown.js.map +1 -1
  921. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  922. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  923. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  924. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  925. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  926. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  927. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  928. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  929. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  930. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  931. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  932. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  933. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  934. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  935. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  936. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  937. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  938. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  939. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  940. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  941. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  942. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  943. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  944. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  945. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  946. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  947. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  948. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  949. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  950. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  951. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  952. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  953. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  954. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  955. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  956. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  957. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  958. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  959. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  960. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  961. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  962. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  963. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  964. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  965. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  966. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  967. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  968. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  969. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  970. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  971. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
  972. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
  973. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  974. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  975. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  976. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  977. package/build/dist/Server/Utils/StartServer.js +4 -3
  978. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  979. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  980. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  981. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  982. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  983. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  984. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  985. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  986. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  987. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  988. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  989. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  990. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  991. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  992. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  993. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  994. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  995. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  996. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  997. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  998. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  999. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  1000. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  1001. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  1002. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  1003. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  1004. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  1005. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  1006. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  1007. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  1008. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  1009. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  1010. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  1011. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  1012. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  1013. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  1014. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  1015. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  1016. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  1017. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  1018. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  1019. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  1020. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  1021. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  1022. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  1023. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  1024. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  1025. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  1026. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  1027. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  1028. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  1029. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  1030. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  1031. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  1032. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  1033. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  1034. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  1035. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  1036. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  1037. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  1038. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  1039. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  1040. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  1041. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  1042. package/build/dist/Types/Date.js +97 -7
  1043. package/build/dist/Types/Date.js.map +1 -1
  1044. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  1045. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  1046. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  1047. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  1048. package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
  1049. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
  1050. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  1051. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  1052. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  1053. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  1054. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  1055. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  1056. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  1057. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  1058. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  1059. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  1060. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  1061. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  1062. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  1063. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  1064. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  1065. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  1066. package/build/dist/Types/Permission.js +42 -0
  1067. package/build/dist/Types/Permission.js.map +1 -1
  1068. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  1069. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  1070. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  1071. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  1072. package/build/dist/Types/Rum/UserFlow.js +115 -0
  1073. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  1074. package/build/dist/Types/Rum/WebVitals.js +25 -0
  1075. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  1076. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  1077. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  1078. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  1079. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  1080. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  1081. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  1082. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  1083. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  1084. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  1085. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  1086. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  1087. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  1088. package/build/dist/Types/Timezone.js +5 -0
  1089. package/build/dist/Types/Timezone.js.map +1 -1
  1090. package/build/dist/Types/TimezoneAlias.js +340 -0
  1091. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  1092. package/build/dist/Types/Workflow/Templates.js +2694 -2
  1093. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  1094. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  1095. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  1096. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  1097. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  1098. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  1099. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  1100. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  1101. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  1102. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  1103. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  1104. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  1105. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  1106. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  1107. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  1108. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  1109. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  1110. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  1111. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  1112. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  1113. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  1114. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  1115. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  1116. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  1117. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  1118. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  1119. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  1120. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  1121. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  1122. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  1123. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  1124. package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
  1125. package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
  1126. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  1127. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  1128. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  1129. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  1130. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  1131. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  1132. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  1133. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  1134. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  1135. package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
  1136. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  1137. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  1138. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1139. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  1140. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1141. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1142. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1143. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1144. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1145. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1146. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1147. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1148. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1149. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1150. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1151. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1152. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1153. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1154. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1155. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +89 -22
  1156. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1157. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1158. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1159. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1160. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1161. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1162. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1163. package/build/dist/UI/Components/Table/TableHeader.js +18 -2
  1164. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  1165. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1166. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1167. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1168. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1169. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1170. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1171. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1172. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1173. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1174. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1175. package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
  1176. package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
  1177. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1178. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1179. package/build/dist/UI/Utils/PermissionGate.js +4 -2
  1180. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  1181. package/build/dist/UI/Utils/Timezone.js +52 -17
  1182. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1183. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1184. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1185. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1186. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1187. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1188. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1189. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1190. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1191. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1192. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1193. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1194. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1195. package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
  1196. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
  1197. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1198. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1199. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1200. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1201. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1202. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1203. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1204. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1205. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1206. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1207. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1208. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1209. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1210. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1211. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1212. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1213. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1214. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1215. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1216. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1217. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1218. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1219. package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
  1220. package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
  1221. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1222. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1223. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1224. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1225. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1226. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1227. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1228. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1229. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1230. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1231. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1232. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1233. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1234. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1235. package/jest.config.json +2 -1
  1236. package/package.json +2 -1
  1237. package/tsconfig.tests.json +36 -0
  1238. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1239. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1240. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1241. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -0,0 +1,1864 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ act,
4
+ fireEvent,
5
+ render,
6
+ screen,
7
+ waitFor,
8
+ within,
9
+ } from "@testing-library/react";
10
+ import { beforeEach, describe, expect, test } from "@jest/globals";
11
+ import * as React from "react";
12
+ import { JSONObject } from "../../../Types/JSON";
13
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
14
+
15
+ /*
16
+ * Integration tests for the "Send Test" button as it is wired into the three
17
+ * workspace cards on Project Settings > Workspace:
18
+ *
19
+ * - Slack Channels -> POST /slack/channels/test { channelId }
20
+ * - Microsoft Teams Channels -> POST /microsoft-teams/channels/test { teamId, channelId }
21
+ * - Microsoft Teams Chats -> POST /microsoft-teams/chats/test { chatId }
22
+ *
23
+ * The shared SendTestNotificationButton has its own unit tests. What can only
24
+ * be caught here is the wiring each card does: which route a row posts to,
25
+ * which ids go in the body (and that nothing else does), what the button is
26
+ * called for a screen reader, that every row gets exactly one button, that a
27
+ * row's result stays with that row, and the row layout each card owns (the
28
+ * row may wrap and the name keeps a minimum width, so on a phone the Send
29
+ * Test control drops onto its own line instead of squeezing the name out).
30
+ *
31
+ * It also covers what each card does around a send:
32
+ *
33
+ * - While any test is in flight, the controls that reload the list are
34
+ * disabled: Refresh on every card, and the team dropdown on the Teams
35
+ * channels card. A reload unmounts the rows, and a row's result would be
36
+ * lost with it. They come back once every send has settled, whether it
37
+ * succeeded or failed.
38
+ * - On the Teams channels card, channel loads that finish out of order.
39
+ * Only the most recent load - for another team, or a second load of the
40
+ * same team - may fill the list, end the loading state or show an error.
41
+ *
42
+ * What these tests cannot see: the Teams channels card keys each row by team
43
+ * AND channel id, and posts the team its list was loaded for (channelsTeamId)
44
+ * rather than the dropdown's current team. Whenever rows are on screen those
45
+ * two teams are the same, because every path that changes the selected team
46
+ * also replaces the list with the loader (which unmounts every row). So the
47
+ * tests would pass just as well with a key of the channel id alone, or with
48
+ * the dropdown's team in the body. Both are defence in depth that nothing in
49
+ * the DOM can observe; the posted teamId assertions below prove the wiring,
50
+ * not that choice.
51
+ *
52
+ * Only the network edge is mocked. API.get answers by URL from per-test
53
+ * fixtures and API.post records every call it is handed, so the assertions are
54
+ * on exactly what would have gone over the wire. Everything else - Card,
55
+ * Button, ConfirmModal, and the real react-select backed Dropdown - is the
56
+ * production component.
57
+ *
58
+ * The team Dropdown is deliberately NOT stubbed. It can be driven in jsdom the
59
+ * same way Tests/UI/Components/Dropdown.test.tsx drives it (ArrowDown on the
60
+ * combobox opens the menu, clicking a role="option" selects it), and keeping it
61
+ * real means the test also proves the card hands the Dropdown's onChange value
62
+ * through to the teamId it posts.
63
+ */
64
+
65
+ interface ApiRequestOptions {
66
+ url: { toString: () => string };
67
+ data?: JSONObject | undefined;
68
+ headers?: JSONObject | undefined;
69
+ }
70
+
71
+ interface RecordedPost {
72
+ url: string;
73
+ data: JSONObject;
74
+ headers: JSONObject;
75
+ }
76
+
77
+ type ApiGetHandler = (url: string) => Promise<unknown>;
78
+ type ApiPostHandler = (request: RecordedPost) => Promise<unknown>;
79
+
80
+ interface Fixtures {
81
+ slackChannels: Array<JSONObject>;
82
+ teams: Array<JSONObject>;
83
+ channelsByTeam: { [teamId: string]: Array<JSONObject> };
84
+ chats: Array<JSONObject>;
85
+ }
86
+
87
+ const COMMON_HEADERS: JSONObject = {
88
+ tenantid: "project-send-test-notification",
89
+ };
90
+
91
+ const GENERIC_ERROR_MESSAGE: string = "Something went wrong.";
92
+
93
+ let fixtures: Fixtures = {
94
+ slackChannels: [],
95
+ teams: [],
96
+ channelsByTeam: {},
97
+ chats: [],
98
+ };
99
+
100
+ let getRequests: Array<string> = [];
101
+ let postRequests: Array<RecordedPost> = [];
102
+
103
+ async function answerGetFromFixtures(url: string): Promise<unknown> {
104
+ const channelsMatch: RegExpMatchArray | null = url.match(
105
+ /\/microsoft-teams\/channels\?teamId=([^&]*)$/,
106
+ );
107
+
108
+ if (channelsMatch) {
109
+ const teamId: string = decodeURIComponent(channelsMatch[1] || "");
110
+ return { data: { channels: fixtures.channelsByTeam[teamId] || [] } };
111
+ }
112
+
113
+ if (url.endsWith("/microsoft-teams/teams")) {
114
+ return { data: { teams: fixtures.teams } };
115
+ }
116
+
117
+ if (url.endsWith("/microsoft-teams/chats")) {
118
+ return { data: { chats: fixtures.chats } };
119
+ }
120
+
121
+ if (url.endsWith("/slack/channels")) {
122
+ return { data: { channels: fixtures.slackChannels } };
123
+ }
124
+
125
+ throw new Error(`Unexpected GET in test: ${url}`);
126
+ }
127
+
128
+ async function answerPostWithSuccess(): Promise<unknown> {
129
+ /*
130
+ * A plain object is not an HTTPErrorResponse, so the button treats it as
131
+ * the empty success response the routes send.
132
+ */
133
+ return { data: {} };
134
+ }
135
+
136
+ let apiGetHandler: ApiGetHandler = answerGetFromFixtures;
137
+ let apiPostHandler: ApiPostHandler = answerPostWithSuccess;
138
+
139
+ jest.mock("../../../UI/Utils/API/API", () => {
140
+ return {
141
+ __esModule: true,
142
+ default: {
143
+ get: (options: ApiRequestOptions): Promise<unknown> => {
144
+ const url: string = options.url.toString();
145
+ getRequests.push(url);
146
+ return apiGetHandler(url);
147
+ },
148
+ post: (options: ApiRequestOptions): Promise<unknown> => {
149
+ const request: RecordedPost = {
150
+ url: options.url.toString(),
151
+ /*
152
+ * Snapshot the body as it was when the request went out. The card
153
+ * builds it inline on each render, so holding the live reference
154
+ * could hide a body that was wrong at send time.
155
+ */
156
+ data: JSON.parse(JSON.stringify(options.data || {})) as JSONObject,
157
+ headers: { ...(options.headers || {}) },
158
+ };
159
+ postRequests.push(request);
160
+ return apiPostHandler(request);
161
+ },
162
+ getFriendlyErrorMessage: (err: unknown): string => {
163
+ /*
164
+ * Error and HTTPErrorResponse both expose `message`; surface it so a
165
+ * test can see its own error text reach the modal.
166
+ */
167
+ if (
168
+ err &&
169
+ typeof err === "object" &&
170
+ typeof (err as { message?: unknown }).message === "string" &&
171
+ (err as { message: string }).message
172
+ ) {
173
+ return (err as { message: string }).message;
174
+ }
175
+
176
+ return GENERIC_ERROR_MESSAGE;
177
+ },
178
+ },
179
+ };
180
+ });
181
+
182
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
183
+ return {
184
+ __esModule: true,
185
+ default: {
186
+ getCommonHeaders: (): JSONObject => {
187
+ return { ...COMMON_HEADERS };
188
+ },
189
+ },
190
+ };
191
+ });
192
+
193
+ import SlackChannelsCard from "../../../../App/FeatureSet/Dashboard/src/Components/Slack/SlackChannelsCard";
194
+ import MicrosoftTeamsChannelsCard from "../../../../App/FeatureSet/Dashboard/src/Components/MicrosoftTeams/MicrosoftTeamsChannelsCard";
195
+ import MicrosoftTeamsChatsCard from "../../../../App/FeatureSet/Dashboard/src/Components/MicrosoftTeams/MicrosoftTeamsChatsCard";
196
+
197
+ const SEND_TEST_NAME: RegExp = /^Send test notification to /;
198
+
199
+ interface Deferred {
200
+ promise: Promise<unknown>;
201
+ resolve: (value: unknown) => void;
202
+ reject: (reason: unknown) => void;
203
+ }
204
+
205
+ function createDeferred(): Deferred {
206
+ const deferred: Partial<Deferred> = {};
207
+
208
+ deferred.promise = new Promise<unknown>(
209
+ (
210
+ resolve: (value: unknown) => void,
211
+ reject: (reason: unknown) => void,
212
+ ): void => {
213
+ deferred.resolve = resolve;
214
+ deferred.reject = reject;
215
+ },
216
+ );
217
+
218
+ return deferred as Deferred;
219
+ }
220
+
221
+ async function renderCard(element: React.ReactElement): Promise<void> {
222
+ // Every card fetches its list on mount; settle that inside act.
223
+ await act(async (): Promise<void> => {
224
+ render(element);
225
+ });
226
+ }
227
+
228
+ async function click(element: HTMLElement): Promise<void> {
229
+ await act(async (): Promise<void> => {
230
+ fireEvent.click(element);
231
+ });
232
+ }
233
+
234
+ function getRows(): Array<HTMLElement> {
235
+ return within(screen.getByRole("list")).getAllByRole("listitem");
236
+ }
237
+
238
+ function getRow(name: string): HTMLElement {
239
+ const row: HTMLElement | undefined = getRows().find(
240
+ (listItem: HTMLElement) => {
241
+ return within(listItem).queryByText(name) !== null;
242
+ },
243
+ );
244
+
245
+ if (!row) {
246
+ throw new Error(`No row named "${name}" is rendered.`);
247
+ }
248
+
249
+ return row;
250
+ }
251
+
252
+ function getSendTestButton(row: HTMLElement): HTMLElement {
253
+ return within(row).getByRole("button", { name: SEND_TEST_NAME });
254
+ }
255
+
256
+ function querySendTestButtons(): Array<HTMLElement> {
257
+ return screen.queryAllByRole("button", { name: SEND_TEST_NAME });
258
+ }
259
+
260
+ type RowResult = "sent" | "failed";
261
+
262
+ function getRowStatus(row: HTMLElement): HTMLElement {
263
+ /*
264
+ * Every row has exactly one live region, rendered even while idle: screen
265
+ * readers often skip a live region that appears already filled, so only
266
+ * its content changes when a result arrives.
267
+ */
268
+ return within(row).getByRole("status");
269
+ }
270
+
271
+ async function findRowResult(
272
+ rowName: string,
273
+ result: RowResult,
274
+ ): Promise<HTMLElement> {
275
+ const resultElement: HTMLElement = await within(getRow(rowName)).findByTestId(
276
+ `send-test-notification-${result}`,
277
+ );
278
+
279
+ // The result has to be inside the live region to be announced.
280
+ expect(getRowStatus(getRow(rowName))).toContainElement(resultElement);
281
+
282
+ return resultElement;
283
+ }
284
+
285
+ function expectRowIsIdle(row: HTMLElement): void {
286
+ expect(within(row).getAllByRole("status")).toHaveLength(1);
287
+ expect(getRowStatus(row)).toBeEmptyDOMElement();
288
+ expect(
289
+ within(row).queryByTestId("send-test-notification-sent"),
290
+ ).not.toBeInTheDocument();
291
+ expect(
292
+ within(row).queryByTestId("send-test-notification-failed"),
293
+ ).not.toBeInTheDocument();
294
+ expect(getSendTestButton(row)).toBeEnabled();
295
+ }
296
+
297
+ function expectEveryRowIsIdle(): void {
298
+ for (const row of getRows()) {
299
+ expectRowIsIdle(row);
300
+ }
301
+ }
302
+
303
+ function expectRowWrapsOnNarrowScreens(rowName: string): void {
304
+ const row: HTMLElement = getRow(rowName);
305
+
306
+ /*
307
+ * flex-wrap plus a floor on the name block is what lets the Send Test
308
+ * control drop onto its own line on a phone. With min-w-0 the name was
309
+ * squeezed to nothing instead.
310
+ */
311
+ expect(row).toHaveClass("flex", "flex-wrap", "items-center");
312
+
313
+ const nameElement: HTMLElement = within(row).getByText(rowName);
314
+ const nameBlock: HTMLElement | null = nameElement.parentElement;
315
+
316
+ // Long names still ellipsize inside the floor.
317
+ expect(nameElement).toHaveClass("truncate");
318
+ expect(nameBlock).toHaveClass("min-w-[8rem]", "flex-1");
319
+ expect(nameBlock).not.toHaveClass("min-w-0");
320
+
321
+ /*
322
+ * The control must be a direct flex item of the row: its ml-auto is what
323
+ * keeps it right-aligned once it has wrapped.
324
+ */
325
+ const control: Element | null = row.lastElementChild;
326
+
327
+ expect(control).not.toBeNull();
328
+ expect(control!.contains(getSendTestButton(row))).toBe(true);
329
+ expect(control).toHaveClass("ml-auto");
330
+ }
331
+
332
+ function expectExactlyOneSendTestButtonPerRow(): void {
333
+ const rows: Array<HTMLElement> = getRows();
334
+
335
+ expect(rows.length).toBeGreaterThan(0);
336
+
337
+ for (const row of rows) {
338
+ expect(
339
+ within(row).getAllByRole("button", { name: SEND_TEST_NAME }),
340
+ ).toHaveLength(1);
341
+ expect(
342
+ within(row).getAllByTestId("send-test-notification-button"),
343
+ ).toHaveLength(1);
344
+ expect(within(row).getAllByText("Send Test")).toHaveLength(1);
345
+ }
346
+
347
+ expect(querySendTestButtons()).toHaveLength(rows.length);
348
+ }
349
+
350
+ function expectButtonIsLastInRow(row: HTMLElement): void {
351
+ const lastChild: Element | null = row.lastElementChild;
352
+
353
+ expect(lastChild).not.toBeNull();
354
+ expect(lastChild!.contains(getSendTestButton(row))).toBe(true);
355
+ }
356
+
357
+ async function selectTeam(teamName: string): Promise<void> {
358
+ const combobox: HTMLElement = screen.getByRole("combobox");
359
+
360
+ await act(async (): Promise<void> => {
361
+ fireEvent.keyDown(combobox, { key: "ArrowDown", code: "ArrowDown" });
362
+ });
363
+
364
+ const option: HTMLElement = await screen.findByRole("option", {
365
+ name: teamName,
366
+ });
367
+
368
+ await click(option);
369
+ }
370
+
371
+ async function settlePendingWork(): Promise<void> {
372
+ /*
373
+ * A response that is correctly ignored changes nothing, so there is no DOM
374
+ * change to wait for. Let every queued continuation run before asserting
375
+ * that nothing happened.
376
+ */
377
+ await act(async (): Promise<void> => {
378
+ await new Promise<void>((resolve: () => void): void => {
379
+ setTimeout(resolve, 0);
380
+ });
381
+ });
382
+ }
383
+
384
+ /*
385
+ * Holds every Send Test POST open until the test settles it. The returned
386
+ * array fills in click order.
387
+ */
388
+ function holdEveryPost(): Array<Deferred> {
389
+ const held: Array<Deferred> = [];
390
+
391
+ apiPostHandler = (): Promise<unknown> => {
392
+ const deferred: Deferred = createDeferred();
393
+ held.push(deferred);
394
+ return deferred.promise;
395
+ };
396
+
397
+ return held;
398
+ }
399
+
400
+ type GetListControlsFunction = () => Array<HTMLElement>;
401
+
402
+ interface SendLockCase {
403
+ /*
404
+ * The card's controls that reload its list. Looked up again on every call:
405
+ * Button wraps a disabled button that has a tooltip in an extra span, so
406
+ * the element can be replaced between renders.
407
+ */
408
+ getListControls: GetListControlsFunction;
409
+ // Two rows of the loaded list.
410
+ rowNames: [string, string];
411
+ }
412
+
413
+ function expectListControlsEnabled(
414
+ getListControls: GetListControlsFunction,
415
+ isEnabled: boolean,
416
+ ): void {
417
+ const controls: Array<HTMLElement> = getListControls();
418
+
419
+ expect(controls.length).toBeGreaterThan(0);
420
+
421
+ for (const control of controls) {
422
+ if (isEnabled) {
423
+ expect(control).toBeEnabled();
424
+ } else {
425
+ expect(control).toBeDisabled();
426
+ }
427
+ }
428
+ }
429
+
430
+ async function expectListControlsLockedUntilSendSucceeds(
431
+ testCase: SendLockCase,
432
+ ): Promise<void> {
433
+ const held: Array<Deferred> = holdEveryPost();
434
+ const rowName: string = testCase.rowNames[0];
435
+
436
+ expectListControlsEnabled(testCase.getListControls, true);
437
+
438
+ await click(getSendTestButton(getRow(rowName)));
439
+
440
+ expect(held).toHaveLength(1);
441
+ expectListControlsEnabled(testCase.getListControls, false);
442
+
443
+ // Trying the locked controls anyway neither reloads nor opens anything.
444
+ const getCountBeforeTrying: number = getRequests.length;
445
+
446
+ for (const control of testCase.getListControls()) {
447
+ await click(control);
448
+ await act(async (): Promise<void> => {
449
+ fireEvent.keyDown(control, { key: "ArrowDown", code: "ArrowDown" });
450
+ });
451
+ }
452
+
453
+ expect(getRequests).toHaveLength(getCountBeforeTrying);
454
+ expect(screen.queryByRole("option")).not.toBeInTheDocument();
455
+ // Still the same list, with the send still in flight on its row.
456
+ expect(getSendTestButton(getRow(rowName))).toBeDisabled();
457
+
458
+ await act(async (): Promise<void> => {
459
+ held[0]!.resolve({ data: {} });
460
+ });
461
+
462
+ await findRowResult(rowName, "sent");
463
+ expectListControlsEnabled(testCase.getListControls, true);
464
+ }
465
+
466
+ async function expectListControlsLockedUntilSendFails(
467
+ testCase: SendLockCase,
468
+ ): Promise<void> {
469
+ const held: Array<Deferred> = holdEveryPost();
470
+ const rowName: string = testCase.rowNames[0];
471
+
472
+ await click(getSendTestButton(getRow(rowName)));
473
+
474
+ expect(held).toHaveLength(1);
475
+ expectListControlsEnabled(testCase.getListControls, false);
476
+
477
+ await act(async (): Promise<void> => {
478
+ held[0]!.reject(new Error("not_in_channel"));
479
+ });
480
+
481
+ await findRowResult(rowName, "failed");
482
+
483
+ /*
484
+ * Unlocked as soon as the send settles, while the failure dialog is still
485
+ * open - not only once the dialog is closed.
486
+ */
487
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
488
+ expectListControlsEnabled(testCase.getListControls, true);
489
+ }
490
+
491
+ async function expectListControlsLockedUntilEverySendSettles(
492
+ testCase: SendLockCase,
493
+ ): Promise<void> {
494
+ const held: Array<Deferred> = holdEveryPost();
495
+ const firstRowName: string = testCase.rowNames[0];
496
+ const secondRowName: string = testCase.rowNames[1];
497
+
498
+ await click(getSendTestButton(getRow(firstRowName)));
499
+ await click(getSendTestButton(getRow(secondRowName)));
500
+
501
+ expect(held).toHaveLength(2);
502
+ expect(postRequests).toHaveLength(2);
503
+ expectListControlsEnabled(testCase.getListControls, false);
504
+
505
+ /*
506
+ * The later click settles first, so the lock has to be a count of sends in
507
+ * flight rather than a flag that follows the most recent one.
508
+ */
509
+ await act(async (): Promise<void> => {
510
+ held[1]!.resolve({ data: {} });
511
+ });
512
+
513
+ await findRowResult(secondRowName, "sent");
514
+ expect(getSendTestButton(getRow(firstRowName))).toBeDisabled();
515
+ expectListControlsEnabled(testCase.getListControls, false);
516
+
517
+ await act(async (): Promise<void> => {
518
+ held[0]!.reject(new Error("channel_not_found"));
519
+ });
520
+
521
+ await findRowResult(firstRowName, "failed");
522
+ expectListControlsEnabled(testCase.getListControls, true);
523
+ }
524
+
525
+ beforeEach(() => {
526
+ fixtures = {
527
+ slackChannels: [],
528
+ teams: [],
529
+ channelsByTeam: {},
530
+ chats: [],
531
+ };
532
+ getRequests = [];
533
+ postRequests = [];
534
+ apiGetHandler = answerGetFromFixtures;
535
+ apiPostHandler = answerPostWithSuccess;
536
+ });
537
+
538
+ describe("SlackChannelsCard Send Test", () => {
539
+ const SLACK_CHANNELS: Array<JSONObject> = [
540
+ { id: "C0ALERTS", name: "alerts" },
541
+ { id: "C0ONCALL", name: "on-call" },
542
+ { id: "C0RELEASES", name: "releases" },
543
+ ];
544
+
545
+ async function renderSlackCard(
546
+ channels: Array<JSONObject> = SLACK_CHANNELS,
547
+ ): Promise<void> {
548
+ fixtures.slackChannels = channels;
549
+ await renderCard(<SlackChannelsCard />);
550
+ }
551
+
552
+ async function waitForSlackChannels(count: number): Promise<void> {
553
+ await screen.findByText(`Channels (${count})`);
554
+ }
555
+
556
+ test("the card description tells the user about Send Test and keeps the existing copy", async () => {
557
+ await renderSlackCard();
558
+ await waitForSlackChannels(3);
559
+
560
+ const description: HTMLElement = screen.getByTestId("card-description");
561
+
562
+ expect(description).toHaveTextContent(
563
+ "Use Send Test to confirm OneUptime can post to a channel.",
564
+ );
565
+ expect(description).toHaveTextContent(
566
+ "Channels OneUptime can see in your Slack workspace.",
567
+ );
568
+ expect(description).toHaveTextContent(
569
+ "Use these names when a notification rule posts to an existing channel.",
570
+ );
571
+ });
572
+
573
+ test("every channel row has exactly one Send Test button, placed last in the row", async () => {
574
+ await renderSlackCard();
575
+ await waitForSlackChannels(3);
576
+
577
+ expect(getRows()).toHaveLength(3);
578
+ expectExactlyOneSendTestButtonPerRow();
579
+
580
+ for (const name of ["alerts", "on-call", "releases"]) {
581
+ expectButtonIsLastInRow(getRow(name));
582
+ expectRowIsIdle(getRow(name));
583
+ }
584
+ });
585
+
586
+ test("the button's accessible name names the channel with a leading #", async () => {
587
+ await renderSlackCard();
588
+ await waitForSlackChannels(3);
589
+
590
+ expect(getSendTestButton(getRow("alerts"))).toHaveAccessibleName(
591
+ "Send test notification to #alerts",
592
+ );
593
+ expect(getSendTestButton(getRow("on-call"))).toHaveAccessibleName(
594
+ "Send test notification to #on-call",
595
+ );
596
+ expect(getSendTestButton(getRow("releases"))).toHaveAccessibleName(
597
+ "Send test notification to #releases",
598
+ );
599
+ });
600
+
601
+ test("loading the card posts nothing: a test is only sent on click", async () => {
602
+ await renderSlackCard();
603
+ await waitForSlackChannels(3);
604
+
605
+ expect(postRequests).toHaveLength(0);
606
+ expect(
607
+ getRequests.filter((url: string) => {
608
+ return url.endsWith("/slack/channels");
609
+ }),
610
+ ).toHaveLength(1);
611
+ });
612
+
613
+ test("clicking Send Test posts only { channelId } to /slack/channels/test with the project headers", async () => {
614
+ await renderSlackCard();
615
+ await waitForSlackChannels(3);
616
+
617
+ await click(getSendTestButton(getRow("on-call")));
618
+
619
+ expect(postRequests).toHaveLength(1);
620
+ expect(postRequests[0]!.url).toMatch(/\/slack\/channels\/test$/);
621
+ expect(postRequests[0]!.data).toEqual({ channelId: "C0ONCALL" });
622
+ expect(postRequests[0]!.headers).toEqual(COMMON_HEADERS);
623
+ });
624
+
625
+ test("each row posts its own channel id", async () => {
626
+ await renderSlackCard();
627
+ await waitForSlackChannels(3);
628
+
629
+ await click(getSendTestButton(getRow("releases")));
630
+ await click(getSendTestButton(getRow("alerts")));
631
+ await click(getSendTestButton(getRow("on-call")));
632
+
633
+ expect(
634
+ postRequests.map((request: RecordedPost) => {
635
+ return request.data;
636
+ }),
637
+ ).toEqual([
638
+ { channelId: "C0RELEASES" },
639
+ { channelId: "C0ALERTS" },
640
+ { channelId: "C0ONCALL" },
641
+ ]);
642
+
643
+ for (const request of postRequests) {
644
+ expect(request.url).toMatch(/\/slack\/channels\/test$/);
645
+ }
646
+ });
647
+
648
+ test("a successful send marks only that row as Sent, with a tooltip naming the channel and Slack", async () => {
649
+ await renderSlackCard();
650
+ await waitForSlackChannels(3);
651
+
652
+ await click(getSendTestButton(getRow("alerts")));
653
+
654
+ const sent: HTMLElement = await findRowResult("alerts", "sent");
655
+
656
+ expect(sent).toHaveTextContent("Sent");
657
+ expect(sent).toHaveAttribute(
658
+ "title",
659
+ "Test notification sent to #alerts. Check Slack to confirm it arrived.",
660
+ );
661
+ expect(getSendTestButton(getRow("alerts"))).toBeEnabled();
662
+
663
+ expectRowIsIdle(getRow("on-call"));
664
+ expectRowIsIdle(getRow("releases"));
665
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
666
+ });
667
+
668
+ test("clicking one row does not change the other rows while the send is in flight", async () => {
669
+ const deferred: Deferred = createDeferred();
670
+ apiPostHandler = (): Promise<unknown> => {
671
+ return deferred.promise;
672
+ };
673
+
674
+ await renderSlackCard();
675
+ await waitForSlackChannels(3);
676
+
677
+ await click(getSendTestButton(getRow("alerts")));
678
+
679
+ // The click sends straight away: no confirmation dialog in between.
680
+ expect(postRequests).toHaveLength(1);
681
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
682
+
683
+ expect(getSendTestButton(getRow("alerts"))).toBeDisabled();
684
+ expectRowIsIdle(getRow("on-call"));
685
+ expectRowIsIdle(getRow("releases"));
686
+
687
+ await act(async (): Promise<void> => {
688
+ deferred.resolve({ data: {} });
689
+ });
690
+
691
+ await waitFor(() => {
692
+ expect(getRowStatus(getRow("alerts"))).toHaveTextContent("Sent");
693
+ });
694
+ expectRowIsIdle(getRow("on-call"));
695
+ expectRowIsIdle(getRow("releases"));
696
+ });
697
+
698
+ test("a second click while the send is in flight does not post again", async () => {
699
+ const deferred: Deferred = createDeferred();
700
+ apiPostHandler = (): Promise<unknown> => {
701
+ return deferred.promise;
702
+ };
703
+
704
+ await renderSlackCard();
705
+ await waitForSlackChannels(3);
706
+
707
+ const button: HTMLElement = getSendTestButton(getRow("alerts"));
708
+
709
+ await click(button);
710
+ await click(getSendTestButton(getRow("alerts")));
711
+
712
+ expect(postRequests).toHaveLength(1);
713
+
714
+ await act(async (): Promise<void> => {
715
+ deferred.resolve({ data: {} });
716
+ });
717
+
718
+ await waitFor(() => {
719
+ expect(getRowStatus(getRow("alerts"))).toHaveTextContent("Sent");
720
+ });
721
+ expect(postRequests).toHaveLength(1);
722
+ });
723
+
724
+ test("a failing send opens the failure modal with the server's error and marks the row Failed", async () => {
725
+ const errorMessage: string =
726
+ "Could not send the test notification to #alerts. not_in_channel";
727
+
728
+ apiPostHandler = async (): Promise<unknown> => {
729
+ throw new Error(errorMessage);
730
+ };
731
+
732
+ await renderSlackCard();
733
+ await waitForSlackChannels(3);
734
+
735
+ await click(getSendTestButton(getRow("alerts")));
736
+
737
+ const dialog: HTMLElement = await screen.findByRole("dialog");
738
+
739
+ expect(within(dialog).getByTestId("modal-title")).toHaveTextContent(
740
+ "Test Notification Failed",
741
+ );
742
+ expect(
743
+ within(dialog).getByTestId("confirm-modal-description"),
744
+ ).toHaveTextContent(
745
+ "OneUptime could not send a test notification to #alerts in Slack.",
746
+ );
747
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(errorMessage);
748
+
749
+ const failed: HTMLElement = await findRowResult("alerts", "failed");
750
+ expect(failed).toHaveTextContent("Failed");
751
+ expect(failed).toHaveAttribute("title", errorMessage);
752
+
753
+ expectRowIsIdle(getRow("on-call"));
754
+ expectRowIsIdle(getRow("releases"));
755
+ });
756
+
757
+ test("closing the failure modal keeps the row's Failed badge and a retry that succeeds turns it into Sent", async () => {
758
+ let shouldFail: boolean = true;
759
+
760
+ apiPostHandler = async (): Promise<unknown> => {
761
+ if (shouldFail) {
762
+ throw new Error("channel_not_found");
763
+ }
764
+
765
+ return { data: {} };
766
+ };
767
+
768
+ await renderSlackCard();
769
+ await waitForSlackChannels(3);
770
+
771
+ await click(getSendTestButton(getRow("releases")));
772
+ await screen.findByRole("dialog");
773
+
774
+ await click(screen.getByTestId("modal-footer-submit-button"));
775
+
776
+ await waitFor(() => {
777
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
778
+ });
779
+ expect(getRowStatus(getRow("releases"))).toHaveTextContent("Failed");
780
+
781
+ shouldFail = false;
782
+ await click(getSendTestButton(getRow("releases")));
783
+
784
+ await waitFor(() => {
785
+ expect(getRowStatus(getRow("releases"))).toHaveTextContent("Sent");
786
+ });
787
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
788
+ expect(postRequests).toHaveLength(2);
789
+ expect(postRequests[1]!.data).toEqual({ channelId: "C0RELEASES" });
790
+ });
791
+
792
+ test("channels the card drops for a missing id or name get no Send Test button", async () => {
793
+ await renderSlackCard([
794
+ { id: "C0ALERTS", name: "alerts" },
795
+ { id: "", name: "no-id" },
796
+ { id: "C0NONAME", name: "" },
797
+ ]);
798
+ await waitForSlackChannels(1);
799
+
800
+ expect(getRows()).toHaveLength(1);
801
+ expect(querySendTestButtons()).toHaveLength(1);
802
+ expect(querySendTestButtons()[0]).toHaveAccessibleName(
803
+ "Send test notification to #alerts",
804
+ );
805
+ });
806
+
807
+ test("an empty channel list renders no Send Test buttons", async () => {
808
+ await renderSlackCard([]);
809
+
810
+ await screen.findByText("No channels found");
811
+
812
+ expect(querySendTestButtons()).toHaveLength(0);
813
+ expect(
814
+ screen.queryByTestId("send-test-notification-button"),
815
+ ).not.toBeInTheDocument();
816
+ });
817
+
818
+ test("a channel list that fails to load renders no Send Test buttons", async () => {
819
+ apiGetHandler = async (): Promise<unknown> => {
820
+ throw new Error("Slack is not connected.");
821
+ };
822
+
823
+ await renderSlackCard();
824
+
825
+ await screen.findByText("Slack is not connected.");
826
+
827
+ expect(querySendTestButtons()).toHaveLength(0);
828
+ });
829
+
830
+ test("Refresh Channels re-reads the list and never posts a test", async () => {
831
+ await renderSlackCard();
832
+ await waitForSlackChannels(3);
833
+
834
+ await click(screen.getByRole("button", { name: /Refresh Channels/i }));
835
+ await waitForSlackChannels(3);
836
+
837
+ expect(
838
+ getRequests.filter((url: string) => {
839
+ return url.endsWith("/slack/channels");
840
+ }),
841
+ ).toHaveLength(2);
842
+ expect(postRequests).toHaveLength(0);
843
+ expectExactlyOneSendTestButtonPerRow();
844
+ });
845
+
846
+ test("channel rows can wrap on a narrow screen: the row is flex-wrap and the name keeps an 8rem floor", async () => {
847
+ await renderSlackCard();
848
+ await waitForSlackChannels(3);
849
+
850
+ for (const name of ["alerts", "on-call", "releases"]) {
851
+ expectRowWrapsOnNarrowScreens(name);
852
+ }
853
+ });
854
+
855
+ describe("while a Send Test is in flight", () => {
856
+ const slackSendLockCase: SendLockCase = {
857
+ getListControls: (): Array<HTMLElement> => {
858
+ return [screen.getByRole("button", { name: /Refresh Channels/i })];
859
+ },
860
+ rowNames: ["alerts", "releases"],
861
+ };
862
+
863
+ test("Refresh Channels is disabled, does not reload when clicked, and is re-enabled once the send succeeds", async () => {
864
+ await renderSlackCard();
865
+ await waitForSlackChannels(3);
866
+
867
+ await expectListControlsLockedUntilSendSucceeds(slackSendLockCase);
868
+ });
869
+
870
+ test("Refresh Channels is re-enabled once the send fails", async () => {
871
+ await renderSlackCard();
872
+ await waitForSlackChannels(3);
873
+
874
+ await expectListControlsLockedUntilSendFails(slackSendLockCase);
875
+ });
876
+
877
+ test("with two sends in flight, Refresh Channels stays disabled after the first settles and is re-enabled after the second", async () => {
878
+ await renderSlackCard();
879
+ await waitForSlackChannels(3);
880
+
881
+ await expectListControlsLockedUntilEverySendSettles(slackSendLockCase);
882
+ });
883
+ });
884
+ });
885
+
886
+ describe("MicrosoftTeamsChannelsCard Send Test", () => {
887
+ /*
888
+ * Deliberately out of alphabetical order: the card sorts teams by name and
889
+ * auto-selects the first, so "Alpha Team" (team-alpha) is the initial team
890
+ * even though "Beta Team" comes first from the API.
891
+ *
892
+ * Both teams also return a channel with the SAME id ("General"). After a
893
+ * team switch that row looks the same as before, so a stale result on it,
894
+ * or a body that still names the old team, would be easy to miss. The row
895
+ * starts idle because the loader unmounts the list during the reload; see
896
+ * the header comment for what that means for the row key.
897
+ */
898
+ const TEAMS: Array<JSONObject> = [
899
+ { id: "team-beta", name: "Beta Team" },
900
+ { id: "team-alpha", name: "Alpha Team" },
901
+ ];
902
+
903
+ const CHANNELS_BY_TEAM: { [teamId: string]: Array<JSONObject> } = {
904
+ "team-alpha": [
905
+ { id: "19:shared@thread.tacv2", name: "General" },
906
+ { id: "19:alpha-ops@thread.tacv2", name: "Alpha Ops" },
907
+ ],
908
+ "team-beta": [
909
+ { id: "19:shared@thread.tacv2", name: "General" },
910
+ { id: "19:beta-oncall@thread.tacv2", name: "Beta On-Call" },
911
+ { id: "19:beta-releases@thread.tacv2", name: "Beta Releases" },
912
+ ],
913
+ };
914
+
915
+ async function renderTeamsChannelsCard(options?: {
916
+ teams?: Array<JSONObject>;
917
+ channelsByTeam?: { [teamId: string]: Array<JSONObject> };
918
+ }): Promise<void> {
919
+ fixtures.teams = options?.teams || TEAMS;
920
+ fixtures.channelsByTeam = options?.channelsByTeam || CHANNELS_BY_TEAM;
921
+ await renderCard(<MicrosoftTeamsChannelsCard />);
922
+ }
923
+
924
+ async function waitForTeamsChannels(count: number): Promise<void> {
925
+ await screen.findByText(`Channels (${count})`);
926
+ }
927
+
928
+ test("the card description tells the user about Send Test and keeps the existing copy", async () => {
929
+ await renderTeamsChannelsCard();
930
+ await waitForTeamsChannels(2);
931
+
932
+ const description: HTMLElement = screen.getByTestId("card-description");
933
+
934
+ expect(description).toHaveTextContent(
935
+ "Use Send Test to confirm OneUptime can post to a channel.",
936
+ );
937
+ expect(description).toHaveTextContent(
938
+ "Browse the channels OneUptime can see in your teams.",
939
+ );
940
+ expect(description).toHaveTextContent(
941
+ "Use these names when a notification rule posts to an existing channel.",
942
+ );
943
+ });
944
+
945
+ test("every channel row of the auto-selected team has exactly one Send Test button, placed last in the row", async () => {
946
+ await renderTeamsChannelsCard();
947
+ await waitForTeamsChannels(2);
948
+
949
+ expect(getRows()).toHaveLength(2);
950
+ expectExactlyOneSendTestButtonPerRow();
951
+
952
+ for (const name of ["General", "Alpha Ops"]) {
953
+ expectButtonIsLastInRow(getRow(name));
954
+ expectRowIsIdle(getRow(name));
955
+ }
956
+ });
957
+
958
+ test("the button's accessible name names the channel (without a Slack-style #)", async () => {
959
+ await renderTeamsChannelsCard();
960
+ await waitForTeamsChannels(2);
961
+
962
+ expect(getSendTestButton(getRow("General"))).toHaveAccessibleName(
963
+ "Send test notification to General",
964
+ );
965
+ expect(getSendTestButton(getRow("Alpha Ops"))).toHaveAccessibleName(
966
+ "Send test notification to Alpha Ops",
967
+ );
968
+ });
969
+
970
+ test("loading teams and channels posts nothing", async () => {
971
+ await renderTeamsChannelsCard();
972
+ await waitForTeamsChannels(2);
973
+
974
+ expect(postRequests).toHaveLength(0);
975
+ });
976
+
977
+ test("clicking Send Test posts { teamId, channelId } for the selected team to /microsoft-teams/channels/test", async () => {
978
+ await renderTeamsChannelsCard();
979
+ await waitForTeamsChannels(2);
980
+
981
+ await click(getSendTestButton(getRow("Alpha Ops")));
982
+
983
+ expect(postRequests).toHaveLength(1);
984
+ expect(postRequests[0]!.url).toMatch(/\/microsoft-teams\/channels\/test$/);
985
+ expect(postRequests[0]!.data).toEqual({
986
+ teamId: "team-alpha",
987
+ channelId: "19:alpha-ops@thread.tacv2",
988
+ });
989
+ expect(postRequests[0]!.headers).toEqual(COMMON_HEADERS);
990
+ });
991
+
992
+ test("a successful send marks only that row as Sent, with a tooltip naming the channel and Microsoft Teams", async () => {
993
+ await renderTeamsChannelsCard();
994
+ await waitForTeamsChannels(2);
995
+
996
+ await click(getSendTestButton(getRow("General")));
997
+
998
+ const sent: HTMLElement = await findRowResult("General", "sent");
999
+
1000
+ expect(sent).toHaveTextContent("Sent");
1001
+ expect(sent).toHaveAttribute(
1002
+ "title",
1003
+ "Test notification sent to General. Check Microsoft Teams to confirm it arrived.",
1004
+ );
1005
+ expectRowIsIdle(getRow("Alpha Ops"));
1006
+ });
1007
+
1008
+ test("after switching team the reloaded rows start idle and post the new teamId, including the channel id both teams share", async () => {
1009
+ await renderTeamsChannelsCard();
1010
+ await waitForTeamsChannels(2);
1011
+
1012
+ await click(getSendTestButton(getRow("General")));
1013
+
1014
+ await waitFor(() => {
1015
+ expect(getRowStatus(getRow("General"))).toHaveTextContent("Sent");
1016
+ });
1017
+ expect(postRequests[0]!.data).toEqual({
1018
+ teamId: "team-alpha",
1019
+ channelId: "19:shared@thread.tacv2",
1020
+ });
1021
+
1022
+ await selectTeam("Beta Team");
1023
+ await waitForTeamsChannels(3);
1024
+
1025
+ expect(
1026
+ getRequests.some((url: string) => {
1027
+ return url.endsWith("/microsoft-teams/channels?teamId=team-beta");
1028
+ }),
1029
+ ).toBe(true);
1030
+
1031
+ /*
1032
+ * Same channel id, new team: the row starts idle. The loader replaced the
1033
+ * list while Beta's channels loaded, so every row was remounted.
1034
+ */
1035
+ expectRowIsIdle(getRow("General"));
1036
+ expect(
1037
+ screen.queryByTestId("send-test-notification-sent"),
1038
+ ).not.toBeInTheDocument();
1039
+ expectEveryRowIsIdle();
1040
+ expectExactlyOneSendTestButtonPerRow();
1041
+
1042
+ await click(getSendTestButton(getRow("General")));
1043
+ await click(getSendTestButton(getRow("Beta On-Call")));
1044
+
1045
+ expect(postRequests).toHaveLength(3);
1046
+ expect(postRequests[1]!.data).toEqual({
1047
+ teamId: "team-beta",
1048
+ channelId: "19:shared@thread.tacv2",
1049
+ });
1050
+ expect(postRequests[2]!.data).toEqual({
1051
+ teamId: "team-beta",
1052
+ channelId: "19:beta-oncall@thread.tacv2",
1053
+ });
1054
+ for (const request of postRequests) {
1055
+ expect(request.url).toMatch(/\/microsoft-teams\/channels\/test$/);
1056
+ }
1057
+ });
1058
+
1059
+ test("switching back to the first team reloads its rows idle and posts the first team again", async () => {
1060
+ await renderTeamsChannelsCard();
1061
+ await waitForTeamsChannels(2);
1062
+
1063
+ await click(getSendTestButton(getRow("Alpha Ops")));
1064
+ await waitFor(() => {
1065
+ expect(getRowStatus(getRow("Alpha Ops"))).toHaveTextContent("Sent");
1066
+ });
1067
+
1068
+ await selectTeam("Beta Team");
1069
+ await waitForTeamsChannels(3);
1070
+
1071
+ await click(getSendTestButton(getRow("Beta Releases")));
1072
+ await waitFor(() => {
1073
+ expect(getRowStatus(getRow("Beta Releases"))).toHaveTextContent("Sent");
1074
+ });
1075
+
1076
+ await selectTeam("Alpha Team");
1077
+ await waitForTeamsChannels(2);
1078
+
1079
+ expectRowIsIdle(getRow("Alpha Ops"));
1080
+ expectRowIsIdle(getRow("General"));
1081
+ expect(
1082
+ screen.queryByTestId("send-test-notification-sent"),
1083
+ ).not.toBeInTheDocument();
1084
+
1085
+ await click(getSendTestButton(getRow("General")));
1086
+
1087
+ expect(postRequests[postRequests.length - 1]!.data).toEqual({
1088
+ teamId: "team-alpha",
1089
+ channelId: "19:shared@thread.tacv2",
1090
+ });
1091
+ });
1092
+
1093
+ test("clicking one row does not change the other rows while the send is in flight", async () => {
1094
+ const deferred: Deferred = createDeferred();
1095
+ apiPostHandler = (): Promise<unknown> => {
1096
+ return deferred.promise;
1097
+ };
1098
+
1099
+ await renderTeamsChannelsCard();
1100
+ await waitForTeamsChannels(2);
1101
+
1102
+ await click(getSendTestButton(getRow("Alpha Ops")));
1103
+
1104
+ expect(getSendTestButton(getRow("Alpha Ops"))).toBeDisabled();
1105
+ expectRowIsIdle(getRow("General"));
1106
+
1107
+ await act(async (): Promise<void> => {
1108
+ deferred.resolve({ data: {} });
1109
+ });
1110
+
1111
+ await waitFor(() => {
1112
+ expect(getRowStatus(getRow("Alpha Ops"))).toHaveTextContent("Sent");
1113
+ });
1114
+ expectRowIsIdle(getRow("General"));
1115
+ });
1116
+
1117
+ test("a failing send opens the failure modal naming the channel and Microsoft Teams with the server's error", async () => {
1118
+ const errorMessage: string =
1119
+ 'Could not send the test notification to "General". The OneUptime app is not installed in this team.';
1120
+
1121
+ apiPostHandler = async (): Promise<unknown> => {
1122
+ return new HTTPErrorResponse(400, { message: errorMessage }, {});
1123
+ };
1124
+
1125
+ await renderTeamsChannelsCard();
1126
+ await waitForTeamsChannels(2);
1127
+
1128
+ await click(getSendTestButton(getRow("General")));
1129
+
1130
+ const dialog: HTMLElement = await screen.findByRole("dialog");
1131
+
1132
+ expect(within(dialog).getByTestId("modal-title")).toHaveTextContent(
1133
+ "Test Notification Failed",
1134
+ );
1135
+ expect(
1136
+ within(dialog).getByTestId("confirm-modal-description"),
1137
+ ).toHaveTextContent(
1138
+ "OneUptime could not send a test notification to General in Microsoft Teams.",
1139
+ );
1140
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(errorMessage);
1141
+
1142
+ expect(getRowStatus(getRow("General"))).toHaveTextContent("Failed");
1143
+ expectRowIsIdle(getRow("Alpha Ops"));
1144
+
1145
+ // The dialog only reports an outcome, so "Close" is its one way out.
1146
+ expect(
1147
+ within(dialog).queryByTestId("close-button"),
1148
+ ).not.toBeInTheDocument();
1149
+ expect(within(dialog).getAllByRole("button")).toHaveLength(1);
1150
+
1151
+ await click(within(dialog).getByRole("button", { name: "Close" }));
1152
+
1153
+ await waitFor(() => {
1154
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
1155
+ });
1156
+ expect(getRowStatus(getRow("General"))).toHaveTextContent("Failed");
1157
+ });
1158
+
1159
+ test("no teams renders no Send Test buttons", async () => {
1160
+ await renderTeamsChannelsCard({ teams: [], channelsByTeam: {} });
1161
+
1162
+ await screen.findByText("No teams found");
1163
+
1164
+ expect(querySendTestButtons()).toHaveLength(0);
1165
+ expect(postRequests).toHaveLength(0);
1166
+ });
1167
+
1168
+ test("a team with no channels renders no Send Test buttons", async () => {
1169
+ await renderTeamsChannelsCard({
1170
+ teams: [{ id: "team-empty", name: "Empty Team" }],
1171
+ channelsByTeam: { "team-empty": [] },
1172
+ });
1173
+
1174
+ await screen.findByText("No channels found in this team.");
1175
+
1176
+ expect(querySendTestButtons()).toHaveLength(0);
1177
+ });
1178
+
1179
+ test("switching to a team with no channels removes every Send Test button", async () => {
1180
+ await renderTeamsChannelsCard({
1181
+ teams: TEAMS,
1182
+ channelsByTeam: {
1183
+ "team-alpha": CHANNELS_BY_TEAM["team-alpha"]!,
1184
+ "team-beta": [],
1185
+ },
1186
+ });
1187
+ await waitForTeamsChannels(2);
1188
+
1189
+ expect(querySendTestButtons()).toHaveLength(2);
1190
+
1191
+ await selectTeam("Beta Team");
1192
+ await screen.findByText("No channels found in this team.");
1193
+
1194
+ expect(querySendTestButtons()).toHaveLength(0);
1195
+ });
1196
+
1197
+ test("channel rows can wrap on a narrow screen: the row is flex-wrap and the name keeps an 8rem floor", async () => {
1198
+ await renderTeamsChannelsCard();
1199
+ await waitForTeamsChannels(2);
1200
+
1201
+ for (const name of ["General", "Alpha Ops"]) {
1202
+ expectRowWrapsOnNarrowScreens(name);
1203
+ }
1204
+ });
1205
+
1206
+ describe("while a Send Test is in flight", () => {
1207
+ /*
1208
+ * Picking another team reloads the list exactly like Refresh does, so the
1209
+ * team dropdown is locked along with it.
1210
+ */
1211
+ const teamsChannelsSendLockCase: SendLockCase = {
1212
+ getListControls: (): Array<HTMLElement> => {
1213
+ return [
1214
+ screen.getByRole("button", { name: /Refresh Channels/i }),
1215
+ /*
1216
+ * hidden: true because a disabled react-select hides its input
1217
+ * with visibility: hidden, which takes the combobox out of the
1218
+ * accessibility tree. It is still the element that carries the
1219
+ * disabled attribute and receives the keyboard.
1220
+ */
1221
+ screen.getByRole("combobox", { hidden: true }),
1222
+ ];
1223
+ },
1224
+ rowNames: ["Alpha Ops", "General"],
1225
+ };
1226
+
1227
+ test("Refresh Channels and the team dropdown are disabled, do nothing when tried, and are re-enabled once the send succeeds", async () => {
1228
+ await renderTeamsChannelsCard();
1229
+ await waitForTeamsChannels(2);
1230
+
1231
+ await expectListControlsLockedUntilSendSucceeds(
1232
+ teamsChannelsSendLockCase,
1233
+ );
1234
+ });
1235
+
1236
+ test("Refresh Channels and the team dropdown are re-enabled once the send fails", async () => {
1237
+ await renderTeamsChannelsCard();
1238
+ await waitForTeamsChannels(2);
1239
+
1240
+ await expectListControlsLockedUntilSendFails(teamsChannelsSendLockCase);
1241
+ });
1242
+
1243
+ test("with two sends in flight, Refresh Channels and the team dropdown stay disabled after the first settles and are re-enabled after the second", async () => {
1244
+ await renderTeamsChannelsCard();
1245
+ await waitForTeamsChannels(2);
1246
+
1247
+ await expectListControlsLockedUntilEverySendSettles(
1248
+ teamsChannelsSendLockCase,
1249
+ );
1250
+ });
1251
+
1252
+ test("once the send has settled the team can be switched again, and the new team's rows post the new teamId", async () => {
1253
+ await renderTeamsChannelsCard();
1254
+ await waitForTeamsChannels(2);
1255
+
1256
+ await expectListControlsLockedUntilSendSucceeds(
1257
+ teamsChannelsSendLockCase,
1258
+ );
1259
+
1260
+ apiPostHandler = answerPostWithSuccess;
1261
+
1262
+ await selectTeam("Beta Team");
1263
+ await waitForTeamsChannels(3);
1264
+
1265
+ await click(getSendTestButton(getRow("Beta On-Call")));
1266
+
1267
+ expect(postRequests[postRequests.length - 1]!.data).toEqual({
1268
+ teamId: "team-beta",
1269
+ channelId: "19:beta-oncall@thread.tacv2",
1270
+ });
1271
+ });
1272
+ });
1273
+
1274
+ describe("channel loads that finish out of order", () => {
1275
+ /*
1276
+ * Three teams, so the test can pick Beta and then Gamma while Alpha's
1277
+ * list is showing, and hold both loads open. Beta and Gamma share the
1278
+ * "General" channel id, so only the teamId in the body tells a Gamma
1279
+ * "General" from a Beta one.
1280
+ */
1281
+ const RACE_TEAMS: Array<JSONObject> = [
1282
+ { id: "team-gamma", name: "Gamma Team" },
1283
+ { id: "team-beta", name: "Beta Team" },
1284
+ { id: "team-alpha", name: "Alpha Team" },
1285
+ ];
1286
+
1287
+ const RACE_CHANNELS_BY_TEAM: { [teamId: string]: Array<JSONObject> } = {
1288
+ "team-alpha": CHANNELS_BY_TEAM["team-alpha"]!,
1289
+ "team-beta": CHANNELS_BY_TEAM["team-beta"]!,
1290
+ "team-gamma": [
1291
+ { id: "19:shared@thread.tacv2", name: "General" },
1292
+ { id: "19:gamma-deploys@thread.tacv2", name: "Gamma Deploys" },
1293
+ ],
1294
+ };
1295
+
1296
+ const BETA_LOAD_ERROR: string = "Could not load the channels of Beta Team.";
1297
+
1298
+ type HeldChannelLoads = { [teamId: string]: Deferred };
1299
+
1300
+ /*
1301
+ * Holds the channel list GET of each given team open until the test
1302
+ * settles it. Every other GET is still answered from the fixtures.
1303
+ */
1304
+ function holdChannelLoads(teamIds: Array<string>): HeldChannelLoads {
1305
+ const held: HeldChannelLoads = {};
1306
+
1307
+ for (const teamId of teamIds) {
1308
+ held[teamId] = createDeferred();
1309
+ }
1310
+
1311
+ apiGetHandler = (url: string): Promise<unknown> => {
1312
+ for (const teamId of teamIds) {
1313
+ if (
1314
+ url.endsWith(
1315
+ `/microsoft-teams/channels?teamId=${encodeURIComponent(teamId)}`,
1316
+ )
1317
+ ) {
1318
+ return held[teamId]!.promise;
1319
+ }
1320
+ }
1321
+
1322
+ return answerGetFromFixtures(url);
1323
+ };
1324
+
1325
+ return held;
1326
+ }
1327
+
1328
+ function channelsResponse(teamId: string): JSONObject {
1329
+ return { data: { channels: RACE_CHANNELS_BY_TEAM[teamId] || [] } };
1330
+ }
1331
+
1332
+ /*
1333
+ * Renders on Alpha's channels, then picks Beta and then Gamma with both
1334
+ * loads held open.
1335
+ */
1336
+ async function pickBetaThenGammaWithLoadsHeld(): Promise<HeldChannelLoads> {
1337
+ await renderTeamsChannelsCard({
1338
+ teams: RACE_TEAMS,
1339
+ channelsByTeam: RACE_CHANNELS_BY_TEAM,
1340
+ });
1341
+ await waitForTeamsChannels(2);
1342
+ getRow("Alpha Ops");
1343
+
1344
+ const held: HeldChannelLoads = holdChannelLoads([
1345
+ "team-beta",
1346
+ "team-gamma",
1347
+ ]);
1348
+
1349
+ await selectTeam("Beta Team");
1350
+ await selectTeam("Gamma Team");
1351
+
1352
+ const betaOrGammaChannelsUrl: RegExp =
1353
+ /\/microsoft-teams\/channels\?teamId=team-(beta|gamma)$/;
1354
+
1355
+ expect(
1356
+ getRequests.filter((url: string) => {
1357
+ return betaOrGammaChannelsUrl.test(url);
1358
+ }),
1359
+ ).toEqual([
1360
+ expect.stringMatching(/teamId=team-beta$/),
1361
+ expect.stringMatching(/teamId=team-gamma$/),
1362
+ ]);
1363
+
1364
+ // Both loads are still out, so no list is shown yet.
1365
+ expect(screen.queryByRole("list")).not.toBeInTheDocument();
1366
+
1367
+ return held;
1368
+ }
1369
+
1370
+ function expectGammaChannelsListed(): void {
1371
+ expect(screen.getByText("Channels (2)")).toBeInTheDocument();
1372
+ expect(getRows()).toHaveLength(2);
1373
+ getRow("General");
1374
+ getRow("Gamma Deploys");
1375
+ expect(screen.queryByText("Beta On-Call")).not.toBeInTheDocument();
1376
+ expect(screen.queryByText("Beta Releases")).not.toBeInTheDocument();
1377
+ expect(screen.queryByText("Alpha Ops")).not.toBeInTheDocument();
1378
+ // The dropdown still names the team the list belongs to.
1379
+ expect(screen.getByText("Gamma Team")).toBeInTheDocument();
1380
+ }
1381
+
1382
+ test("the earlier team's late response is dropped: the list keeps the later team's channels and Send Test posts the later team", async () => {
1383
+ const held: HeldChannelLoads = await pickBetaThenGammaWithLoadsHeld();
1384
+
1385
+ // Gamma, picked last, answers first.
1386
+ await act(async (): Promise<void> => {
1387
+ held["team-gamma"]!.resolve(channelsResponse("team-gamma"));
1388
+ });
1389
+ await screen.findByText("Gamma Deploys");
1390
+ expectGammaChannelsListed();
1391
+
1392
+ // Beta's answer lands afterwards and must not replace Gamma's list.
1393
+ await act(async (): Promise<void> => {
1394
+ held["team-beta"]!.resolve(channelsResponse("team-beta"));
1395
+ });
1396
+ await settlePendingWork();
1397
+
1398
+ expectGammaChannelsListed();
1399
+ expectEveryRowIsIdle();
1400
+
1401
+ await click(getSendTestButton(getRow("General")));
1402
+ await click(getSendTestButton(getRow("Gamma Deploys")));
1403
+
1404
+ expect(
1405
+ postRequests.map((request: RecordedPost) => {
1406
+ return request.data;
1407
+ }),
1408
+ ).toEqual([
1409
+ { teamId: "team-gamma", channelId: "19:shared@thread.tacv2" },
1410
+ { teamId: "team-gamma", channelId: "19:gamma-deploys@thread.tacv2" },
1411
+ ]);
1412
+ });
1413
+
1414
+ test("the earlier team's load failing while the later team is still loading shows no error and keeps loading", async () => {
1415
+ const held: HeldChannelLoads = await pickBetaThenGammaWithLoadsHeld();
1416
+
1417
+ await act(async (): Promise<void> => {
1418
+ held["team-beta"]!.reject(new Error(BETA_LOAD_ERROR));
1419
+ });
1420
+ await settlePendingWork();
1421
+
1422
+ expect(screen.queryByText(BETA_LOAD_ERROR)).not.toBeInTheDocument();
1423
+ // An error would have replaced the whole card body, dropdown included.
1424
+ expect(screen.getByRole("combobox")).toBeInTheDocument();
1425
+ /*
1426
+ * Still loading Gamma. Had Beta's failure ended the loading state, the
1427
+ * list left over from Alpha would be showing under "Gamma Team".
1428
+ */
1429
+ expect(screen.queryByRole("list")).not.toBeInTheDocument();
1430
+ expect(screen.queryByText("Alpha Ops")).not.toBeInTheDocument();
1431
+
1432
+ await act(async (): Promise<void> => {
1433
+ held["team-gamma"]!.resolve(channelsResponse("team-gamma"));
1434
+ });
1435
+ await screen.findByText("Gamma Deploys");
1436
+
1437
+ expectGammaChannelsListed();
1438
+ expect(screen.queryByText(BETA_LOAD_ERROR)).not.toBeInTheDocument();
1439
+
1440
+ await click(getSendTestButton(getRow("Gamma Deploys")));
1441
+
1442
+ expect(postRequests).toHaveLength(1);
1443
+ expect(postRequests[0]!.data).toEqual({
1444
+ teamId: "team-gamma",
1445
+ channelId: "19:gamma-deploys@thread.tacv2",
1446
+ });
1447
+ });
1448
+
1449
+ test("the earlier team's load failing after the later team's channels arrived shows no error and keeps the list", async () => {
1450
+ const held: HeldChannelLoads = await pickBetaThenGammaWithLoadsHeld();
1451
+
1452
+ await act(async (): Promise<void> => {
1453
+ held["team-gamma"]!.resolve(channelsResponse("team-gamma"));
1454
+ });
1455
+ await screen.findByText("Gamma Deploys");
1456
+
1457
+ // The API reports a failed request by returning an HTTPErrorResponse.
1458
+ await act(async (): Promise<void> => {
1459
+ held["team-beta"]!.resolve(
1460
+ new HTTPErrorResponse(500, { message: BETA_LOAD_ERROR }, {}),
1461
+ );
1462
+ });
1463
+ await settlePendingWork();
1464
+
1465
+ expect(screen.queryByText(BETA_LOAD_ERROR)).not.toBeInTheDocument();
1466
+ expectGammaChannelsListed();
1467
+
1468
+ await click(getSendTestButton(getRow("General")));
1469
+
1470
+ expect(postRequests).toHaveLength(1);
1471
+ expect(postRequests[0]!.data).toEqual({
1472
+ teamId: "team-gamma",
1473
+ channelId: "19:shared@thread.tacv2",
1474
+ });
1475
+ });
1476
+
1477
+ /*
1478
+ * Two loads of the SAME team: react-select fires onChange again when the
1479
+ * already-selected option is picked, so re-picking Beta while its list is
1480
+ * loading starts a second Beta load. A guard that compared team ids would
1481
+ * treat the first one as current too.
1482
+ */
1483
+ function holdEveryBetaLoad(): Array<Deferred> {
1484
+ const held: Array<Deferred> = [];
1485
+
1486
+ apiGetHandler = (url: string): Promise<unknown> => {
1487
+ if (url.endsWith("/microsoft-teams/channels?teamId=team-beta")) {
1488
+ const deferred: Deferred = createDeferred();
1489
+ held.push(deferred);
1490
+ return deferred.promise;
1491
+ }
1492
+
1493
+ return answerGetFromFixtures(url);
1494
+ };
1495
+
1496
+ return held;
1497
+ }
1498
+
1499
+ async function pickBetaTwiceWithLoadsHeld(): Promise<Array<Deferred>> {
1500
+ await renderTeamsChannelsCard({
1501
+ teams: RACE_TEAMS,
1502
+ channelsByTeam: RACE_CHANNELS_BY_TEAM,
1503
+ });
1504
+ await waitForTeamsChannels(2);
1505
+ getRow("Alpha Ops");
1506
+
1507
+ const held: Array<Deferred> = holdEveryBetaLoad();
1508
+
1509
+ await selectTeam("Beta Team");
1510
+ await selectTeam("Beta Team");
1511
+
1512
+ expect(held).toHaveLength(2);
1513
+ expect(screen.queryByRole("list")).not.toBeInTheDocument();
1514
+
1515
+ return held;
1516
+ }
1517
+
1518
+ test("an older load of the same team answering first does not end the newer load's loading state", async () => {
1519
+ const held: Array<Deferred> = await pickBetaTwiceWithLoadsHeld();
1520
+
1521
+ await act(async (): Promise<void> => {
1522
+ held[0]!.resolve(channelsResponse("team-beta"));
1523
+ });
1524
+ await settlePendingWork();
1525
+
1526
+ // The second Beta load is still out, so its loader is still showing.
1527
+ expect(screen.queryByRole("list")).not.toBeInTheDocument();
1528
+ expect(screen.queryByText("Beta On-Call")).not.toBeInTheDocument();
1529
+
1530
+ await act(async (): Promise<void> => {
1531
+ held[1]!.resolve(channelsResponse("team-beta"));
1532
+ });
1533
+ await screen.findByText("Beta On-Call");
1534
+
1535
+ expectEveryRowIsIdle();
1536
+ });
1537
+
1538
+ test("an older load of the same team failing after the newer one arrived shows no error and keeps the rows", async () => {
1539
+ const held: Array<Deferred> = await pickBetaTwiceWithLoadsHeld();
1540
+
1541
+ await act(async (): Promise<void> => {
1542
+ held[1]!.resolve(channelsResponse("team-beta"));
1543
+ });
1544
+ await screen.findByText("Beta On-Call");
1545
+
1546
+ await act(async (): Promise<void> => {
1547
+ held[0]!.reject(new Error(BETA_LOAD_ERROR));
1548
+ });
1549
+ await settlePendingWork();
1550
+
1551
+ expect(screen.queryByText(BETA_LOAD_ERROR)).not.toBeInTheDocument();
1552
+ getRow("Beta On-Call");
1553
+
1554
+ await click(getSendTestButton(getRow("Beta On-Call")));
1555
+
1556
+ expect(postRequests).toHaveLength(1);
1557
+ expect(postRequests[0]!.data).toMatchObject({ teamId: "team-beta" });
1558
+ });
1559
+ });
1560
+ });
1561
+
1562
+ describe("MicrosoftTeamsChatsCard Send Test", () => {
1563
+ const CHATS: Array<JSONObject> = [
1564
+ {
1565
+ id: "19:groupchat-war-room@thread.v2",
1566
+ name: "Incident War Room",
1567
+ chatType: "groupChat",
1568
+ addedAt: null,
1569
+ },
1570
+ {
1571
+ id: "19:personal-jane@unq.gbl.spaces",
1572
+ name: "Jane Doe",
1573
+ chatType: "personal",
1574
+ addedAt: null,
1575
+ },
1576
+ ];
1577
+
1578
+ async function renderChatsCard(
1579
+ chats: Array<JSONObject> = CHATS,
1580
+ ): Promise<void> {
1581
+ fixtures.chats = chats;
1582
+ await renderCard(<MicrosoftTeamsChatsCard />);
1583
+ }
1584
+
1585
+ async function waitForChats(count: number): Promise<void> {
1586
+ await screen.findByText(`Connected chats (${count})`);
1587
+ }
1588
+
1589
+ test("the card description tells the user about Send Test for chats and keeps the existing copy", async () => {
1590
+ await renderChatsCard();
1591
+ await waitForChats(2);
1592
+
1593
+ const description: HTMLElement = screen.getByTestId("card-description");
1594
+
1595
+ expect(description).toHaveTextContent(
1596
+ "Use Send Test to confirm OneUptime can post to a chat.",
1597
+ );
1598
+ expect(description).toHaveTextContent(
1599
+ "Send notifications straight into group chats and one-on-one chats.",
1600
+ );
1601
+ });
1602
+
1603
+ test("every chat row has exactly one Send Test button, after the chat-type badge and last in the row", async () => {
1604
+ await renderChatsCard();
1605
+ await waitForChats(2);
1606
+
1607
+ expect(getRows()).toHaveLength(2);
1608
+ expectExactlyOneSendTestButtonPerRow();
1609
+
1610
+ const cases: Array<[string, string]> = [
1611
+ ["Incident War Room", "Group chat"],
1612
+ ["Jane Doe", "1:1 chat"],
1613
+ ];
1614
+
1615
+ for (const [name, badgeText] of cases) {
1616
+ const row: HTMLElement = getRow(name);
1617
+ const badge: HTMLElement = within(row).getByText(badgeText);
1618
+
1619
+ expect(
1620
+ badge.compareDocumentPosition(getSendTestButton(row)) &
1621
+ Node.DOCUMENT_POSITION_FOLLOWING,
1622
+ ).toBeTruthy();
1623
+ expectButtonIsLastInRow(row);
1624
+ expectRowIsIdle(row);
1625
+ }
1626
+ });
1627
+
1628
+ test("the button's accessible name names the chat", async () => {
1629
+ await renderChatsCard();
1630
+ await waitForChats(2);
1631
+
1632
+ expect(getSendTestButton(getRow("Incident War Room"))).toHaveAccessibleName(
1633
+ "Send test notification to Incident War Room",
1634
+ );
1635
+ expect(getSendTestButton(getRow("Jane Doe"))).toHaveAccessibleName(
1636
+ "Send test notification to Jane Doe",
1637
+ );
1638
+ });
1639
+
1640
+ test("loading the chats posts nothing", async () => {
1641
+ await renderChatsCard();
1642
+ await waitForChats(2);
1643
+
1644
+ expect(postRequests).toHaveLength(0);
1645
+ });
1646
+
1647
+ test("clicking Send Test posts only { chatId } to /microsoft-teams/chats/test", async () => {
1648
+ await renderChatsCard();
1649
+ await waitForChats(2);
1650
+
1651
+ await click(getSendTestButton(getRow("Jane Doe")));
1652
+
1653
+ expect(postRequests).toHaveLength(1);
1654
+ expect(postRequests[0]!.url).toMatch(/\/microsoft-teams\/chats\/test$/);
1655
+ // No teamId, no channelId: a chat is addressed by its id alone.
1656
+ expect(postRequests[0]!.data).toEqual({
1657
+ chatId: "19:personal-jane@unq.gbl.spaces",
1658
+ });
1659
+ expect(postRequests[0]!.headers).toEqual(COMMON_HEADERS);
1660
+ });
1661
+
1662
+ test("each chat row posts its own chat id", async () => {
1663
+ await renderChatsCard();
1664
+ await waitForChats(2);
1665
+
1666
+ await click(getSendTestButton(getRow("Incident War Room")));
1667
+ await click(getSendTestButton(getRow("Jane Doe")));
1668
+
1669
+ expect(
1670
+ postRequests.map((request: RecordedPost) => {
1671
+ return request.data;
1672
+ }),
1673
+ ).toEqual([
1674
+ { chatId: "19:groupchat-war-room@thread.v2" },
1675
+ { chatId: "19:personal-jane@unq.gbl.spaces" },
1676
+ ]);
1677
+ });
1678
+
1679
+ test("a successful send marks only that row as Sent", async () => {
1680
+ await renderChatsCard();
1681
+ await waitForChats(2);
1682
+
1683
+ await click(getSendTestButton(getRow("Incident War Room")));
1684
+
1685
+ const sent: HTMLElement = await findRowResult("Incident War Room", "sent");
1686
+
1687
+ expect(sent).toHaveTextContent("Sent");
1688
+ expect(sent).toHaveAttribute(
1689
+ "title",
1690
+ "Test notification sent to Incident War Room. Check Microsoft Teams to confirm it arrived.",
1691
+ );
1692
+ expectRowIsIdle(getRow("Jane Doe"));
1693
+ });
1694
+
1695
+ test("clicking one row does not change the other rows while the send is in flight", async () => {
1696
+ const deferred: Deferred = createDeferred();
1697
+ apiPostHandler = (): Promise<unknown> => {
1698
+ return deferred.promise;
1699
+ };
1700
+
1701
+ await renderChatsCard();
1702
+ await waitForChats(2);
1703
+
1704
+ await click(getSendTestButton(getRow("Jane Doe")));
1705
+
1706
+ expect(getSendTestButton(getRow("Jane Doe"))).toBeDisabled();
1707
+ expectRowIsIdle(getRow("Incident War Room"));
1708
+
1709
+ await act(async (): Promise<void> => {
1710
+ deferred.resolve({ data: {} });
1711
+ });
1712
+
1713
+ await waitFor(() => {
1714
+ expect(getRowStatus(getRow("Jane Doe"))).toHaveTextContent("Sent");
1715
+ });
1716
+ expectRowIsIdle(getRow("Incident War Room"));
1717
+ });
1718
+
1719
+ test("a failing send opens the failure modal naming the chat and Microsoft Teams with the server's error", async () => {
1720
+ const errorMessage: string =
1721
+ "This chat is no longer connected to OneUptime. Add the OneUptime app to the chat in Microsoft Teams, click Refresh Chats, and try again.";
1722
+
1723
+ apiPostHandler = async (): Promise<unknown> => {
1724
+ throw new Error(errorMessage);
1725
+ };
1726
+
1727
+ await renderChatsCard();
1728
+ await waitForChats(2);
1729
+
1730
+ await click(getSendTestButton(getRow("Incident War Room")));
1731
+
1732
+ const dialog: HTMLElement = await screen.findByRole("dialog");
1733
+
1734
+ expect(within(dialog).getByTestId("modal-title")).toHaveTextContent(
1735
+ "Test Notification Failed",
1736
+ );
1737
+ expect(
1738
+ within(dialog).getByTestId("confirm-modal-description"),
1739
+ ).toHaveTextContent(
1740
+ "OneUptime could not send a test notification to Incident War Room in Microsoft Teams.",
1741
+ );
1742
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(errorMessage);
1743
+
1744
+ expect(getRowStatus(getRow("Incident War Room"))).toHaveTextContent(
1745
+ "Failed",
1746
+ );
1747
+ expectRowIsIdle(getRow("Jane Doe"));
1748
+
1749
+ // "Close" dismisses the dialog; the row keeps its result.
1750
+ await click(within(dialog).getByRole("button", { name: "Close" }));
1751
+
1752
+ await waitFor(() => {
1753
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
1754
+ });
1755
+ expect(getRowStatus(getRow("Incident War Room"))).toHaveTextContent(
1756
+ "Failed",
1757
+ );
1758
+ });
1759
+
1760
+ test("a chat without a name still gets a Send Test button that posts its chat id", async () => {
1761
+ await renderChatsCard([
1762
+ {
1763
+ id: "19:unnamed-chat@thread.v2",
1764
+ name: "",
1765
+ chatType: "groupChat",
1766
+ addedAt: null,
1767
+ },
1768
+ ]);
1769
+ await waitForChats(1);
1770
+
1771
+ const buttons: Array<HTMLElement> = querySendTestButtons();
1772
+
1773
+ expect(buttons).toHaveLength(1);
1774
+ // The label must still name a destination rather than trail off.
1775
+ expect(buttons[0]).toHaveAccessibleName(
1776
+ "Send test notification to this chat",
1777
+ );
1778
+
1779
+ await click(buttons[0]!);
1780
+
1781
+ expect(postRequests).toHaveLength(1);
1782
+ expect(postRequests[0]!.data).toEqual({
1783
+ chatId: "19:unnamed-chat@thread.v2",
1784
+ });
1785
+ });
1786
+
1787
+ test("chats the card drops for a missing id get no Send Test button", async () => {
1788
+ await renderChatsCard([
1789
+ ...CHATS,
1790
+ { id: "", name: "Ghost Chat", chatType: "groupChat", addedAt: null },
1791
+ ]);
1792
+ await waitForChats(2);
1793
+
1794
+ expect(querySendTestButtons()).toHaveLength(2);
1795
+ expect(screen.queryByText("Ghost Chat")).not.toBeInTheDocument();
1796
+ });
1797
+
1798
+ test("an empty chat list renders no Send Test buttons", async () => {
1799
+ await renderChatsCard([]);
1800
+
1801
+ await screen.findByText("No chats connected yet");
1802
+
1803
+ expect(querySendTestButtons()).toHaveLength(0);
1804
+ expect(
1805
+ screen.queryByTestId("send-test-notification-button"),
1806
+ ).not.toBeInTheDocument();
1807
+ });
1808
+
1809
+ test("a chat list that fails to load renders no Send Test buttons", async () => {
1810
+ apiGetHandler = async (): Promise<unknown> => {
1811
+ throw new Error("Microsoft Teams is not connected.");
1812
+ };
1813
+
1814
+ await renderChatsCard();
1815
+
1816
+ await screen.findByText("Microsoft Teams is not connected.");
1817
+
1818
+ expect(querySendTestButtons()).toHaveLength(0);
1819
+ });
1820
+
1821
+ test("chat rows can wrap on a narrow screen: the row is flex-wrap and the name keeps an 8rem floor beside the badge", async () => {
1822
+ await renderChatsCard();
1823
+ await waitForChats(2);
1824
+
1825
+ /*
1826
+ * This row is the tightest of the three cards: avatar, name, chat-type
1827
+ * badge and the Send Test control. It is where the name used to collapse
1828
+ * to nothing on a phone.
1829
+ */
1830
+ for (const name of ["Incident War Room", "Jane Doe"]) {
1831
+ expectRowWrapsOnNarrowScreens(name);
1832
+ }
1833
+ });
1834
+
1835
+ describe("while a Send Test is in flight", () => {
1836
+ const chatsSendLockCase: SendLockCase = {
1837
+ getListControls: (): Array<HTMLElement> => {
1838
+ return [screen.getByRole("button", { name: /Refresh Chats/i })];
1839
+ },
1840
+ rowNames: ["Jane Doe", "Incident War Room"],
1841
+ };
1842
+
1843
+ test("Refresh Chats is disabled, does not reload when clicked, and is re-enabled once the send succeeds", async () => {
1844
+ await renderChatsCard();
1845
+ await waitForChats(2);
1846
+
1847
+ await expectListControlsLockedUntilSendSucceeds(chatsSendLockCase);
1848
+ });
1849
+
1850
+ test("Refresh Chats is re-enabled once the send fails", async () => {
1851
+ await renderChatsCard();
1852
+ await waitForChats(2);
1853
+
1854
+ await expectListControlsLockedUntilSendFails(chatsSendLockCase);
1855
+ });
1856
+
1857
+ test("with two sends in flight, Refresh Chats stays disabled after the first settles and is re-enabled after the second", async () => {
1858
+ await renderChatsCard();
1859
+ await waitForChats(2);
1860
+
1861
+ await expectListControlsLockedUntilEverySendSettles(chatsSendLockCase);
1862
+ });
1863
+ });
1864
+ });