@oneuptime/common 14.0.1 → 14.0.2

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 (1090) hide show
  1. package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
  2. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
  3. package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
  4. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
  5. package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
  6. package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
  7. package/Models/DatabaseModels/Monitor.ts +48 -0
  8. package/Models/DatabaseModels/Runner.ts +5 -4
  9. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  10. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  11. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  12. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  13. package/Server/API/AIInvestigationAPI.ts +341 -0
  14. package/Server/API/AutoRemediationAPI.ts +184 -2
  15. package/Server/API/CommonAPI.ts +70 -0
  16. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  17. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  18. package/Server/API/MonitorAPI.ts +140 -0
  19. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  20. package/Server/API/SlackAPI.ts +447 -99
  21. package/Server/API/StatusPageAPI.ts +146 -26
  22. package/Server/API/TelemetryAPI.ts +216 -5
  23. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  24. package/Server/API/WorkflowVariableAPI.ts +156 -0
  25. package/Server/Infrastructure/GlobalCache.ts +35 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  33. package/Server/Middleware/TelemetryIngest.ts +47 -2
  34. package/Server/Middleware/UserAuthorization.ts +56 -2
  35. package/Server/Services/AIAgentService.ts +1 -0
  36. package/Server/Services/AccessTokenService.ts +121 -48
  37. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  38. package/Server/Services/AlertEpisodeService.ts +14 -1
  39. package/Server/Services/AlertService.ts +134 -22
  40. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  41. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  42. package/Server/Services/BillingService.ts +1 -0
  43. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  44. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  45. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  46. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  47. package/Server/Services/IncidentEpisodeService.ts +14 -1
  48. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  49. package/Server/Services/IncidentMemberService.ts +21 -0
  50. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  51. package/Server/Services/IncidentService.ts +120 -20
  52. package/Server/Services/IncidentStateTimelineService.ts +15 -0
  53. package/Server/Services/IncidentTemplateService.ts +83 -3
  54. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  55. package/Server/Services/KubernetesClusterService.ts +1570 -1
  56. package/Server/Services/LogAggregationService.ts +14 -6
  57. package/Server/Services/MonitorService.ts +651 -52
  58. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  59. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  60. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  61. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  62. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  63. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  64. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  65. package/Server/Services/ProbeService.ts +7 -1
  66. package/Server/Services/ProjectService.ts +11 -1
  67. package/Server/Services/RunbookCredentialService.ts +26 -1
  68. package/Server/Services/RunbookSecretService.ts +36 -0
  69. package/Server/Services/RunnerJobService.ts +941 -23
  70. package/Server/Services/RunnerService.ts +439 -6
  71. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  72. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  73. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  74. package/Server/Services/ServiceLevelObjectiveService.ts +14 -1
  75. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  76. package/Server/Services/StatusPageService.ts +301 -74
  77. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  78. package/Server/Services/TeamMemberService.ts +309 -7
  79. package/Server/Services/TraceAggregationService.ts +10 -6
  80. package/Server/Services/UserEmailService.ts +1 -0
  81. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  82. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  83. package/Server/Services/UserNotificationRuleService.ts +4 -0
  84. package/Server/Services/UserService.ts +122 -1
  85. package/Server/Services/WorkflowVariableService.ts +593 -8
  86. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  87. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  88. package/Server/Types/Database/QueryHelper.ts +22 -0
  89. package/Server/Types/Markdown.ts +502 -27
  90. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  91. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  92. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  93. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  94. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  95. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  96. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  97. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  98. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  99. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  100. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  101. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  102. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  103. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  104. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  105. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  106. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  107. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  108. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  109. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  110. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  111. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  112. package/Server/Utils/PushNotificationUtil.ts +45 -0
  113. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  114. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  115. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  116. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  117. package/Server/Utils/StartServer.ts +4 -3
  118. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  119. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  120. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  121. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  122. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  123. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  124. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  125. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  126. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  127. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  128. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  129. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  130. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  131. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  132. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  133. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  134. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  135. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  136. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  137. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  138. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  139. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  140. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  141. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  142. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  143. package/Server/Utils/Workspace/Workspace.ts +171 -54
  144. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  145. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  146. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  147. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  148. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  149. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  150. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  151. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  152. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  153. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  154. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  155. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  156. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  157. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  158. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  159. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  160. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  161. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
  162. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  163. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  164. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  165. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  166. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  167. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  168. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  169. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  170. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +12 -0
  171. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  172. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  173. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  174. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  175. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  176. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  177. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  178. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  179. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  180. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  181. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  182. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  183. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  184. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  185. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  186. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  187. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  188. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  189. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  190. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  191. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  192. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  193. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  194. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  195. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  196. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  197. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  198. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  199. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  200. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  201. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  202. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  203. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  204. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  205. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  206. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  207. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  208. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  209. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  210. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  211. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  212. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  213. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  214. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  215. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  216. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  217. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  218. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  219. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  220. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  221. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  222. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  223. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  224. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  225. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  226. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  227. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  228. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  229. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  230. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  231. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  232. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  233. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  234. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  235. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  236. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  237. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  238. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  239. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  240. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  241. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  242. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  243. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  244. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  245. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  246. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  247. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  248. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  249. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  250. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  251. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  252. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  253. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  254. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  255. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  256. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  257. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  258. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  259. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  260. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  261. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  262. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  263. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  264. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  265. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  266. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  267. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  268. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  269. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  270. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  271. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  272. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  273. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  274. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  275. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  276. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  277. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  278. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  279. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  280. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  281. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  282. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  283. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  284. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  285. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  286. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  287. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  288. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  289. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  290. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  291. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  292. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  293. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  294. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  295. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  296. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  297. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  298. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  299. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  300. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  301. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  302. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  303. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  304. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  305. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  306. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  307. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  308. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  309. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  310. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  311. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  312. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  313. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  314. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  315. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  316. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  317. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +9 -1
  318. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  319. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  320. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  321. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  322. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  323. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  324. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  325. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  326. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  327. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +5 -0
  328. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +12 -0
  329. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  330. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  331. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  332. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  333. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  334. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  335. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  336. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  337. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  338. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  339. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  340. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  341. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  342. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  343. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  344. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  345. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  346. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  347. package/Tests/Server/Types/Markdown.test.ts +1033 -0
  348. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  349. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  350. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  351. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  352. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  353. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  354. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  355. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  356. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  357. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  358. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  359. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  360. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  361. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  362. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  363. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  364. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  365. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  366. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  367. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  368. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  369. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  370. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  371. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  372. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +55 -0
  373. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  374. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  375. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  376. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  377. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  378. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  379. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  380. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  381. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  382. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  383. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  384. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  385. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  386. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  387. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  388. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  389. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  390. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  391. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  392. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  393. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  394. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  395. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  396. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  397. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  398. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  399. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  400. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  401. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  402. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  403. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  404. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  405. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  406. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  407. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  408. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  409. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  410. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  411. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  412. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  413. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  414. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  415. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  416. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  417. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  418. package/Tests/Types/Timezone.test.ts +5 -0
  419. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  420. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  421. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  422. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  423. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  424. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  425. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  426. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  427. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  428. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  429. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  430. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  431. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  432. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  433. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  434. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  435. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  436. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  437. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  438. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  439. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  440. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  441. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  442. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  443. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  444. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  445. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  446. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  447. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  448. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  449. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  450. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  451. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  452. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  453. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  454. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  455. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  456. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  457. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  458. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  459. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  460. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  461. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  462. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  463. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  464. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  465. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  466. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  467. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  468. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  469. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  470. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  471. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  472. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  473. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  474. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  475. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  476. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  477. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  478. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  479. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  480. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  481. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  482. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  483. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  484. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  485. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  486. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  487. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  488. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  489. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  490. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  491. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  492. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  493. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  494. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  495. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  496. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  497. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  498. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  499. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  500. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  501. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  502. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  503. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  504. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  505. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  506. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  507. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  508. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  509. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  510. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  511. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  512. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  513. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  514. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  515. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  516. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  517. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  518. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  519. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  520. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  521. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  522. package/Types/Date.ts +121 -7
  523. package/Types/Email/EmailMessage.ts +8 -0
  524. package/Types/Email/EmailTemplateType.ts +1 -0
  525. package/Types/Exception/ExceptionMessages.ts +1 -0
  526. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  527. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  528. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  529. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  530. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  531. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  532. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  533. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  534. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  535. package/Types/Rum/SessionReplay.ts +55 -2
  536. package/Types/Rum/SessionReplayApi.ts +44 -1
  537. package/Types/Rum/UserFlow.ts +187 -0
  538. package/Types/Runbook/RunbookStepType.ts +10 -0
  539. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  540. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  541. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  542. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  543. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  544. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  545. package/Types/Timezone.ts +5 -0
  546. package/Types/TimezoneAlias.ts +385 -0
  547. package/Types/Workflow/Templates.ts +3041 -2
  548. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  549. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  550. package/Typings/Index.d.ts +24 -0
  551. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  552. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  553. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  554. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  555. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  556. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  557. package/UI/Components/Calendar/Calendar.css +83 -0
  558. package/UI/Components/Calendar/Calendar.tsx +136 -7
  559. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  560. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  561. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  562. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  563. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  564. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  565. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  566. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  567. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  568. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  569. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  570. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  571. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  572. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  573. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  574. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  575. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  576. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  577. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  578. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  579. package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
  580. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  581. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  582. package/UI/Components/Page/ModelPage.tsx +59 -3
  583. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  584. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  585. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  586. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  587. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  588. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  589. package/UI/Utils/Timezone.ts +69 -28
  590. package/Utils/AI/InvestigationReport.ts +195 -25
  591. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  592. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  593. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  594. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  595. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  596. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  597. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  598. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  599. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  600. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  601. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  602. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  603. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  604. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  605. package/Utils/Rum/SessionIdentity.ts +44 -0
  606. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  607. package/Utils/Rum/UserFlow.ts +1527 -0
  608. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  609. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  610. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  611. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  612. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  613. package/Utils/Uptime/UptimeUtil.ts +122 -38
  614. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  615. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  616. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  617. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  618. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  619. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  620. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  621. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  622. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  623. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  624. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  625. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  626. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  627. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  628. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  629. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  630. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  631. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  632. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  633. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  634. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  635. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  636. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  637. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  638. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  639. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  640. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  641. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  642. package/build/dist/Server/API/CommonAPI.js +49 -0
  643. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  644. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  645. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  646. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  647. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  648. package/build/dist/Server/API/MonitorAPI.js +107 -1
  649. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  650. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  651. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  652. package/build/dist/Server/API/SlackAPI.js +322 -78
  653. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  654. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  655. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  656. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  657. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  658. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  659. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  660. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  661. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  662. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  663. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  664. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  665. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  666. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  667. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  668. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  669. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  670. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  671. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  672. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  673. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  674. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  675. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  676. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  677. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  678. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  679. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  680. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  681. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  682. package/build/dist/Server/Services/AIAgentService.js +1 -0
  683. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  684. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  685. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  686. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  687. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  688. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  689. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  690. package/build/dist/Server/Services/AlertService.js +110 -23
  691. package/build/dist/Server/Services/AlertService.js.map +1 -1
  692. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  693. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  694. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  695. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  696. package/build/dist/Server/Services/BillingService.js +1 -0
  697. package/build/dist/Server/Services/BillingService.js.map +1 -1
  698. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  699. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  700. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  701. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  702. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  703. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  704. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  705. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  706. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  707. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  708. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  709. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  710. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  711. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  712. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  713. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  714. package/build/dist/Server/Services/IncidentService.js +99 -21
  715. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  716. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  717. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  718. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  719. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  720. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  721. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  722. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  723. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  724. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  725. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  726. package/build/dist/Server/Services/MonitorService.js +487 -48
  727. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  728. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  729. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  730. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  731. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  732. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  733. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  734. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  735. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  736. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  737. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  738. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  739. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  740. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  741. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  742. package/build/dist/Server/Services/ProbeService.js +7 -1
  743. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  744. package/build/dist/Server/Services/ProjectService.js +11 -1
  745. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  746. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  747. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  748. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  749. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  750. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  751. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  752. package/build/dist/Server/Services/RunnerService.js +344 -5
  753. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  754. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  755. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  756. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  757. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  758. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  759. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  760. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +12 -1
  761. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  762. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  763. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  764. package/build/dist/Server/Services/StatusPageService.js +230 -32
  765. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  766. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  767. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  768. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  769. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  770. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  771. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  772. package/build/dist/Server/Services/UserEmailService.js +1 -0
  773. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  774. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  775. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  776. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  777. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  778. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  779. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  780. package/build/dist/Server/Services/UserService.js +107 -1
  781. package/build/dist/Server/Services/UserService.js.map +1 -1
  782. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  783. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  784. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  785. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  786. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  787. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  788. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  789. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  790. package/build/dist/Server/Types/Markdown.js +394 -27
  791. package/build/dist/Server/Types/Markdown.js.map +1 -1
  792. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  793. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  794. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  795. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  796. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  797. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  798. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  799. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  800. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  801. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  802. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  803. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  804. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  805. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  806. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  807. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  808. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  809. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  810. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  811. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  812. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  813. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  814. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  815. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  816. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  817. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  818. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  819. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  820. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  821. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  822. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  823. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  824. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  825. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  826. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  827. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  828. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  829. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  830. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  831. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  832. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  833. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  834. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  835. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  836. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  837. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  838. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  839. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  840. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  841. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  842. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  843. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  844. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  845. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  846. package/build/dist/Server/Utils/StartServer.js +4 -3
  847. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  848. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  849. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  850. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  851. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  852. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  853. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  854. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  855. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  856. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  857. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  858. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  859. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  860. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  861. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  862. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  863. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  864. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  865. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  866. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  867. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  868. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  869. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  870. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  871. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  872. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  873. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  874. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  875. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  876. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  877. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  878. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  879. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  880. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  881. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  882. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  883. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  884. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  885. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  886. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  887. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  888. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  889. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  890. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  891. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  892. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  893. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  894. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  895. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  896. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  897. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  898. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  899. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  900. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  901. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  902. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  903. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  904. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  905. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  906. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  907. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  908. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  909. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  910. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  911. package/build/dist/Types/Date.js +97 -7
  912. package/build/dist/Types/Date.js.map +1 -1
  913. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  914. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  915. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  916. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  917. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  918. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  919. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  920. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  921. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  922. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  923. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  924. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  925. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  926. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  927. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  928. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  929. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  930. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  931. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  932. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  933. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  934. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  935. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  936. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  937. package/build/dist/Types/Rum/UserFlow.js +115 -0
  938. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  939. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  940. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  941. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  942. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  943. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  944. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  945. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  946. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  947. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  948. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  949. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  950. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  951. package/build/dist/Types/Timezone.js +5 -0
  952. package/build/dist/Types/Timezone.js.map +1 -1
  953. package/build/dist/Types/TimezoneAlias.js +340 -0
  954. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  955. package/build/dist/Types/Workflow/Templates.js +2694 -2
  956. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  957. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  958. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  959. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  960. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  961. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  962. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  963. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  964. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  965. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  966. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  967. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  968. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  969. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  970. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  971. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  972. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  973. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  974. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  975. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  976. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  977. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  978. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  979. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  980. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  981. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  982. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  983. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  984. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  985. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  986. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  987. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  988. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  989. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  990. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  991. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  992. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  993. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  994. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  995. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  996. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  997. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  998. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  999. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1000. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1001. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1002. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1003. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1004. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1005. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1006. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1007. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1008. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1009. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1010. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1011. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1012. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1013. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1014. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
  1015. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1016. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1017. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1018. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1019. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1020. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1021. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1022. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1023. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1024. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1025. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1026. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1027. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1028. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1029. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1030. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1031. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1032. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1033. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1034. package/build/dist/UI/Utils/Timezone.js +52 -17
  1035. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1036. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1037. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1038. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1039. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1040. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1041. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1042. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1043. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1044. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1045. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1046. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1047. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1048. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1049. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1050. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1051. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1052. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1053. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1054. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1055. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1056. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1057. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1058. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1059. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1060. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1061. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1062. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1063. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1064. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1065. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1066. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1067. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1068. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1069. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1070. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1071. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1072. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1073. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1074. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1075. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1076. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1077. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1078. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1079. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1080. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1081. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1082. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1083. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1084. package/jest.config.json +2 -1
  1085. package/package.json +2 -1
  1086. package/tsconfig.tests.json +36 -0
  1087. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1088. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1089. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1090. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -0,0 +1,1854 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ act,
