@oneuptime/common 12.0.13 → 12.0.14

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 (376) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  2. package/Models/DatabaseModels/UserCall.ts +94 -3
  3. package/Models/DatabaseModels/UserEmail.ts +94 -3
  4. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  5. package/Models/DatabaseModels/UserPush.ts +37 -0
  6. package/Models/DatabaseModels/UserSMS.ts +94 -3
  7. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  8. package/Server/API/BaseAPI.ts +79 -14
  9. package/Server/API/UserCallAPI.ts +53 -45
  10. package/Server/API/UserEmailAPI.ts +53 -44
  11. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  12. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  13. package/Server/API/UserPushAPI.ts +126 -2
  14. package/Server/API/UserSmsAPI.ts +53 -44
  15. package/Server/API/UserWhatsAppAPI.ts +53 -50
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  21. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  22. package/Server/Services/DatabaseService.ts +101 -0
  23. package/Server/Services/LogAggregationService.ts +5 -0
  24. package/Server/Services/PushNotificationService.ts +127 -12
  25. package/Server/Services/TeamMemberService.ts +53 -13
  26. package/Server/Services/UserCallService.ts +55 -24
  27. package/Server/Services/UserEmailService.ts +83 -17
  28. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  29. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  30. package/Server/Services/UserNotificationRuleService.ts +26 -0
  31. package/Server/Services/UserPushService.ts +89 -0
  32. package/Server/Services/UserSmsService.ts +50 -17
  33. package/Server/Services/UserWhatsAppService.ts +48 -15
  34. package/Server/Types/Database/QueryHelper.ts +30 -0
  35. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  36. package/Server/Utils/ChannelVerification.ts +510 -0
  37. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
  38. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  39. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  40. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  41. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  42. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  43. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  44. package/Server/Utils/TotpAuth.ts +137 -9
  45. package/Server/Utils/UserRegistrationToken.ts +182 -0
  46. package/Server/Utils/VerificationCode.ts +134 -0
  47. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  48. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  49. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  50. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  51. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  52. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  53. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  54. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  55. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  56. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  57. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  58. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  59. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  60. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  61. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
  62. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  63. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  64. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  65. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  66. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  67. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  68. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  69. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  70. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  71. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  72. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  73. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  74. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  75. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  76. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  77. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  78. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  79. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  80. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  81. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  82. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  83. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  84. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  85. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  86. package/Tests/Server/Utils/Browser.test.ts +162 -0
  87. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  88. package/Tests/Server/Utils/Express.test.ts +371 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  91. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  92. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  93. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  94. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  95. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  96. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  97. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  98. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  99. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  100. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  101. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  102. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  103. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  104. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  105. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  106. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  107. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  108. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  109. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  110. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  111. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  112. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  113. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  114. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  115. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  116. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  117. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  118. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  119. package/Tests/UI/Components/Button.test.tsx +175 -1
  120. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  121. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  122. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  123. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  124. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  125. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  126. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  127. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  128. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  129. package/Tests/UI/Components/List.test.tsx +9 -2
  130. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  131. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  132. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  133. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  134. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  135. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  136. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  137. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  138. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  139. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  140. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  141. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  142. package/Tests/UI/Components/Toast.test.tsx +130 -7
  143. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  144. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  145. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  146. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  147. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  148. package/Tests/Utils/API.test.ts +16 -2
  149. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  150. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  151. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  152. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  153. package/Types/Email/EmailTemplateType.ts +1 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  155. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  156. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  157. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  158. package/Types/Permission.ts +35 -0
  159. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  160. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  161. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  162. package/Types/Service/ServiceLanguage.ts +144 -0
  163. package/Types/Text.ts +111 -19
  164. package/UI/Components/Button/Button.tsx +27 -14
  165. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  166. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  167. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  168. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  169. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  170. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  171. package/UI/Components/CommandPalette/Types.ts +49 -0
  172. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  173. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  174. package/UI/Components/Feed/FeedItem.tsx +1 -1
  175. package/UI/Components/List/List.tsx +37 -5
  176. package/UI/Components/List/ListSkeleton.tsx +54 -0
  177. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  178. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  179. package/UI/Components/Modal/Modal.tsx +2 -2
  180. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  181. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  182. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  183. package/UI/Components/Navbar/NavBar.tsx +14 -1
  184. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  185. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  186. package/UI/Components/SideOver/SideOver.tsx +37 -2
  187. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  188. package/UI/Components/Table/Table.tsx +74 -44
  189. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  190. package/UI/Components/Tabs/Tab.tsx +1 -1
  191. package/UI/Components/Toast/Toast.tsx +118 -53
  192. package/UI/Components/Toast/ToastInit.tsx +82 -32
  193. package/UI/Styles/Theme.css +40 -0
  194. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  195. package/UI/Utils/ModelListCache.ts +188 -0
  196. package/UI/Utils/Project.ts +29 -0
  197. package/Utils/API.ts +6 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  201. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  203. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  205. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  207. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  209. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  211. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  212. package/build/dist/Server/API/BaseAPI.js +69 -13
  213. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  214. package/build/dist/Server/API/UserCallAPI.js +42 -38
  215. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  216. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  217. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  218. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  219. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  220. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  221. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  222. package/build/dist/Server/API/UserPushAPI.js +95 -2
  223. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  224. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  225. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  226. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  227. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  236. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  237. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  239. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  240. package/build/dist/Server/Services/DatabaseService.js +74 -0
  241. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  242. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  243. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  244. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  245. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  246. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  247. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  248. package/build/dist/Server/Services/UserCallService.js +54 -22
  249. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  250. package/build/dist/Server/Services/UserEmailService.js +78 -17
  251. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  252. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  253. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  254. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  255. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  256. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  257. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  258. package/build/dist/Server/Services/UserPushService.js +77 -0
  259. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  260. package/build/dist/Server/Services/UserSmsService.js +50 -17
  261. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  262. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  263. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  264. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  265. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  266. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  267. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  268. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  269. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  270. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
  271. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  272. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  273. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  274. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  275. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  276. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  277. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  278. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  279. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  280. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  281. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  282. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  283. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  284. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  285. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  286. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  287. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  288. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  289. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  290. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  291. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  292. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  293. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  294. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  295. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  296. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  297. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  298. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  299. package/build/dist/Types/Permission.js +33 -0
  300. package/build/dist/Types/Permission.js.map +1 -1
  301. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  302. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  303. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  304. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  305. package/build/dist/Types/Text.js +82 -17
  306. package/build/dist/Types/Text.js.map +1 -1
  307. package/build/dist/UI/Components/Button/Button.js +22 -14
  308. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  309. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  310. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  311. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  312. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  313. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  314. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  315. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  316. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  317. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  318. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  319. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  320. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  321. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  322. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  323. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  324. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  325. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  326. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  327. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  328. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  329. package/build/dist/UI/Components/List/List.js +19 -5
  330. package/build/dist/UI/Components/List/List.js.map +1 -1
  331. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  332. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  333. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  334. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  335. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  338. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  340. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  343. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  344. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  345. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  346. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  347. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  348. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  349. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  350. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  351. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  352. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  353. package/build/dist/UI/Components/Table/Table.js +35 -24
  354. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  355. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  356. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  357. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  358. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  359. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  360. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  361. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  362. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  363. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  364. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  365. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  366. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  367. package/build/dist/UI/Utils/Project.js +24 -1
  368. package/build/dist/UI/Utils/Project.js.map +1 -1
  369. package/build/dist/Utils/API.js +6 -1
  370. package/build/dist/Utils/API.js.map +1 -1
  371. package/jest.config.json +1 -1
  372. package/package.json +2 -3
  373. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  374. package/Tests/__mocks__/otpauth.js +0 -30
  375. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  376. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -0,0 +1,234 @@