4
+ fireEvent,
5
+ render,
6
+ RenderResult,
7
+ screen,
8
+ waitFor,
9
+ within,
10
+ } from "@testing-library/react";
11
+ import { beforeEach, describe, expect, test } from "@jest/globals";
12
+ import * as React from "react";
13
+ import { JSONObject } from "../../../Types/JSON";
14
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
15
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
16
+ import URL from "../../../Types/API/URL";
17
+ import { APP_API_URL } from "../../../UI/Config";
18
+
19
+ /*
20
+ * SendTestNotificationButton sits on every row of the Slack channels card and
21
+ * the Microsoft Teams channels and chats cards. One click posts a test
22
+ * notification to that one destination - no confirmation dialog - and the row
23
+ * reports the outcome: a "Sent" badge on success, and on failure a "Failed"
24
+ * badge plus a dialog (with a single "Close" button) that carries the
25
+ * server's error. Both badges appear inside one polite live region that is
26
+ * mounted from the first render, and an optional onSendingChange callback
27
+ * tells the card when each send starts and settles.
28
+ *
29
+ * API and ModelAPI are mocked the same way the chats card test mocks them: a
30
+ * module-level object whose functions forward to mutable test state, so each
31
+ * test decides what the server "answers" without jest.resetModules() (which
32
+ * would hand the component a second copy of React). Every request the
33
+ * component makes is recorded so the tests can assert on the URL, body and
34
+ * headers it sent.
35
+ */
36
+
37
+ interface PostRequest {
38
+ url: URL;
39
+ data: JSONObject;
40
+ headers: Record<string, string>;
41
+ }
42
+
43
+ type PostResult = HTTPResponse<JSONObject> | HTTPErrorResponse;
44
+ type ApiPostFunction = (request: PostRequest) => Promise<PostResult>;
45
+ type FriendlyErrorMessageFunction = (error: unknown) => string;
46
+
47
+ const COMMON_HEADERS: Record<string, string> = {
48
+ tenantid: "5f8b9c2e-project-under-test",
49
+ authorization: "Bearer test-token",
50
+ };
51
+
52
+ const postRequests: Array<PostRequest> = [];
53
+ const friendlyErrorMessageArguments: Array<unknown> = [];
54
+ let commonHeadersCallCount: number = 0;
55
+
56
+ const successResponse: () => HTTPResponse<JSONObject> =
57
+ (): HTTPResponse<JSONObject> => {
58
+ return new HTTPResponse<JSONObject>(200, {}, {});
59
+ };
60
+
61
+ const defaultApiPost: ApiPostFunction = async (): Promise<PostResult> => {
62
+ return successResponse();
63
+ };
64
+
65
+ /*
66
+ * The real getFriendlyErrorMessage reads `.message` off whatever it is given.
67
+ * HTTPErrorResponse exposes the server's { message } body through a getter, so
68
+ * this default mirrors what the dashboard would show for both shapes.
69
+ */
70
+ const defaultFriendlyErrorMessage: FriendlyErrorMessageFunction = (
71
+ error: unknown,
72
+ ): string => {
73
+ if (error instanceof HTTPErrorResponse) {
74
+ return error.message;
75
+ }
76
+
77
+ if (error instanceof Error) {
78
+ return error.message;
79
+ }
80
+
81
+ return "Something went wrong.";
82
+ };
83
+
84
+ let apiPost: ApiPostFunction = defaultApiPost;
85
+ let friendlyErrorMessage: FriendlyErrorMessageFunction =
86
+ defaultFriendlyErrorMessage;
87
+
88
+ jest.mock("../../../UI/Utils/API/API", () => {
89
+ return {
90
+ __esModule: true,
91
+ default: {
92
+ post: (request: PostRequest): Promise<PostResult> => {
93
+ postRequests.push(request);
94
+ return apiPost(request);
95
+ },
96
+ getFriendlyErrorMessage: (error: unknown): string => {
97
+ friendlyErrorMessageArguments.push(error);
98
+ return friendlyErrorMessage(error);
99
+ },
100
+ },
101
+ };
102
+ });
103
+
104
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
105
+ return {
106
+ __esModule: true,
107
+ default: {
108
+ getCommonHeaders: (): Record<string, string> => {
109
+ commonHeadersCallCount += 1;
110
+ return COMMON_HEADERS;
111
+ },
112
+ },
113
+ };
114
+ });
115
+
116
+ import SendTestNotificationButton, {
117
+ ComponentProps,
118
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Workspace/SendTestNotificationButton";
119
+
120
+ interface Deferred<T> {
121
+ promise: Promise<T>;
122
+ resolve: (value: T) => void;
123
+ reject: (reason: unknown) => void;
124
+ }
125
+
126
+ function createDeferred<T>(): Deferred<T> {
127
+ let resolvePromise!: (value: T) => void;
128
+ let rejectPromise!: (reason: unknown) => void;
129
+
130
+ const promise: Promise<T> = new Promise<T>(
131
+ (resolve: (value: T) => void, reject: (reason: unknown) => void) => {
132
+ resolvePromise = resolve;
133
+ rejectPromise = reject;
134
+ },
135
+ );
136
+
137
+ return {
138
+ promise: promise,
139
+ resolve: resolvePromise,
140
+ reject: rejectPromise,
141
+ };
142
+ }
143
+
144
+ /*
145
+ * Hands every subsequent post the same pending promise, so the test controls
146
+ * exactly when the request "comes back".
147
+ */
148
+ function holdNextPosts(): Deferred<PostResult> {
149
+ const deferred: Deferred<PostResult> = createDeferred<PostResult>();
150
+
151
+ apiPost = (): Promise<PostResult> => {
152
+ return deferred.promise;
153
+ };
154
+
155
+ return deferred;
156
+ }
157
+
158
+ async function settle(
159
+ deferred: Deferred<PostResult>,
160
+ outcome: { resolveWith: PostResult } | { rejectWith: unknown },
161
+ ): Promise<void> {
162
+ await act(async (): Promise<void> => {
163
+ if ("resolveWith" in outcome) {
164
+ deferred.resolve(outcome.resolveWith);
165
+ } else {
166
+ deferred.reject(outcome.rejectWith);
167
+ }
168
+
169
+ try {
170
+ await deferred.promise;
171
+ } catch {
172
+ // The component handles the rejection; this only waits it out.
173
+ }
174
+ });
175
+ }
176
+
177
+ const SLACK_PROPS: ComponentProps = {
178
+ route: "/slack/channels/test",
179
+ requestBody: { channelId: "C0123ALERTS" },
180
+ destinationName: "#alerts",
181
+ workspaceName: "Slack",
182
+ };
183
+
184
+ const TEAMS_CHANNEL_PROPS: ComponentProps = {
185
+ route: "/microsoft-teams/channels/test",
186
+ requestBody: {
187
+ teamId: "a1b2c3d4-team-platform",
188
+ channelId: "19:general-thread@thread.tacv2",
189
+ },
190
+ destinationName: "General",
191
+ workspaceName: "Microsoft Teams",
192
+ };
193
+
194
+ const TEAMS_CHAT_PROPS: ComponentProps = {
195
+ route: "/microsoft-teams/chats/test",
196
+ requestBody: { chatId: "19:groupchat-war-room@thread.v2" },
197
+ destinationName: "Incident War Room",
198
+ workspaceName: "Microsoft Teams",
199
+ };
200
+
201
+ const SENT_TEST_ID: string = "send-test-notification-sent";
202
+ const FAILED_TEST_ID: string = "send-test-notification-failed";
203
+ const BUTTON_TEST_ID: string = "send-test-notification-button";
204
+ const ERROR_MODAL_TITLE: string = "Test Notification Failed";
205
+
206
+ function renderButton(props: ComponentProps = SLACK_PROPS): RenderResult {
207
+ return render(<SendTestNotificationButton {...props} />);
208
+ }
209
+
210
+ function getSendButton(destinationName: string): HTMLElement {
211
+ return screen.getByRole("button", {
212
+ name: `Send test notification to ${destinationName}`,
213
+ });
214
+ }
215
+
216
+ function querySentStatus(
217
+ scope: HTMLElement = document.body,
218
+ ): HTMLElement | null {
219
+ return within(scope).queryByTestId(SENT_TEST_ID);
220
+ }
221
+
222
+ function queryFailedStatus(
223
+ scope: HTMLElement = document.body,
224
+ ): HTMLElement | null {
225
+ return within(scope).queryByTestId(FAILED_TEST_ID);
226
+ }
227
+
228
+ function queryErrorModal(): HTMLElement | null {
229
+ return screen.queryByRole("dialog", { name: ERROR_MODAL_TITLE });
230
+ }
231
+
232
+ function getSpinner(button: HTMLElement): Element | null {
233
+ return button.querySelector("svg.animate-spin");
234
+ }
235
+
236
+ async function clickSend(button: HTMLElement): Promise<void> {
237
+ await act(async (): Promise<void> => {
238
+ fireEvent.click(button);
239
+ });
240
+ }
241
+
242
+ /*
243
+ * The modal mounts transparent and fades in on the next frame. Waiting for the
244
+ * fade both proves the dialog actually becomes visible and keeps that
245
+ * follow-up state update inside act.
246
+ */
247
+ async function waitForErrorModal(): Promise<HTMLElement> {
248
+ const dialog: HTMLElement = await screen.findByRole("dialog", {
249
+ name: ERROR_MODAL_TITLE,
250
+ });
251
+
252
+ await waitFor(() => {
253
+ expect(dialog).toHaveClass("opacity-100");
254
+ });
255
+
256
+ return dialog;
257
+ }
258
+
259
+ async function waitForSent(scope: HTMLElement = document.body): Promise<void> {
260
+ await waitFor(() => {
261
+ expect(querySentStatus(scope)).toBeInTheDocument();
262
+ });
263
+ }
264
+
265
+ beforeEach(() => {
266
+ postRequests.length = 0;
267
+ friendlyErrorMessageArguments.length = 0;
268
+ commonHeadersCallCount = 0;
269
+ apiPost = defaultApiPost;
270
+ friendlyErrorMessage = defaultFriendlyErrorMessage;
271
+ });
272
+
273
+ describe("SendTestNotificationButton", () => {
274
+ describe("initial render", () => {
275
+ test('renders an enabled "Send Test" button labelled with the destination', () => {
276
+ renderButton(SLACK_PROPS);
277
+
278
+ const button: HTMLElement = getSendButton("#alerts");
279
+
280
+ expect(button).toBeInTheDocument();
281
+ expect(button).toHaveTextContent("Send Test");
282
+ expect(button).toHaveAttribute(
283
+ "aria-label",
284
+ "Send test notification to #alerts",
285
+ );
286
+ expect(button).toHaveAttribute("data-testid", BUTTON_TEST_ID);
287
+ expect(button).toBeEnabled();
288
+ expect(button).toHaveAttribute("aria-disabled", "false");
289
+ });
290
+
291
+ test("is a plain button, so it can never submit a surrounding form", () => {
292
+ renderButton(SLACK_PROPS);
293
+
294
+ expect(getSendButton("#alerts")).toHaveAttribute("type", "button");
295
+ });
296
+
297
+ test.each([
298
+ ["a Slack channel", "#alerts"],
299
+ ["a Teams channel", "General"],
300
+ ["a group chat named after its members", "Alice, Bob"],
301
+ ["a name with quotes", 'Ops "Night" Shift'],
302
+ ])(
303
+ "uses the destination name verbatim in the accessible name for %s",
304
+ (_label: string, destinationName: string) => {
305
+ renderButton({ ...SLACK_PROPS, destinationName: destinationName });
306
+
307
+ expect(
308
+ screen.getByRole("button", {
309
+ name: `Send test notification to ${destinationName}`,
310
+ }),
311
+ ).toBeInTheDocument();
312
+ },
313
+ );
314
+
315
+ test("shows no status, no dialog and no spinner before it is clicked", () => {
316
+ renderButton(SLACK_PROPS);
317
+
318
+ const button: HTMLElement = getSendButton("#alerts");
319
+
320
+ expect(querySentStatus()).not.toBeInTheDocument();
321
+ expect(queryFailedStatus()).not.toBeInTheDocument();
322
+ // The live region is already there, but it has nothing to say yet.
323
+ expect(screen.getByRole("status")).toBeEmptyDOMElement();
324
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
325
+ expect(screen.queryByRole("alert")).not.toBeInTheDocument();
326
+ expect(getSpinner(button)).toBeNull();
327
+ // The send icon is drawn instead of the spinner.
328
+ expect(button.querySelector("svg")).not.toBeNull();
329
+ });
330
+
331
+ test("sends nothing on mount", () => {
332
+ renderButton(SLACK_PROPS);
333
+
334
+ expect(postRequests).toHaveLength(0);
335
+ expect(commonHeadersCallCount).toBe(0);
336
+ });
337
+
338
+ test("wraps its content in a non-shrinking flex row so a long name cannot squash it", () => {
339
+ const { container } = renderButton(SLACK_PROPS);
340
+
341
+ const root: HTMLElement = container.firstElementChild as HTMLElement;
342
+
343
+ expect(root.tagName).toBe("DIV");
344
+ expect(root).toHaveClass("flex", "flex-none", "items-center", "gap-2");
345
+ expect(root).toContainElement(getSendButton("#alerts"));
346
+ });
347
+
348
+ test("uses the small button size", () => {
349
+ renderButton(SLACK_PROPS);
350
+
351
+ expect(getSendButton("#alerts")).toHaveClass("px-2", "py-1");
352
+ });
353
+
354
+ test("keeps the row's text size and hugs the right edge when a narrow row wraps it", () => {
355
+ const { container } = renderButton(SLACK_PROPS);
356
+
357
+ const root: HTMLElement = container.firstElementChild as HTMLElement;
358
+
359
+ /*
360
+ * Button is text-base below md; in a list row it has to match the
361
+ * row's text-sm. ml-auto keeps the control right-aligned whether it
362
+ * sits beside the name or on a line of its own under it.
363
+ */
364
+ expect(getSendButton("#alerts")).toHaveClass("!text-sm");
365
+ expect(root).toHaveClass("ml-auto");
366
+ });
367
+ });
368
+
369
+ describe("status live region", () => {
370
+ /*
371
+ * Screen readers often ignore a live region that is inserted already
372
+ * filled, so the component keeps one polite role="status" region mounted
373
+ * from the first render and only swaps what is inside it. The tests hold
374
+ * on to the node from the idle render and check it is the very same node
375
+ * that later carries the result.
376
+ */
377
+ function getLiveRegion(): HTMLElement {
378
+ return screen.getByRole("status");
379
+ }
380
+
381
+ test("is mounted, polite and empty while idle", () => {
382
+ renderButton(SLACK_PROPS);
383
+
384
+ const liveRegion: HTMLElement = getLiveRegion();
385
+
386
+ expect(liveRegion).toHaveAttribute("aria-live", "polite");
387
+ expect(liveRegion).toBeEmptyDOMElement();
388
+ expect(screen.getAllByRole("status")).toHaveLength(1);
389
+ });
390
+
391
+ test('stays empty while sending, then the same node announces "Sent"', async () => {
392
+ renderButton(SLACK_PROPS);
393
+
394
+ const liveRegion: HTMLElement = getLiveRegion();
395
+ const deferred: Deferred<PostResult> = holdNextPosts();
396
+
397
+ await clickSend(getSendButton("#alerts"));
398
+
399
+ expect(getLiveRegion()).toBe(liveRegion);
400
+ expect(liveRegion).toBeEmptyDOMElement();
401
+
402
+ await settle(deferred, { resolveWith: successResponse() });
403
+ await waitForSent();
404
+
405
+ expect(getLiveRegion()).toBe(liveRegion);
406
+ expect(liveRegion).toBeInTheDocument();
407
+ expect(liveRegion).toHaveAttribute("aria-live", "polite");
408
+ expect(liveRegion).toHaveTextContent("Sent");
409
+ expect(liveRegion).toContainElement(screen.getByTestId(SENT_TEST_ID));
410
+ expect(screen.getAllByRole("status")).toHaveLength(1);
411
+ });
412
+
413
+ test('the same node announces "Failed" when the send fails', async () => {
414
+ apiPost = async (): Promise<PostResult> => {
415
+ return new HTTPErrorResponse(400, { message: "Nope." }, {});
416
+ };
417
+
418
+ renderButton(SLACK_PROPS);
419
+
420
+ const liveRegion: HTMLElement = getLiveRegion();
421
+
422
+ await clickSend(getSendButton("#alerts"));
423
+ await waitForErrorModal();
424
+
425
+ expect(getLiveRegion()).toBe(liveRegion);
426
+ expect(liveRegion).toBeInTheDocument();
427
+ expect(liveRegion).toHaveAttribute("aria-live", "polite");
428
+ expect(liveRegion).toHaveTextContent("Failed");
429
+ expect(liveRegion).toContainElement(screen.getByTestId(FAILED_TEST_ID));
430
+ expect(screen.getAllByRole("status")).toHaveLength(1);
431
+ });
432
+
433
+ test("one node carries every outcome across sends: idle, Sent, empty again, Failed", async () => {
434
+ renderButton(SLACK_PROPS);
435
+
436
+ const liveRegion: HTMLElement = getLiveRegion();
437
+
438
+ await clickSend(getSendButton("#alerts"));
439
+ await waitForSent();
440
+
441
+ expect(getLiveRegion()).toBe(liveRegion);
442
+ expect(liveRegion).toHaveTextContent("Sent");
443
+
444
+ const deferred: Deferred<PostResult> = holdNextPosts();
445
+
446
+ await clickSend(getSendButton("#alerts"));
447
+
448
+ // The retry clears the old result without replacing the region.
449
+ expect(getLiveRegion()).toBe(liveRegion);
450
+ expect(liveRegion).toBeEmptyDOMElement();
451
+
452
+ await settle(deferred, {
453
+ resolveWith: new HTTPErrorResponse(400, { message: "Nope." }, {}),
454
+ });
455
+ await waitForErrorModal();
456
+
457
+ expect(getLiveRegion()).toBe(liveRegion);
458
+ expect(liveRegion).toHaveTextContent("Failed");
459
+ expect(liveRegion).not.toHaveTextContent("Sent");
460
+ });
461
+ });
462
+
463
+ describe("sending", () => {
464
+ test("posts once, immediately, without asking for confirmation", async () => {
465
+ renderButton(SLACK_PROPS);
466
+
467
+ const deferred: Deferred<PostResult> = holdNextPosts();
468
+
469
+ fireEvent.click(getSendButton("#alerts"));
470
+
471
+ // The request goes out synchronously on click - no dialog in between.
472
+ expect(postRequests).toHaveLength(1);
473
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
474
+
475
+ await settle(deferred, { resolveWith: successResponse() });
476
+ await waitForSent();
477
+
478
+ expect(postRequests).toHaveLength(1);
479
+ });
480
+
481
+ test("posts to the route under APP_API_URL with the request body and the common headers", async () => {
482
+ renderButton(SLACK_PROPS);
483
+
484
+ await clickSend(getSendButton("#alerts"));
485
+ await waitForSent();
486
+
487
+ expect(postRequests).toHaveLength(1);
488
+
489
+ const request: PostRequest = postRequests[0]!;
490
+
491
+ expect(request.url).toBeInstanceOf(URL);
492
+ expect(request.url.toString().endsWith("/slack/channels/test")).toBe(
493
+ true,
494
+ );
495
+ expect(request.url.toString()).toBe(
496
+ URL.fromURL(APP_API_URL).addRoute("/slack/channels/test").toString(),
497
+ );
498
+ expect(request.url.toString().startsWith(APP_API_URL.toString())).toBe(
499
+ true,
500
+ );
501
+
502
+ expect(request.data).toEqual({ channelId: "C0123ALERTS" });
503
+
504
+ /*
505
+ * This is a custom route, so the tenantid header only arrives through
506
+ * getCommonHeaders() - without it the server cannot tell which project
507
+ * the test is sent from.
508
+ */
509
+ expect(commonHeadersCallCount).toBe(1);
510
+ expect(request.headers).toBe(COMMON_HEADERS);
511
+ expect(request.headers).toEqual({
512
+ tenantid: "5f8b9c2e-project-under-test",
513
+ authorization: "Bearer test-token",
514
+ });
515
+ });
516
+
517
+ test.each([
518
+ ["a Slack channel", SLACK_PROPS],
519
+ ["a Microsoft Teams channel", TEAMS_CHANNEL_PROPS],
520
+ ["a Microsoft Teams chat", TEAMS_CHAT_PROPS],
521
+ ])(
522
+ "sends the route and body the card passes for %s",
523
+ async (_label: string, props: ComponentProps) => {
524
+ renderButton(props);
525
+
526
+ await clickSend(getSendButton(props.destinationName));
527
+ await waitForSent();
528
+
529
+ expect(postRequests).toHaveLength(1);
530
+ expect(postRequests[0]!.url.toString().endsWith(props.route)).toBe(
531
+ true,
532
+ );
533
+ expect(postRequests[0]!.data).toEqual(props.requestBody);
534
+ expect(postRequests[0]!.headers).toBe(COMMON_HEADERS);
535
+ },
536
+ );
537
+
538
+ test("sends the Teams channel's team id alongside its channel id", async () => {
539
+ renderButton(TEAMS_CHANNEL_PROPS);
540
+
541
+ await clickSend(getSendButton("General"));
542
+ await waitForSent();
543
+
544
+ expect(postRequests[0]!.data).toEqual({
545
+ teamId: "a1b2c3d4-team-platform",
546
+ channelId: "19:general-thread@thread.tacv2",
547
+ });
548
+ expect(postRequests[0]!.data).not.toHaveProperty("chatId");
549
+ });
550
+
551
+ test("builds a fresh URL for every click and never mutates APP_API_URL", async () => {
552
+ const appApiUrlBefore: string = APP_API_URL.toString();
553
+
554
+ renderButton(SLACK_PROPS);
555
+
556
+ await clickSend(getSendButton("#alerts"));
557
+ await waitForSent();
558
+
559
+ await clickSend(getSendButton("#alerts"));
560
+ await waitForSent();
561
+
562
+ expect(postRequests).toHaveLength(2);
563
+ expect(postRequests[0]!.url).not.toBe(APP_API_URL);
564
+ expect(postRequests[1]!.url).not.toBe(APP_API_URL);
565
+ expect(postRequests[0]!.url).not.toBe(postRequests[1]!.url);
566
+
567
+ // No route piles up on the shared config URL between clicks.
568
+ expect(postRequests[1]!.url.toString()).toBe(
569
+ postRequests[0]!.url.toString(),
570
+ );
571
+ expect(APP_API_URL.toString()).toBe(appApiUrlBefore);
572
+ expect(
573
+ postRequests[1]!.url.toString().split("/slack/channels/test"),
574
+ ).toHaveLength(2);
575
+ });
576
+ });
577
+
578
+ describe("while the request is in flight", () => {
579
+ test("disables the button and shows its loading spinner", async () => {
580
+ renderButton(SLACK_PROPS);
581
+
582
+ const deferred: Deferred<PostResult> = holdNextPosts();
583
+
584
+ await clickSend(getSendButton("#alerts"));
585
+
586
+ const button: HTMLElement = getSendButton("#alerts");
587
+
588
+ expect(button).toBeDisabled();
589
+ expect(button).toHaveAttribute("aria-disabled", "true");
590
+ expect(getSpinner(button)).not.toBeNull();
591
+ expect(button).toHaveTextContent("Send Test");
592
+
593
+ expect(querySentStatus()).not.toBeInTheDocument();
594
+ expect(queryFailedStatus()).not.toBeInTheDocument();
595
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
596
+
597
+ await settle(deferred, { resolveWith: successResponse() });
598
+ await waitForSent();
599
+ });
600
+
601
+ test("a second click does not post again", async () => {
602
+ renderButton(SLACK_PROPS);
603
+
604
+ const deferred: Deferred<PostResult> = holdNextPosts();
605
+
606
+ await clickSend(getSendButton("#alerts"));
607
+ expect(postRequests).toHaveLength(1);
608
+
609
+ await clickSend(getSendButton("#alerts"));
610
+ expect(postRequests).toHaveLength(1);
611
+
612
+ await settle(deferred, { resolveWith: successResponse() });
613
+ await waitForSent();
614
+
615
+ expect(postRequests).toHaveLength(1);
616
+ expect(commonHeadersCallCount).toBe(1);
617
+ });
618
+
619
+ test("a burst of clicks still posts only once", async () => {
620
+ renderButton(SLACK_PROPS);
621
+
622
+ const deferred: Deferred<PostResult> = holdNextPosts();
623
+
624
+ for (let i: number = 0; i < 5; i++) {
625
+ await clickSend(getSendButton("#alerts"));
626
+ }
627
+
628
+ expect(postRequests).toHaveLength(1);
629
+
630
+ await settle(deferred, { resolveWith: successResponse() });
631
+ await waitForSent();
632
+
633
+ expect(postRequests).toHaveLength(1);
634
+ });
635
+
636
+ test("two clicks in the same frame, before the disabled state renders, still post only once", async () => {
637
+ renderButton(SLACK_PROPS);
638
+
639
+ const deferred: Deferred<PostResult> = holdNextPosts();
640
+ const button: HTMLElement = getSendButton("#alerts");
641
+
642
+ /*
643
+ * Both clicks land inside one act() scope, so React has not re-rendered
644
+ * the button as disabled when the second one arrives. Only the
645
+ * synchronous in-flight guard can stop it.
646
+ */
647
+ act(() => {
648
+ button.click();
649
+ button.click();
650
+ });
651
+
652
+ expect(postRequests).toHaveLength(1);
653
+
654
+ await settle(deferred, { resolveWith: successResponse() });
655
+ await waitForSent();
656
+
657
+ expect(postRequests).toHaveLength(1);
658
+ });
659
+
660
+ test("re-enables the button and drops the spinner once the request settles", async () => {
661
+ renderButton(SLACK_PROPS);
662
+
663
+ const deferred: Deferred<PostResult> = holdNextPosts();
664
+
665
+ await clickSend(getSendButton("#alerts"));
666
+ expect(getSendButton("#alerts")).toBeDisabled();
667
+
668
+ await settle(deferred, { resolveWith: successResponse() });
669
+ await waitForSent();
670
+
671
+ const button: HTMLElement = getSendButton("#alerts");
672
+
673
+ expect(button).toBeEnabled();
674
+ expect(button).toHaveAttribute("aria-disabled", "false");
675
+ expect(getSpinner(button)).toBeNull();
676
+ });
677
+ });
678
+
679
+ describe("success", () => {
680
+ test('shows a "Sent" status that tells the user where to look', async () => {
681
+ renderButton(SLACK_PROPS);
682
+
683
+ await clickSend(getSendButton("#alerts"));
684
+ await waitForSent();
685
+
686
+ const sent: HTMLElement = screen.getByTestId(SENT_TEST_ID);
687
+
688
+ expect(sent).toHaveTextContent("Sent");
689
+ expect(sent).toHaveAttribute(
690
+ "title",
691
+ "Test notification sent to #alerts. Check Slack to confirm it arrived.",
692
+ );
693
+ expect(sent).toHaveClass("text-emerald-700");
694
+ expect(sent.querySelector("svg")).not.toBeNull();
695
+ // The badge is announced through the row's one live region.
696
+ expect(sent).not.toHaveAttribute("role");
697
+ expect(screen.getByRole("status")).toContainElement(sent);
698
+ });
699
+
700
+ test.each([
701
+ [
702
+ "a Slack channel",
703
+ SLACK_PROPS,
704
+ "Test notification sent to #alerts. Check Slack to confirm it arrived.",
705
+ ],
706
+ [
707
+ "a Microsoft Teams channel",
708
+ TEAMS_CHANNEL_PROPS,
709
+ "Test notification sent to General. Check Microsoft Teams to confirm it arrived.",
710
+ ],
711
+ [
712
+ "a Microsoft Teams chat",
713
+ TEAMS_CHAT_PROPS,
714
+ "Test notification sent to Incident War Room. Check Microsoft Teams to confirm it arrived.",
715
+ ],
716
+ ])(
717
+ "names the destination and workspace in the Sent title for %s",
718
+ async (_label: string, props: ComponentProps, expectedTitle: string) => {
719
+ renderButton(props);
720
+
721
+ await clickSend(getSendButton(props.destinationName));
722
+ await waitForSent();
723
+
724
+ expect(screen.getByTestId(SENT_TEST_ID)).toHaveAttribute(
725
+ "title",
726
+ expectedTitle,
727
+ );
728
+ },
729
+ );
730
+
731
+ test("shows no failure, no dialog and no error text on success", async () => {
732
+ renderButton(SLACK_PROPS);
733
+
734
+ await clickSend(getSendButton("#alerts"));
735
+ await waitForSent();
736
+
737
+ expect(queryFailedStatus()).not.toBeInTheDocument();
738
+ expect(queryErrorModal()).not.toBeInTheDocument();
739
+ expect(screen.queryByRole("alert")).not.toBeInTheDocument();
740
+ expect(friendlyErrorMessageArguments).toHaveLength(0);
741
+ expect(getSendButton("#alerts")).toBeEnabled();
742
+ });
743
+
744
+ test("treats any non-error response as success, whatever its body", async () => {
745
+ apiPost = async (): Promise<PostResult> => {
746
+ return new HTTPResponse<JSONObject>(200, { ok: true }, {});
747
+ };
748
+
749
+ renderButton(TEAMS_CHAT_PROPS);
750
+
751
+ await clickSend(getSendButton("Incident War Room"));
752
+ await waitForSent();
753
+
754
+ expect(queryFailedStatus()).not.toBeInTheDocument();
755
+ });
756
+ });
757
+
758
+ describe("failure", () => {
759
+ const SERVER_MESSAGE: string =
760
+ "This chat is no longer connected to OneUptime. Add the OneUptime app to the chat in Microsoft Teams, click Refresh Chats, and try again.";
761
+
762
+ test("an HTTPErrorResponse result opens the failure dialog with the friendly error", async () => {
763
+ const errorResponse: HTTPErrorResponse = new HTTPErrorResponse(
764
+ 400,
765
+ { message: SERVER_MESSAGE },
766
+ {},
767
+ );
768
+
769
+ apiPost = async (): Promise<PostResult> => {
770
+ return errorResponse;
771
+ };
772
+
773
+ friendlyErrorMessage = (): string => {
774
+ return "Friendly: this chat is no longer connected.";
775
+ };
776
+
777
+ renderButton(TEAMS_CHAT_PROPS);
778
+
779
+ await clickSend(getSendButton("Incident War Room"));
780
+
781
+ const dialog: HTMLElement = await waitForErrorModal();
782
+
783
+ expect(within(dialog).getByTestId("modal-title")).toHaveTextContent(
784
+ ERROR_MODAL_TITLE,
785
+ );
786
+ expect(
787
+ within(dialog).getByTestId("confirm-modal-description"),
788
+ ).toHaveTextContent(
789
+ "OneUptime could not send a test notification to Incident War Room in Microsoft Teams.",
790
+ );
791
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
792
+ "Friendly: this chat is no longer connected.",
793
+ );
794
+
795
+ // The exact response object is what gets turned into the message.
796
+ expect(friendlyErrorMessageArguments).toHaveLength(1);
797
+ expect(friendlyErrorMessageArguments[0]).toBe(errorResponse);
798
+ });
799
+
800
+ test("shows the server's message when the friendly error is taken from the response", async () => {
801
+ apiPost = async (): Promise<PostResult> => {
802
+ return new HTTPErrorResponse(400, { message: SERVER_MESSAGE }, {});
803
+ };
804
+
805
+ renderButton(TEAMS_CHAT_PROPS);
806
+
807
+ await clickSend(getSendButton("Incident War Room"));
808
+
809
+ const dialog: HTMLElement = await waitForErrorModal();
810
+
811
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
812
+ SERVER_MESSAGE,
813
+ );
814
+ });
815
+
816
+ test.each([
817
+ [
818
+ "a Slack channel",
819
+ SLACK_PROPS,
820
+ "OneUptime could not send a test notification to #alerts in Slack.",
821
+ ],
822
+ [
823
+ "a Microsoft Teams channel",
824
+ TEAMS_CHANNEL_PROPS,
825
+ "OneUptime could not send a test notification to General in Microsoft Teams.",
826
+ ],
827
+ [
828
+ "a Microsoft Teams chat",
829
+ TEAMS_CHAT_PROPS,
830
+ "OneUptime could not send a test notification to Incident War Room in Microsoft Teams.",
831
+ ],
832
+ ])(
833
+ "names the destination and workspace in the dialog for %s",
834
+ async (
835
+ _label: string,
836
+ props: ComponentProps,
837
+ expectedDescription: string,
838
+ ) => {
839
+ apiPost = async (): Promise<PostResult> => {
840
+ return new HTTPErrorResponse(
841
+ 500,
842
+ { message: "Upstream failure." },
843
+ {},
844
+ );
845
+ };
846
+
847
+ renderButton(props);
848
+
849
+ await clickSend(getSendButton(props.destinationName));
850
+
851
+ const dialog: HTMLElement = await waitForErrorModal();
852
+
853
+ expect(
854
+ within(dialog).getByTestId("confirm-modal-description"),
855
+ ).toHaveTextContent(expectedDescription);
856
+ },
857
+ );
858
+
859
+ test('shows a "Failed" status carrying the error while the dialog is open', async () => {
860
+ apiPost = async (): Promise<PostResult> => {
861
+ return new HTTPErrorResponse(400, { message: SERVER_MESSAGE }, {});
862
+ };
863
+
864
+ renderButton(TEAMS_CHAT_PROPS);
865
+
866
+ await clickSend(getSendButton("Incident War Room"));
867
+ await waitForErrorModal();
868
+
869
+ const failed: HTMLElement = screen.getByTestId(FAILED_TEST_ID);
870
+
871
+ expect(failed).toHaveTextContent("Failed");
872
+ expect(failed).toHaveAttribute("title", SERVER_MESSAGE);
873
+ expect(failed).toHaveClass("text-red-700");
874
+ expect(failed.querySelector("svg")).not.toBeNull();
875
+ // The badge is announced through the row's one live region.
876
+ expect(failed).not.toHaveAttribute("role");
877
+ expect(screen.getByRole("status")).toContainElement(failed);
878
+
879
+ expect(querySentStatus()).not.toBeInTheDocument();
880
+ });
881
+
882
+ test('the dialog\'s footer has exactly one button, "Close", and no "Cancel"', async () => {
883
+ apiPost = async (): Promise<PostResult> => {
884
+ return new HTTPErrorResponse(400, { message: SERVER_MESSAGE }, {});
885
+ };
886
+
887
+ renderButton(TEAMS_CHAT_PROPS);
888
+
889
+ await clickSend(getSendButton("Incident War Room"));
890
+
891
+ const dialog: HTMLElement = await waitForErrorModal();
892
+ const footer: HTMLElement = within(dialog).getByTestId("modal-footer");
893
+ const footerButtons: Array<HTMLElement> =
894
+ within(footer).getAllByRole("button");
895
+
896
+ /*
897
+ * The dialog only reports an outcome, so it offers one way out. A
898
+ * "Cancel" beside "Close" would be two buttons doing the same thing.
899
+ */
900
+ expect(footerButtons).toHaveLength(1);
901
+ expect(footerButtons[0]).toHaveTextContent("Close");
902
+ expect(footerButtons[0]).toHaveAttribute(
903
+ "data-testid",
904
+ "modal-footer-submit-button",
905
+ );
906
+ expect(
907
+ within(dialog).queryByTestId("modal-footer-close-button"),
908
+ ).not.toBeInTheDocument();
909
+ expect(
910
+ within(dialog).queryByRole("button", { name: /cancel/i }),
911
+ ).not.toBeInTheDocument();
912
+ expect(within(dialog).queryByText(/cancel/i)).not.toBeInTheDocument();
913
+ expect(dialog).toHaveAttribute("aria-modal", "true");
914
+ });
915
+
916
+ test("re-enables the button after a failure", async () => {
917
+ apiPost = async (): Promise<PostResult> => {
918
+ return new HTTPErrorResponse(400, { message: SERVER_MESSAGE }, {});
919
+ };
920
+
921
+ renderButton(TEAMS_CHAT_PROPS);
922
+
923
+ await clickSend(getSendButton("Incident War Room"));
924
+ await waitForErrorModal();
925
+
926
+ const button: HTMLElement = getSendButton("Incident War Room");
927
+
928
+ expect(button).toBeEnabled();
929
+ expect(getSpinner(button)).toBeNull();
930
+ });
931
+
932
+ test("a rejected request opens the same dialog with the friendly error", async () => {
933
+ const networkError: Error = new Error("Network Error");
934
+
935
+ apiPost = async (): Promise<PostResult> => {
936
+ throw networkError;
937
+ };
938
+
939
+ friendlyErrorMessage = (): string => {
940
+ return "Could not reach OneUptime. Check your connection.";
941
+ };
942
+
943
+ renderButton(SLACK_PROPS);
944
+
945
+ await clickSend(getSendButton("#alerts"));
946
+
947
+ const dialog: HTMLElement = await waitForErrorModal();
948
+
949
+ expect(within(dialog).getByTestId("modal-title")).toHaveTextContent(
950
+ ERROR_MODAL_TITLE,
951
+ );
952
+ expect(
953
+ within(dialog).getByTestId("confirm-modal-description"),
954
+ ).toHaveTextContent(
955
+ "OneUptime could not send a test notification to #alerts in Slack.",
956
+ );
957
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
958
+ "Could not reach OneUptime. Check your connection.",
959
+ );
960
+
961
+ expect(friendlyErrorMessageArguments[0]).toBe(networkError);
962
+
963
+ const failed: HTMLElement = screen.getByTestId(FAILED_TEST_ID);
964
+
965
+ expect(failed).toHaveAttribute(
966
+ "title",
967
+ "Could not reach OneUptime. Check your connection.",
968
+ );
969
+ expect(querySentStatus()).not.toBeInTheDocument();
970
+ });
971
+
972
+ test("a request that fails before returning a promise is still reported", async () => {
973
+ apiPost = (): Promise<PostResult> => {
974
+ throw new Error("URL is required for static method");
975
+ };
976
+
977
+ renderButton(SLACK_PROPS);
978
+
979
+ await clickSend(getSendButton("#alerts"));
980
+
981
+ const dialog: HTMLElement = await waitForErrorModal();
982
+
983
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
984
+ "URL is required for static method",
985
+ );
986
+ expect(screen.getByTestId(FAILED_TEST_ID)).toBeInTheDocument();
987
+ expect(getSendButton("#alerts")).toBeEnabled();
988
+ });
989
+
990
+ test("an HTTPErrorResponse thrown as a rejection is reported like a returned one", async () => {
991
+ const errorResponse: HTTPErrorResponse = new HTTPErrorResponse(
992
+ 403,
993
+ {
994
+ message:
995
+ "You do not have permission to send test notifications in this project.",
996
+ },
997
+ {},
998
+ );
999
+
1000
+ apiPost = async (): Promise<PostResult> => {
1001
+ throw errorResponse;
1002
+ };
1003
+
1004
+ renderButton(TEAMS_CHANNEL_PROPS);
1005
+
1006
+ await clickSend(getSendButton("General"));
1007
+
1008
+ const dialog: HTMLElement = await waitForErrorModal();
1009
+
1010
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
1011
+ "You do not have permission to send test notifications in this project.",
1012
+ );
1013
+ expect(friendlyErrorMessageArguments[0]).toBe(errorResponse);
1014
+ });
1015
+
1016
+ test("a failure that settles later, after the in-flight state, lands in the dialog", async () => {
1017
+ renderButton(SLACK_PROPS);
1018
+
1019
+ const deferred: Deferred<PostResult> = holdNextPosts();
1020
+
1021
+ await clickSend(getSendButton("#alerts"));
1022
+ expect(queryErrorModal()).not.toBeInTheDocument();
1023
+
1024
+ await settle(deferred, {
1025
+ resolveWith: new HTTPErrorResponse(
1026
+ 400,
1027
+ { message: "The Slack channel id is not valid." },
1028
+ {},
1029
+ ),
1030
+ });
1031
+
1032
+ const dialog: HTMLElement = await waitForErrorModal();
1033
+
1034
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
1035
+ "The Slack channel id is not valid.",
1036
+ );
1037
+ });
1038
+
1039
+ test("still reports a failure when no friendly message can be derived", async () => {
1040
+ apiPost = async (): Promise<PostResult> => {
1041
+ throw new Error("");
1042
+ };
1043
+
1044
+ friendlyErrorMessage = (): string => {
1045
+ return "";
1046
+ };
1047
+
1048
+ renderButton(SLACK_PROPS);
1049
+
1050
+ await clickSend(getSendButton("#alerts"));
1051
+
1052
+ const dialog: HTMLElement = await waitForErrorModal();
1053
+
1054
+ expect(screen.getByTestId(FAILED_TEST_ID)).toBeInTheDocument();
1055
+ // The dialog never shows an empty error banner.
1056
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
1057
+ "Could not send the test notification. Please try again.",
1058
+ );
1059
+ });
1060
+
1061
+ test("does not retry on its own after a failure", async () => {
1062
+ apiPost = async (): Promise<PostResult> => {
1063
+ return new HTTPErrorResponse(400, { message: SERVER_MESSAGE }, {});
1064
+ };
1065
+
1066
+ renderButton(TEAMS_CHAT_PROPS);
1067
+
1068
+ await clickSend(getSendButton("Incident War Room"));
1069
+ await waitForErrorModal();
1070
+
1071
+ expect(postRequests).toHaveLength(1);
1072
+ });
1073
+ });
1074
+
1075
+ describe("closing the failure dialog", () => {
1076
+ async function closeErrorModal(): Promise<void> {
1077
+ await act(async (): Promise<void> => {
1078
+ fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
1079
+ });
1080
+
1081
+ await waitFor(() => {
1082
+ expect(queryErrorModal()).not.toBeInTheDocument();
1083
+ });
1084
+ }
1085
+
1086
+ test('closing with "Close" leaves a "Failed" status and an enabled button', async () => {
1087
+ apiPost = async (): Promise<PostResult> => {
1088
+ return new HTTPErrorResponse(
1089
+ 400,
1090
+ { message: "The Slack channel id is not valid." },
1091
+ {},
1092
+ );
1093
+ };
1094
+
1095
+ renderButton(SLACK_PROPS);
1096
+
1097
+ await clickSend(getSendButton("#alerts"));
1098
+ await waitForErrorModal();
1099
+
1100
+ await closeErrorModal();
1101
+
1102
+ const failed: HTMLElement = screen.getByTestId(FAILED_TEST_ID);
1103
+
1104
+ expect(failed).toHaveTextContent("Failed");
1105
+ // The error survives as the badge's tooltip once the dialog is gone.
1106
+ expect(failed).toHaveAttribute(
1107
+ "title",
1108
+ "The Slack channel id is not valid.",
1109
+ );
1110
+ expect(screen.queryByRole("alert")).not.toBeInTheDocument();
1111
+ expect(querySentStatus()).not.toBeInTheDocument();
1112
+
1113
+ const button: HTMLElement = getSendButton("#alerts");
1114
+
1115
+ expect(button).toBeEnabled();
1116
+ expect(getSpinner(button)).toBeNull();
1117
+
1118
+ // Dismissing the dialog is not a retry.
1119
+ expect(postRequests).toHaveLength(1);
1120
+ });
1121
+
1122
+ test("the dialog stays closed until the next failure", async () => {
1123
+ apiPost = async (): Promise<PostResult> => {
1124
+ return new HTTPErrorResponse(400, { message: "First failure." }, {});
1125
+ };
1126
+
1127
+ const { rerender } = renderButton(SLACK_PROPS);
1128
+
1129
+ await clickSend(getSendButton("#alerts"));
1130
+ await waitForErrorModal();
1131
+
1132
+ await closeErrorModal();
1133
+
1134
+ /*
1135
+ * The card re-renders its rows whenever its own state changes, so the
1136
+ * button gets the same props again - once as the very same object, and
1137
+ * once with a request body that is equal but freshly built, the way an
1138
+ * inline `{ channelId: channel.id }` arrives on every render. Neither
1139
+ * may bring the dialog back or clear the badge.
1140
+ */
1141
+ rerender(<SendTestNotificationButton {...SLACK_PROPS} />);
1142
+
1143
+ await act(async (): Promise<void> => {
1144
+ await Promise.resolve();
1145
+ });
1146
+
1147
+ expect(queryErrorModal()).not.toBeInTheDocument();
1148
+ expect(screen.getByTestId(FAILED_TEST_ID)).toHaveAttribute(
1149
+ "title",
1150
+ "First failure.",
1151
+ );
1152
+
1153
+ const equalButNewRequestBody: JSONObject = { channelId: "C0123ALERTS" };
1154
+
1155
+ expect(equalButNewRequestBody).not.toBe(SLACK_PROPS.requestBody);
1156
+ expect(equalButNewRequestBody).toEqual(SLACK_PROPS.requestBody);
1157
+
1158
+ rerender(
1159
+ <SendTestNotificationButton
1160
+ {...SLACK_PROPS}
1161
+ requestBody={equalButNewRequestBody}
1162
+ />,
1163
+ );
1164
+
1165
+ await act(async (): Promise<void> => {
1166
+ await Promise.resolve();
1167
+ });
1168
+
1169
+ expect(queryErrorModal()).not.toBeInTheDocument();
1170
+ expect(screen.getByTestId(FAILED_TEST_ID)).toHaveAttribute(
1171
+ "title",
1172
+ "First failure.",
1173
+ );
1174
+ expect(screen.queryByRole("alert")).not.toBeInTheDocument();
1175
+ expect(getSendButton("#alerts")).toBeEnabled();
1176
+
1177
+ // Re-rendering never sends anything on its own.
1178
+ expect(postRequests).toHaveLength(1);
1179
+
1180
+ // The next failure opens it again, with its own error.
1181
+ apiPost = async (): Promise<PostResult> => {
1182
+ return new HTTPErrorResponse(400, { message: "Second failure." }, {});
1183
+ };
1184
+
1185
+ await clickSend(getSendButton("#alerts"));
1186
+
1187
+ const dialog: HTMLElement = await waitForErrorModal();
1188
+
1189
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
1190
+ "Second failure.",
1191
+ );
1192
+ expect(postRequests).toHaveLength(2);
1193
+ expect(postRequests[1]!.data).toEqual({ channelId: "C0123ALERTS" });
1194
+ });
1195
+ });
1196
+
1197
+ describe("retrying", () => {
1198
+ test('a retry that succeeds shows "Sent" and drops the "Failed" status', async () => {
1199
+ apiPost = async (): Promise<PostResult> => {
1200
+ return new HTTPErrorResponse(
1201
+ 400,
1202
+ { message: "The Slack channel id is not valid." },
1203
+ {},
1204
+ );
1205
+ };
1206
+
1207
+ renderButton(SLACK_PROPS);
1208
+
1209
+ await clickSend(getSendButton("#alerts"));
1210
+ await waitForErrorModal();
1211
+
1212
+ await act(async (): Promise<void> => {
1213
+ fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
1214
+ });
1215
+
1216
+ await waitFor(() => {
1217
+ expect(queryErrorModal()).not.toBeInTheDocument();
1218
+ });
1219
+
1220
+ expect(screen.getByTestId(FAILED_TEST_ID)).toBeInTheDocument();
1221
+
1222
+ apiPost = defaultApiPost;
1223
+
1224
+ await clickSend(getSendButton("#alerts"));
1225
+ await waitForSent();
1226
+
1227
+ expect(queryFailedStatus()).not.toBeInTheDocument();
1228
+ expect(queryErrorModal()).not.toBeInTheDocument();
1229
+ expect(screen.queryByRole("alert")).not.toBeInTheDocument();
1230
+ expect(screen.getAllByRole("status")).toHaveLength(1);
1231
+
1232
+ expect(postRequests).toHaveLength(2);
1233
+ expect(postRequests[1]!.data).toEqual(postRequests[0]!.data);
1234
+ expect(postRequests[1]!.url.toString()).toBe(
1235
+ postRequests[0]!.url.toString(),
1236
+ );
1237
+ });
1238
+
1239
+ test('the old "Failed" status and error clear as soon as the retry starts', async () => {
1240
+ apiPost = async (): Promise<PostResult> => {
1241
+ return new HTTPErrorResponse(400, { message: "First failure." }, {});
1242
+ };
1243
+
1244
+ renderButton(SLACK_PROPS);
1245
+
1246
+ await clickSend(getSendButton("#alerts"));
1247
+ await waitForErrorModal();
1248
+
1249
+ await act(async (): Promise<void> => {
1250
+ fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
1251
+ });
1252
+
1253
+ await waitFor(() => {
1254
+ expect(queryErrorModal()).not.toBeInTheDocument();
1255
+ });
1256
+
1257
+ const deferred: Deferred<PostResult> = holdNextPosts();
1258
+
1259
+ await clickSend(getSendButton("#alerts"));
1260
+
1261
+ expect(queryFailedStatus()).not.toBeInTheDocument();
1262
+ expect(querySentStatus()).not.toBeInTheDocument();
1263
+ expect(screen.queryByText("First failure.")).not.toBeInTheDocument();
1264
+ expect(getSendButton("#alerts")).toBeDisabled();
1265
+
1266
+ await settle(deferred, { resolveWith: successResponse() });
1267
+ await waitForSent();
1268
+ });
1269
+
1270
+ test('a retry after success clears "Sent" while in flight and reports a new failure', async () => {
1271
+ renderButton(SLACK_PROPS);
1272
+
1273
+ await clickSend(getSendButton("#alerts"));
1274
+ await waitForSent();
1275
+
1276
+ const deferred: Deferred<PostResult> = holdNextPosts();
1277
+
1278
+ await clickSend(getSendButton("#alerts"));
1279
+
1280
+ expect(querySentStatus()).not.toBeInTheDocument();
1281
+ expect(getSendButton("#alerts")).toBeDisabled();
1282
+
1283
+ await settle(deferred, {
1284
+ resolveWith: new HTTPErrorResponse(
1285
+ 400,
1286
+ { message: "The bot was removed from the channel." },
1287
+ {},
1288
+ ),
1289
+ });
1290
+
1291
+ const dialog: HTMLElement = await waitForErrorModal();
1292
+
1293
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
1294
+ "The bot was removed from the channel.",
1295
+ );
1296
+ expect(screen.getByTestId(FAILED_TEST_ID)).toBeInTheDocument();
1297
+ expect(querySentStatus()).not.toBeInTheDocument();
1298
+ expect(postRequests).toHaveLength(2);
1299
+ });
1300
+
1301
+ test("a second failure shows only the new error", async () => {
1302
+ apiPost = async (): Promise<PostResult> => {
1303
+ return new HTTPErrorResponse(400, { message: "First failure." }, {});
1304
+ };
1305
+
1306
+ renderButton(SLACK_PROPS);
1307
+
1308
+ await clickSend(getSendButton("#alerts"));
1309
+ await waitForErrorModal();
1310
+
1311
+ await act(async (): Promise<void> => {
1312
+ fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
1313
+ });
1314
+
1315
+ await waitFor(() => {
1316
+ expect(queryErrorModal()).not.toBeInTheDocument();
1317
+ });
1318
+
1319
+ apiPost = async (): Promise<PostResult> => {
1320
+ return new HTTPErrorResponse(400, { message: "Second failure." }, {});
1321
+ };
1322
+
1323
+ await clickSend(getSendButton("#alerts"));
1324
+
1325
+ const dialog: HTMLElement = await waitForErrorModal();
1326
+
1327
+ expect(within(dialog).getByRole("alert")).toHaveTextContent(
1328
+ "Second failure.",
1329
+ );
1330
+ expect(screen.queryByText("First failure.")).not.toBeInTheDocument();
1331
+ expect(screen.getByTestId(FAILED_TEST_ID)).toHaveAttribute(
1332
+ "title",
1333
+ "Second failure.",
1334
+ );
1335
+ expect(screen.getAllByRole("dialog")).toHaveLength(1);
1336
+ });
1337
+
1338
+ test("many sequential sends each post exactly once", async () => {
1339
+ renderButton(TEAMS_CHANNEL_PROPS);
1340
+
1341
+ for (let i: number = 1; i <= 3; i++) {
1342
+ await clickSend(getSendButton("General"));
1343
+ await waitForSent();
1344
+ expect(postRequests).toHaveLength(i);
1345
+ }
1346
+ });
1347
+ });
1348
+
1349
+ describe("several buttons on one page", () => {
1350
+ const ALERTS_PROPS: ComponentProps = SLACK_PROPS;
1351
+ const OPS_PROPS: ComponentProps = {
1352
+ route: "/slack/channels/test",
1353
+ requestBody: { channelId: "C0456OPS" },
1354
+ destinationName: "#ops",
1355
+ workspaceName: "Slack",
1356
+ };
1357
+
1358
+ function renderTwoRows(): { alertsRow: HTMLElement; opsRow: HTMLElement } {
1359
+ render(
1360
+ <ul>
1361
+ <li data-testid="row-alerts">
1362
+ <span>#alerts</span>
1363
+ <SendTestNotificationButton {...ALERTS_PROPS} />
1364
+ </li>
1365
+ <li data-testid="row-ops">
1366
+ <span>#ops</span>
1367
+ <SendTestNotificationButton {...OPS_PROPS} />
1368
+ </li>
1369
+ </ul>,
1370
+ );
1371
+
1372
+ return {
1373
+ alertsRow: screen.getByTestId("row-alerts"),
1374
+ opsRow: screen.getByTestId("row-ops"),
1375
+ };
1376
+ }
1377
+
1378
+ test("each row posts its own destination", async () => {
1379
+ renderTwoRows();
1380
+
1381
+ await clickSend(getSendButton("#ops"));
1382
+ await waitFor(() => {
1383
+ expect(postRequests).toHaveLength(1);
1384
+ });
1385
+
1386
+ expect(postRequests[0]!.data).toEqual({ channelId: "C0456OPS" });
1387
+ });
1388
+
1389
+ test("a success on one row leaves the other untouched", async () => {
1390
+ const { alertsRow, opsRow } = renderTwoRows();
1391
+
1392
+ await clickSend(getSendButton("#alerts"));
1393
+ await waitForSent(alertsRow);
1394
+
1395
+ expect(postRequests).toHaveLength(1);
1396
+ expect(postRequests[0]!.data).toEqual({ channelId: "C0123ALERTS" });
1397
+
1398
+ expect(querySentStatus(alertsRow)).toBeInTheDocument();
1399
+ expect(querySentStatus(opsRow)).not.toBeInTheDocument();
1400
+ expect(queryFailedStatus(opsRow)).not.toBeInTheDocument();
1401
+ expect(getSendButton("#ops")).toBeEnabled();
1402
+ });
1403
+
1404
+ test("a failure on one row does not disturb the other row's success", async () => {
1405
+ const { alertsRow, opsRow } = renderTwoRows();
1406
+
1407
+ await clickSend(getSendButton("#alerts"));
1408
+ await waitForSent(alertsRow);
1409
+
1410
+ apiPost = async (): Promise<PostResult> => {
1411
+ return new HTTPErrorResponse(
1412
+ 400,
1413
+ { message: "OneUptime is not in #ops." },
1414
+ {},
1415
+ );
1416
+ };
1417
+
1418
+ await clickSend(getSendButton("#ops"));
1419
+
1420
+ const dialog: HTMLElement = await waitForErrorModal();
1421
+
1422
+ // The dialog names the row that failed, not its neighbour.
1423
+ expect(
1424
+ within(dialog).getByTestId("confirm-modal-description"),
1425
+ ).toHaveTextContent(
1426
+ "OneUptime could not send a test notification to #ops in Slack.",
1427
+ );
1428
+
1429
+ expect(queryFailedStatus(opsRow)).toBeInTheDocument();
1430
+ expect(querySentStatus(opsRow)).not.toBeInTheDocument();
1431
+
1432
+ expect(querySentStatus(alertsRow)).toBeInTheDocument();
1433
+ expect(queryFailedStatus(alertsRow)).not.toBeInTheDocument();
1434
+
1435
+ expect(screen.getAllByRole("dialog")).toHaveLength(1);
1436
+ });
1437
+
1438
+ test("a send in flight on one row does not disable the other", async () => {
1439
+ const { alertsRow, opsRow } = renderTwoRows();
1440
+
1441
+ const alertsDeferred: Deferred<PostResult> = holdNextPosts();
1442
+
1443
+ await clickSend(getSendButton("#alerts"));
1444
+
1445
+ expect(getSendButton("#alerts")).toBeDisabled();
1446
+ expect(getSendButton("#ops")).toBeEnabled();
1447
+ expect(getSpinner(getSendButton("#ops"))).toBeNull();
1448
+
1449
+ const opsDeferred: Deferred<PostResult> = holdNextPosts();
1450
+
1451
+ await clickSend(getSendButton("#ops"));
1452
+
1453
+ expect(postRequests).toHaveLength(2);
1454
+ expect(postRequests[0]!.data).toEqual({ channelId: "C0123ALERTS" });
1455
+ expect(postRequests[1]!.data).toEqual({ channelId: "C0456OPS" });
1456
+
1457
+ // Settle them out of order: ops first, then alerts.
1458
+ await settle(opsDeferred, { resolveWith: successResponse() });
1459
+ await waitForSent(opsRow);
1460
+
1461
+ expect(getSendButton("#alerts")).toBeDisabled();
1462
+ expect(querySentStatus(alertsRow)).not.toBeInTheDocument();
1463
+
1464
+ await settle(alertsDeferred, { resolveWith: successResponse() });
1465
+ await waitForSent(alertsRow);
1466
+
1467
+ expect(getSendButton("#alerts")).toBeEnabled();
1468
+ expect(getSendButton("#ops")).toBeEnabled();
1469
+ });
1470
+
1471
+ test("closing one row's dialog keeps that row's Failed status and nothing else changes", async () => {
1472
+ const { alertsRow, opsRow } = renderTwoRows();
1473
+
1474
+ apiPost = async (): Promise<PostResult> => {
1475
+ return new HTTPErrorResponse(400, { message: "Nope." }, {});
1476
+ };
1477
+
1478
+ await clickSend(getSendButton("#alerts"));
1479
+ await waitForErrorModal();
1480
+
1481
+ await act(async (): Promise<void> => {
1482
+ fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
1483
+ });
1484
+
1485
+ await waitFor(() => {
1486
+ expect(queryErrorModal()).not.toBeInTheDocument();
1487
+ });
1488
+
1489
+ expect(queryFailedStatus(alertsRow)).toBeInTheDocument();
1490
+ expect(queryFailedStatus(opsRow)).not.toBeInTheDocument();
1491
+ expect(querySentStatus(opsRow)).not.toBeInTheDocument();
1492
+ });
1493
+ });
1494
+
1495
+ describe("changing props", () => {
1496
+ test("the next click posts the new request body and route", async () => {
1497
+ const { rerender } = renderButton(TEAMS_CHANNEL_PROPS);
1498
+
1499
+ await clickSend(getSendButton("General"));
1500
+ await waitForSent();
1501
+
1502
+ const nextProps: ComponentProps = {
1503
+ ...TEAMS_CHANNEL_PROPS,
1504
+ requestBody: {
1505
+ teamId: "e5f6-team-support",
1506
+ channelId: "19:escalations@thread.tacv2",
1507
+ },
1508
+ destinationName: "Escalations",
1509
+ };
1510
+
1511
+ rerender(<SendTestNotificationButton {...nextProps} />);
1512
+
1513
+ await clickSend(getSendButton("Escalations"));
1514
+
1515
+ await waitFor(() => {
1516
+ expect(postRequests).toHaveLength(2);
1517
+ });
1518
+
1519
+ expect(postRequests[0]!.data).toEqual({
1520
+ teamId: "a1b2c3d4-team-platform",
1521
+ channelId: "19:general-thread@thread.tacv2",
1522
+ });
1523
+ expect(postRequests[1]!.data).toEqual({
1524
+ teamId: "e5f6-team-support",
1525
+ channelId: "19:escalations@thread.tacv2",
1526
+ });
1527
+
1528
+ await waitForSent();
1529
+ });
1530
+
1531
+ test("a new route is used from the next click on", async () => {
1532
+ const { rerender } = renderButton(SLACK_PROPS);
1533
+
1534
+ await clickSend(getSendButton("#alerts"));
1535
+ await waitForSent();
1536
+
1537
+ rerender(<SendTestNotificationButton {...TEAMS_CHAT_PROPS} />);
1538
+
1539
+ await clickSend(getSendButton("Incident War Room"));
1540
+ await waitForSent();
1541
+
1542
+ expect(postRequests).toHaveLength(2);
1543
+ expect(
1544
+ postRequests[0]!.url.toString().endsWith("/slack/channels/test"),
1545
+ ).toBe(true);
1546
+ expect(
1547
+ postRequests[1]!.url.toString().endsWith("/microsoft-teams/chats/test"),
1548
+ ).toBe(true);
1549
+ expect(postRequests[1]!.data).toEqual({
1550
+ chatId: "19:groupchat-war-room@thread.v2",
1551
+ });
1552
+ });
1553
+
1554
+ test("the accessible name follows the destination name", () => {
1555
+ const { rerender } = renderButton(SLACK_PROPS);
1556
+
1557
+ expect(getSendButton("#alerts")).toBeInTheDocument();
1558
+
1559
+ rerender(
1560
+ <SendTestNotificationButton
1561
+ {...SLACK_PROPS}
1562
+ destinationName="#renamed"
1563
+ />,
1564
+ );
1565
+
1566
+ expect(getSendButton("#renamed")).toBeInTheDocument();
1567
+ expect(
1568
+ screen.queryByRole("button", {
1569
+ name: "Send test notification to #alerts",
1570
+ }),
1571
+ ).not.toBeInTheDocument();
1572
+ });
1573
+
1574
+ test("a request already in flight keeps the body it was sent with", async () => {
1575
+ const { rerender } = renderButton(SLACK_PROPS);
1576
+
1577
+ const deferred: Deferred<PostResult> = holdNextPosts();
1578
+
1579
+ await clickSend(getSendButton("#alerts"));
1580
+
1581
+ rerender(
1582
+ <SendTestNotificationButton
1583
+ {...SLACK_PROPS}
1584
+ requestBody={{ channelId: "C0999OTHER" }}
1585
+ destinationName="#other"
1586
+ />,
1587
+ );
1588
+
1589
+ await settle(deferred, { resolveWith: successResponse() });
1590
+ await waitForSent();
1591
+
1592
+ expect(postRequests).toHaveLength(1);
1593
+ expect(postRequests[0]!.data).toEqual({ channelId: "C0123ALERTS" });
1594
+ });
1595
+
1596
+ test("the failure dialog names the destination the row shows now", async () => {
1597
+ const { rerender } = renderButton(SLACK_PROPS);
1598
+
1599
+ rerender(
1600
+ <SendTestNotificationButton
1601
+ {...SLACK_PROPS}
1602
+ requestBody={{ channelId: "C0777INC" }}
1603
+ destinationName="#incidents"
1604
+ />,
1605
+ );
1606
+
1607
+ apiPost = async (): Promise<PostResult> => {
1608
+ return new HTTPErrorResponse(400, { message: "Nope." }, {});
1609
+ };
1610
+
1611
+ await clickSend(getSendButton("#incidents"));
1612
+
1613
+ const dialog: HTMLElement = await waitForErrorModal();
1614
+
1615
+ expect(
1616
+ within(dialog).getByTestId("confirm-modal-description"),
1617
+ ).toHaveTextContent(
1618
+ "OneUptime could not send a test notification to #incidents in Slack.",
1619
+ );
1620
+ expect(postRequests[0]!.data).toEqual({ channelId: "C0777INC" });
1621
+ });
1622
+ });
1623
+
1624
+ describe("reporting sends in flight (onSendingChange)", () => {
1625
+ /*
1626
+ * The cards count sends in flight through this callback and lock
1627
+ * Refresh (and the Teams team picker) while the count is above zero, so
1628
+ * every send has to report exactly one true when it starts and exactly
1629
+ * one false when it settles - an unmatched true would lock the card for
1630
+ * good, an extra false would unlock it while a send is still out.
1631
+ */
1632
+ let sendingChanges: Array<boolean> = [];
1633
+
1634
+ const recordSendingChange: (isSending: boolean) => void = (
1635
+ isSending: boolean,
1636
+ ): void => {
1637
+ sendingChanges.push(isSending);
1638
+ };
1639
+
1640
+ const withSendingChange: (props: ComponentProps) => ComponentProps = (
1641
+ props: ComponentProps,
1642
+ ): ComponentProps => {
1643
+ return { ...props, onSendingChange: recordSendingChange };
1644
+ };
1645
+
1646
+ beforeEach(() => {
1647
+ sendingChanges = [];
1648
+ });
1649
+
1650
+ test("reports nothing on mount", () => {
1651
+ renderButton(withSendingChange(SLACK_PROPS));
1652
+
1653
+ expect(sendingChanges).toEqual([]);
1654
+ });
1655
+
1656
+ test.each([
1657
+ [
1658
+ "succeeds",
1659
+ { resolveWith: successResponse() },
1660
+ (): Promise<void> => {
1661
+ return waitForSent();
1662
+ },
1663
+ ],
1664
+ [
1665
+ "comes back as an HTTPErrorResponse",
1666
+ {
1667
+ resolveWith: new HTTPErrorResponse(400, { message: "Nope." }, {}),
1668
+ },
1669
+ async (): Promise<void> => {
1670
+ await waitForErrorModal();
1671
+ },
1672
+ ],
1673
+ [
1674
+ "is rejected with an error",
1675
+ { rejectWith: new Error("Network Error") },
1676
+ async (): Promise<void> => {
1677
+ await waitForErrorModal();
1678
+ },
1679
+ ],
1680
+ [
1681
+ "is rejected with an HTTPErrorResponse",
1682
+ {
1683
+ rejectWith: new HTTPErrorResponse(403, { message: "Denied." }, {}),
1684
+ },
1685
+ async (): Promise<void> => {
1686
+ await waitForErrorModal();
1687
+ },
1688
+ ],
1689
+ ])(
1690
+ "a send that %s reports true when it starts and false once it settles",
1691
+ async (
1692
+ _label: string,
1693
+ outcome: { resolveWith: PostResult } | { rejectWith: unknown },
1694
+ waitForResult: () => Promise<void>,
1695
+ ) => {
1696
+ renderButton(withSendingChange(SLACK_PROPS));
1697
+
1698
+ const deferred: Deferred<PostResult> = holdNextPosts();
1699
+
1700
+ await clickSend(getSendButton("#alerts"));
1701
+
1702
+ // Started, not yet settled.
1703
+ expect(postRequests).toHaveLength(1);
1704
+ expect(sendingChanges).toEqual([true]);
1705
+
1706
+ await settle(deferred, outcome);
1707
+ await waitForResult();
1708
+
1709
+ expect(sendingChanges).toEqual([true, false]);
1710
+ },
1711
+ );
1712
+
1713
+ test("a request that fails before returning a promise still reports true then false", async () => {
1714
+ apiPost = (): Promise<PostResult> => {
1715
+ throw new Error("URL is required for static method");
1716
+ };
1717
+
1718
+ renderButton(withSendingChange(SLACK_PROPS));
1719
+
1720
+ await clickSend(getSendButton("#alerts"));
1721
+ await waitForErrorModal();
1722
+
1723
+ expect(sendingChanges).toEqual([true, false]);
1724
+ });
1725
+
1726
+ test("two clicks in the same frame while in flight still report one true and one false", async () => {
1727
+ renderButton(withSendingChange(SLACK_PROPS));
1728
+
1729
+ const deferred: Deferred<PostResult> = holdNextPosts();
1730
+ const button: HTMLElement = getSendButton("#alerts");
1731
+
1732
+ /*
1733
+ * Both clicks land before the disabled state renders; the second is
1734
+ * dropped by the in-flight guard, and a dropped click must not report
1735
+ * anything either.
1736
+ */
1737
+ act(() => {
1738
+ button.click();
1739
+ button.click();
1740
+ });
1741
+
1742
+ expect(postRequests).toHaveLength(1);
1743
+ expect(sendingChanges).toEqual([true]);
1744
+
1745
+ // A later click on the now-disabled button reports nothing as well.
1746
+ await clickSend(getSendButton("#alerts"));
1747
+
1748
+ expect(postRequests).toHaveLength(1);
1749
+ expect(sendingChanges).toEqual([true]);
1750
+
1751
+ await settle(deferred, { resolveWith: successResponse() });
1752
+ await waitForSent();
1753
+
1754
+ expect(postRequests).toHaveLength(1);
1755
+ expect(sendingChanges).toEqual([true, false]);
1756
+ });
1757
+
1758
+ test("each send reports its own pair, in order", async () => {
1759
+ renderButton(withSendingChange(SLACK_PROPS));
1760
+
1761
+ await clickSend(getSendButton("#alerts"));
1762
+ await waitForSent();
1763
+
1764
+ expect(sendingChanges).toEqual([true, false]);
1765
+
1766
+ apiPost = async (): Promise<PostResult> => {
1767
+ return new HTTPErrorResponse(400, { message: "Nope." }, {});
1768
+ };
1769
+
1770
+ await clickSend(getSendButton("#alerts"));
1771
+ await waitForErrorModal();
1772
+
1773
+ expect(sendingChanges).toEqual([true, false, true, false]);
1774
+ });
1775
+
1776
+ test("sending works the same when no onSendingChange is passed", async () => {
1777
+ renderButton(SLACK_PROPS);
1778
+
1779
+ expect(SLACK_PROPS.onSendingChange).toBeUndefined();
1780
+
1781
+ await clickSend(getSendButton("#alerts"));
1782
+ await waitForSent();
1783
+
1784
+ apiPost = async (): Promise<PostResult> => {
1785
+ return new HTTPErrorResponse(400, { message: "Nope." }, {});
1786
+ };
1787
+
1788
+ await clickSend(getSendButton("#alerts"));
1789
+
1790
+ const dialog: HTMLElement = await waitForErrorModal();
1791
+
1792
+ expect(within(dialog).getByRole("alert")).toHaveTextContent("Nope.");
1793
+ expect(screen.getByTestId(FAILED_TEST_ID)).toBeInTheDocument();
1794
+ expect(getSendButton("#alerts")).toBeEnabled();
1795
+ expect(postRequests).toHaveLength(2);
1796
+ });
1797
+ });
1798
+
1799
+ describe("unmounting mid-request", () => {
1800
+ /*
1801
+ * A card reload (Refresh, or a team switch in the Teams channels card)
1802
+ * unmounts its rows. A send still in flight then has no row to report
1803
+ * to, but its parent still has to hear that it settled, or the card's
1804
+ * count of sends in flight would never come back down.
1805
+ */
1806
+ test.each([
1807
+ ["succeeds", { resolveWith: successResponse() }],
1808
+ [
1809
+ "fails",
1810
+ {
1811
+ resolveWith: new HTTPErrorResponse(400, { message: "Late." }, {}),
1812
+ },
1813
+ ],
1814
+ ["rejects", { rejectWith: new Error("Late network error.") }],
1815
+ ])(
1816
+ "a request that %s after the row is gone still reports onSendingChange(false)",
1817
+ async (
1818
+ _label: string,
1819
+ outcome: { resolveWith: PostResult } | { rejectWith: unknown },
1820
+ ) => {
1821
+ const sendingChanges: Array<boolean> = [];
1822
+
1823
+ const { unmount } = renderButton({
1824
+ ...SLACK_PROPS,
1825
+ onSendingChange: (isSending: boolean): void => {
1826
+ sendingChanges.push(isSending);
1827
+ },
1828
+ });
1829
+
1830
+ const deferred: Deferred<PostResult> = holdNextPosts();
1831
+
1832
+ await clickSend(getSendButton("#alerts"));
1833
+
1834
+ expect(sendingChanges).toEqual([true]);
1835
+
1836
+ unmount();
1837
+
1838
+ // Unmounting alone does not settle the send.
1839
+ expect(sendingChanges).toEqual([true]);
1840
+
1841
+ await settle(deferred, outcome);
1842
+
1843
+ await waitFor(() => {
1844
+ expect(sendingChanges).toEqual([true, false]);
1845
+ });
1846
+
1847
+ // Nothing is left on screen to show the late result.
1848
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
1849
+ expect(screen.queryByRole("status")).not.toBeInTheDocument();
1850
+ expect(postRequests).toHaveLength(1);
1851
+ },
1852
+ );
1853
+ });
1854
+ });