1
+ import {
2
+ filterPaletteCommands,
3
+ getHighlightSegments,
4
+ getPaletteSectionId,
5
+ isSubsequenceMatch,
6
+ normalizePaletteQuery,
7
+ PaletteCommandMatch,
8
+ PaletteHighlightSegment,
9
+ PaletteMatchRank,
10
+ rankPaletteCommand,
11
+ } from "../../../../UI/Components/CommandPalette/PaletteFilter";
12
+ import { PaletteCommand } from "../../../../UI/Components/CommandPalette/Types";
13
+ import { describe, expect, test } from "@jest/globals";
14
+
15
+ type MakeCommandFunction = (
16
+ id: string,
17
+ title: string,
18
+ category: string,
19
+ keywords?: Array<string>,
20
+ ) => PaletteCommand;
21
+
22
+ const makeCommand: MakeCommandFunction = (
23
+ id: string,
24
+ title: string,
25
+ category: string,
26
+ keywords?: Array<string>,
27
+ ): PaletteCommand => {
28
+ return {
29
+ id,
30
+ title,
31
+ category,
32
+ keywords,
33
+ onSelect: (): void => {},
34
+ };
35
+ };
36
+
37
+ describe("normalizePaletteQuery", () => {
38
+ test("trims whitespace and lowercases", () => {
39
+ expect(normalizePaletteQuery(" MoNiToRs ")).toBe("monitors");
40
+ expect(normalizePaletteQuery(" ")).toBe("");
41
+ });
42
+ });
43
+
44
+ describe("isSubsequenceMatch", () => {
45
+ test("matches characters in order with gaps", () => {
46
+ expect(isSubsequenceMatch("mtr", "monitors")).toBe(true);
47
+ });
48
+
49
+ test("rejects characters out of order", () => {
50
+ expect(isSubsequenceMatch("rtm", "monitors")).toBe(false);
51
+ });
52
+
53
+ test("rejects characters that are missing entirely", () => {
54
+ expect(isSubsequenceMatch("xyz", "monitors")).toBe(false);
55
+ });
56
+
57
+ test("an empty needle matches anything", () => {
58
+ expect(isSubsequenceMatch("", "monitors")).toBe(true);
59
+ });
60
+ });
61
+
62
+ describe("rankPaletteCommand", () => {
63
+ test("title prefix wins over everything", () => {
64
+ const command: PaletteCommand = makeCommand("a", "Monitors", "Monitoring", [
65
+ "monitor",
66
+ ]);
67
+ expect(rankPaletteCommand(command, "mon")).toBe(
68
+ PaletteMatchRank.TitlePrefix,
69
+ );
70
+ });
71
+
72
+ test("title substring ranks below prefix", () => {
73
+ const command: PaletteCommand = makeCommand("a", "Create Monitor", "Other");
74
+ expect(rankPaletteCommand(command, "mon")).toBe(
75
+ PaletteMatchRank.TitleSubstring,
76
+ );
77
+ });
78
+
79
+ test("keyword match ranks below title matches", () => {
80
+ const command: PaletteCommand = makeCommand("a", "Uptime", "Other", [
81
+ "monitor",
82
+ "checks",
83
+ ]);
84
+ expect(rankPaletteCommand(command, "mon")).toBe(PaletteMatchRank.Keyword);
85
+ });
86
+
87
+ test("category match ranks below keyword", () => {
88
+ const command: PaletteCommand = makeCommand("a", "Stats", "Monitoring");
89
+ expect(rankPaletteCommand(command, "mon")).toBe(PaletteMatchRank.Category);
90
+ });
91
+
92
+ test("title subsequence is the last resort", () => {
93
+ // "mtr" is not a substring of anything here, but m→t→r appear in order.
94
+ const command: PaletteCommand = makeCommand("a", "Metrics", "Other");
95
+ expect(rankPaletteCommand(command, "mtr")).toBe(
96
+ PaletteMatchRank.TitleSubsequence,
97
+ );
98
+ });
99
+
100
+ test("no match at all returns null", () => {
101
+ const command: PaletteCommand = makeCommand("a", "Logs", "Telemetry");
102
+ expect(rankPaletteCommand(command, "zzz")).toBe(null);
103
+ });
104
+
105
+ test("matching is case-insensitive", () => {
106
+ const command: PaletteCommand = makeCommand("a", "MONITORS", "Other");
107
+ expect(rankPaletteCommand(command, "mon")).toBe(
108
+ PaletteMatchRank.TitlePrefix,
109
+ );
110
+ });
111
+ });
112
+
113
+ describe("filterPaletteCommands", () => {
114
+ test("orders results prefix > substring > keyword > category > subsequence", () => {
115
+ const commands: Array<PaletteCommand> = [
116
+ // Deliberately listed in reverse rank order to prove sorting happens.
117
+ makeCommand("subsequence", "Man on wire", "Zeta"),
118
+ makeCommand("category", "Stats", "Monitoring"),
119
+ makeCommand("keyword", "Uptime", "Other", ["monitor"]),
120
+ makeCommand("substring", "Create Monitor", "Other"),
121
+ makeCommand("prefix", "Monitors", "Other"),
122
+ makeCommand("excluded", "Logs", "Telemetry"),
123
+ ];
124
+
125
+ const matches: Array<PaletteCommandMatch> = filterPaletteCommands(
126
+ commands,
127
+ "mon",
128
+ );
129
+
130
+ expect(
131
+ matches.map((match: PaletteCommandMatch) => {
132
+ return match.command.id;
133
+ }),
134
+ ).toEqual(["prefix", "substring", "keyword", "category", "subsequence"]);
135
+ });
136
+
137
+ test("keeps catalog order within the same rank tier", () => {
138
+ const commands: Array<PaletteCommand> = [
139
+ makeCommand("first", "Monitors", "A"),
140
+ makeCommand("second", "Monitor Groups", "B"),
141
+ makeCommand("third", "Monitoring Secrets", "C"),
142
+ ];
143
+
144
+ const matches: Array<PaletteCommandMatch> = filterPaletteCommands(
145
+ commands,
146
+ "mon",
147
+ );
148
+
149
+ expect(
150
+ matches.map((match: PaletteCommandMatch) => {
151
+ return match.command.id;
152
+ }),
153
+ ).toEqual(["first", "second", "third"]);
154
+ });
155
+
156
+ test("an empty or whitespace query matches everything in original order", () => {
157
+ const commands: Array<PaletteCommand> = [
158
+ makeCommand("one", "Alpha", "A"),
159
+ makeCommand("two", "Beta", "B"),
160
+ ];
161
+
162
+ expect(
163
+ filterPaletteCommands(commands, " ").map(
164
+ (match: PaletteCommandMatch) => {
165
+ return match.command.id;
166
+ },
167
+ ),
168
+ ).toEqual(["one", "two"]);
169
+ });
170
+
171
+ test("excludes commands that match nowhere", () => {
172
+ const commands: Array<PaletteCommand> = [
173
+ makeCommand("only", "Monitors", "Other"),
174
+ makeCommand("gone", "Logs", "Telemetry"),
175
+ ];
176
+
177
+ const matches: Array<PaletteCommandMatch> = filterPaletteCommands(
178
+ commands,
179
+ "monitors",
180
+ );
181
+
182
+ expect(matches).toHaveLength(1);
183
+ expect(matches[0]!.command.id).toBe("only");
184
+ });
185
+ });
186
+
187
+ describe("getHighlightSegments", () => {
188
+ test("marks a single case-insensitive occurrence, preserving original casing", () => {
189
+ const segments: Array<PaletteHighlightSegment> = getHighlightSegments(
190
+ "Monitors",
191
+ "mon",
192
+ );
193
+
194
+ expect(segments).toEqual([
195
+ { text: "Mon", isMatch: true },
196
+ { text: "itors", isMatch: false },
197
+ ]);
198
+ });
199
+
200
+ test("marks every occurrence", () => {
201
+ const segments: Array<PaletteHighlightSegment> = getHighlightSegments(
202
+ "on and on",
203
+ "on",
204
+ );
205
+
206
+ expect(segments).toEqual([
207
+ { text: "on", isMatch: true },
208
+ { text: " and ", isMatch: false },
209
+ { text: "on", isMatch: true },
210
+ ]);
211
+ });
212
+
213
+ test("no occurrence yields one unmarked segment", () => {
214
+ expect(getHighlightSegments("Monitors", "zzz")).toEqual([
215
+ { text: "Monitors", isMatch: false },
216
+ ]);
217
+ });
218
+
219
+ test("an empty query yields one unmarked segment", () => {
220
+ expect(getHighlightSegments("Monitors", " ")).toEqual([
221
+ { text: "Monitors", isMatch: false },
222
+ ]);
223
+ });
224
+ });
225
+
226
+ describe("getPaletteSectionId", () => {
227
+ test("slugs human titles for test ids", () => {
228
+ expect(getPaletteSectionId("Analytics & Automation")).toBe(
229
+ "analytics-automation",
230
+ );
231
+ expect(getPaletteSectionId("Essentials")).toBe("essentials");
232
+ expect(getPaletteSectionId(" Odd Spacing ")).toBe("odd-spacing");
233
+ });
234
+ });
@@ -0,0 +1,97 @@
1
+ import {
2
+ readRecentCommandIds,
3
+ RECENT_COMMANDS_LIMIT,
4
+ writeRecentCommandId,
5
+ } from "../../../../UI/Components/CommandPalette/RecentCommands";
6
+ import { afterEach, describe, expect, test } from "@jest/globals";
7
+
8
+ const STORAGE_KEY: string = "test-command-palette-recents";
9
+
10
+ describe("RecentCommands storage", () => {
11
+ afterEach(() => {
12
+ window.localStorage.clear();
13
+ jest.restoreAllMocks();
14
+ });
15
+
16
+ test("reads back what was written, most recent first", () => {
17
+ writeRecentCommandId(STORAGE_KEY, "first");
18
+ writeRecentCommandId(STORAGE_KEY, "second");
19
+
20
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual(["second", "first"]);
21
+ });
22
+
23
+ test("re-selecting a command moves it to the front instead of duplicating", () => {
24
+ writeRecentCommandId(STORAGE_KEY, "a");
25
+ writeRecentCommandId(STORAGE_KEY, "b");
26
+ writeRecentCommandId(STORAGE_KEY, "a");
27
+
28
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual(["a", "b"]);
29
+ });
30
+
31
+ test(`keeps at most ${RECENT_COMMANDS_LIMIT} ids`, () => {
32
+ for (let index: number = 0; index < RECENT_COMMANDS_LIMIT + 3; index++) {
33
+ writeRecentCommandId(STORAGE_KEY, `command-${index}`);
34
+ }
35
+
36
+ const stored: Array<string> = readRecentCommandIds(STORAGE_KEY);
37
+
38
+ expect(stored).toHaveLength(RECENT_COMMANDS_LIMIT);
39
+ // Newest first; the earliest writes fell off the end.
40
+ expect(stored[0]).toBe(`command-${RECENT_COMMANDS_LIMIT + 2}`);
41
+ });
42
+
43
+ test("caps reads even when storage holds more ids than the limit", () => {
44
+ const oversized: Array<string> = [];
45
+ for (let index: number = 0; index < 20; index++) {
46
+ oversized.push(`command-${index}`);
47
+ }
48
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(oversized));
49
+
50
+ expect(readRecentCommandIds(STORAGE_KEY)).toHaveLength(
51
+ RECENT_COMMANDS_LIMIT,
52
+ );
53
+ });
54
+
55
+ test("tolerates corrupt JSON", () => {
56
+ window.localStorage.setItem(STORAGE_KEY, "{not json[");
57
+
58
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual([]);
59
+ });
60
+
61
+ test("tolerates JSON that is not an array", () => {
62
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify({ nope: true }));
63
+
64
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual([]);
65
+ });
66
+
67
+ test("filters non-string entries out of a stored array", () => {
68
+ window.localStorage.setItem(
69
+ STORAGE_KEY,
70
+ JSON.stringify(["ok", 42, null, { id: "x" }, "also-ok"]),
71
+ );
72
+
73
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual(["ok", "also-ok"]);
74
+ });
75
+
76
+ test("an empty key reads as no recents", () => {
77
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual([]);
78
+ });
79
+
80
+ test("a throwing setItem (quota / private mode) is swallowed", () => {
81
+ jest.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
82
+ throw new Error("QuotaExceededError");
83
+ });
84
+
85
+ expect(() => {
86
+ writeRecentCommandId(STORAGE_KEY, "anything");
87
+ }).not.toThrow();
88
+ });
89
+
90
+ test("a throwing getItem is swallowed and reads as no recents", () => {
91
+ jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
92
+ throw new Error("SecurityError");
93
+ });
94
+
95
+ expect(readRecentCommandIds(STORAGE_KEY)).toEqual([]);
96
+ });
97
+ });
@@ -0,0 +1,198 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+ import "@testing-library/jest-dom";
10
+ import { act, cleanup, render, screen, waitFor } from "@testing-library/react";
11
+ import React from "react";
12
+ import getJestMockFunction, { MockFunction } from "../../MockType";
13
+
14
+ /*
15
+ * Side menus build `countQuery` (and `requestOptions`) as fresh object
16
+ * literals on every render, and section layouts re-render on every in-section
17
+ * navigation. Without a value-level guard every one of those re-renders
18
+ * re-issued the badge's POST /count - across the 43 badge usages that was 1-2
19
+ * redundant requests per navigation. These tests pin the guard's contract:
20
+ * value-equal props never refetch, a real value change does, and the global
21
+ * refresh event still forces a refetch regardless of the guard.
22
+ */
23
+
24
+ /*
25
+ * Declared before jest.mock but dereferenced inside the factory: ts-jest
26
+ * hoists the jest.mock call above these initializers, so naming the mock
27
+ * directly in the factory would capture undefined.
28
+ */
29
+ const countMock: MockFunction = getJestMockFunction();
30
+
31
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
32
+ return {
33
+ __esModule: true,
34
+ default: {
35
+ count: (...args: Array<any>) => {
36
+ return countMock(...args);
37
+ },
38
+ },
39
+ };
40
+ });
41
+
42
+ /*
43
+ * SideMenuItem reads the active route off Navigation and UILink navigates
44
+ * through it; neither is what these tests are about.
45
+ */
46
+ jest.mock("../../../UI/Utils/Navigation", () => {
47
+ return {
48
+ __esModule: true,
49
+ default: {
50
+ isOnThisPage: () => {
51
+ return false;
52
+ },
53
+ navigate: () => {
54
+ return undefined;
55
+ },
56
+ },
57
+ };
58
+ });
59
+
60
+ jest.mock("../../../UI/Utils/Translation", () => {
61
+ return {
62
+ __esModule: true,
63
+ default: () => {
64
+ return {
65
+ translateString: (value: string | undefined) => {
66
+ return value;
67
+ },
68
+ translateValue: (value: unknown) => {
69
+ return value;
70
+ },
71
+ };
72
+ },
73
+ };
74
+ });
75
+
76
+ import CountModelSideMenuItem, {
77
+ REFRESH_SIDEBAR_COUNT_EVENT,
78
+ } from "../../../UI/Components/SideMenu/CountModelSideMenuItem";
79
+ import { RequestOptions } from "../../../UI/Utils/ModelAPI/ModelAPI";
80
+ import GlobalEvents from "../../../UI/Utils/GlobalEvents";
81
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
82
+ import Query from "../../../Types/BaseDatabase/Query";
83
+ import Route from "../../../Types/API/Route";
84
+ import Link from "../../../Types/Link";
85
+
86
+ const link: Link = {
87
+ title: "Monitors",
88
+ to: new Route("/dashboard/monitors"),
89
+ };
90
+
91
+ type MakeQueryFunction = (name: string) => Query<Monitor>;
92
+
93
+ /**
94
+ * A FRESH object every call - the point of these tests is that value-equal
95
+ * but identity-distinct props must not refetch, exactly what side menus
96
+ * produce on every parent render.
97
+ */
98
+ const makeQuery: MakeQueryFunction = (name: string): Query<Monitor> => {
99
+ return { name: name } as Query<Monitor>;
100
+ };
101
+
102
+ type MakeRequestOptionsFunction = () => RequestOptions;
103
+
104
+ const makeRequestOptions: MakeRequestOptionsFunction = (): RequestOptions => {
105
+ return {
106
+ requestHeaders: {
107
+ "x-badge": "1",
108
+ },
109
+ };
110
+ };
111
+
112
+ type RenderItemFunction = (
113
+ query: Query<Monitor>,
114
+ requestOptions?: RequestOptions,
115
+ ) => React.ReactElement;
116
+
117
+ const renderItem: RenderItemFunction = (
118
+ query: Query<Monitor>,
119
+ requestOptions?: RequestOptions,
120
+ ): React.ReactElement => {
121
+ return (
122
+ <CountModelSideMenuItem<Monitor>
123
+ link={link}
124
+ modelType={Monitor}
125
+ countQuery={query}
126
+ requestOptions={requestOptions}
127
+ />
128
+ );
129
+ };
130
+
131
+ describe("CountModelSideMenuItem fetch guard", () => {
132
+ beforeEach(() => {
133
+ countMock.mockReset();
134
+ countMock.mockResolvedValue(5 as never);
135
+ });
136
+
137
+ afterEach(() => {
138
+ cleanup();
139
+ });
140
+
141
+ test("re-rendering with a value-equal but identity-fresh query fetches exactly once", async () => {
142
+ const { rerender } = render(
143
+ renderItem(makeQuery("active"), makeRequestOptions()),
144
+ );
145
+
146
+ // wait for the count to land so the initial fetch is fully settled.
147
+ await waitFor(() => {
148
+ expect(screen.getByText("5")).toBeInTheDocument();
149
+ });
150
+ expect(countMock).toHaveBeenCalledTimes(1);
151
+
152
+ /*
153
+ * Simulate what section layouts do on every in-section navigation: a
154
+ * re-render where countQuery and requestOptions are brand-new objects
155
+ * with identical contents.
156
+ */
157
+ rerender(renderItem(makeQuery("active"), makeRequestOptions()));
158
+ rerender(renderItem(makeQuery("active"), makeRequestOptions()));
159
+
160
+ // effects flushed synchronously by RTL's act - still exactly one fetch.
161
+ expect(countMock).toHaveBeenCalledTimes(1);
162
+ });
163
+
164
+ test("a value-level change to the query refetches with the new query", async () => {
165
+ const { rerender } = render(renderItem(makeQuery("active")));
166
+
167
+ await waitFor(() => {
168
+ expect(countMock).toHaveBeenCalledTimes(1);
169
+ });
170
+
171
+ rerender(renderItem(makeQuery("resolved")));
172
+
173
+ await waitFor(() => {
174
+ expect(countMock).toHaveBeenCalledTimes(2);
175
+ });
176
+
177
+ // the refetch must carry the NEW query, not a stale closure.
178
+ const secondCallArgs: any = countMock.mock.calls[1]![0];
179
+ expect(secondCallArgs.query).toEqual({ name: "resolved" });
180
+ expect(secondCallArgs.modelType).toBe(Monitor);
181
+ });
182
+
183
+ test("the global refresh event still forces a refetch even when the query is unchanged", async () => {
184
+ render(renderItem(makeQuery("active")));
185
+
186
+ await waitFor(() => {
187
+ expect(countMock).toHaveBeenCalledTimes(1);
188
+ });
189
+
190
+ await act(async () => {
191
+ GlobalEvents.dispatchEvent(REFRESH_SIDEBAR_COUNT_EVENT);
192
+ });
193
+
194
+ await waitFor(() => {
195
+ expect(countMock).toHaveBeenCalledTimes(2);
196
+ });
197
+ });
198
+ });
@@ -6,7 +6,7 @@ import getJestMockFunction, { MockFunction } from "../../MockType";
6
6
 
7
7
  const markdownViewerMock: MockFunction = getJestMockFunction();
8
8
 
9
- jest.mock("../../../UI/Components/Markdown.tsx/MarkdownViewer", () => {
9
+ jest.mock("../../../UI/Components/Markdown.tsx/LazyMarkdownViewer", () => {
10
10
  return {
11
11
  __esModule: true,
12
12
  default: (props: MarkdownViewerProps): React.ReactElement => {