@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.
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
- package/Models/DatabaseModels/UserCall.ts +94 -3
- package/Models/DatabaseModels/UserEmail.ts +94 -3
- package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
- package/Models/DatabaseModels/UserPush.ts +37 -0
- package/Models/DatabaseModels/UserSMS.ts +94 -3
- package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
- package/Server/API/BaseAPI.ts +79 -14
- package/Server/API/UserCallAPI.ts +53 -45
- package/Server/API/UserEmailAPI.ts +53 -44
- package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
- package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
- package/Server/API/UserPushAPI.ts +126 -2
- package/Server/API/UserSmsAPI.ts +53 -44
- package/Server/API/UserWhatsAppAPI.ts +53 -50
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
- package/Server/Services/AnalyticsDatabaseService.ts +169 -0
- package/Server/Services/DatabaseService.ts +101 -0
- package/Server/Services/LogAggregationService.ts +5 -0
- package/Server/Services/PushNotificationService.ts +127 -12
- package/Server/Services/TeamMemberService.ts +53 -13
- package/Server/Services/UserCallService.ts +55 -24
- package/Server/Services/UserEmailService.ts +83 -17
- package/Server/Services/UserIncomingCallNumberService.ts +49 -85
- package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
- package/Server/Services/UserNotificationRuleService.ts +26 -0
- package/Server/Services/UserPushService.ts +89 -0
- package/Server/Services/UserSmsService.ts +50 -17
- package/Server/Services/UserWhatsAppService.ts +48 -15
- package/Server/Types/Database/QueryHelper.ts +30 -0
- package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
- package/Server/Utils/ChannelVerification.ts +510 -0
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
- package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
- package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
- package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
- package/Server/Utils/TotpAuth.ts +137 -9
- package/Server/Utils/UserRegistrationToken.ts +182 -0
- package/Server/Utils/VerificationCode.ts +134 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
- package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
- package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
- package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
- package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
- package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
- package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
- package/Tests/Server/API/UserSmsApi.test.ts +107 -14
- package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
- package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
- package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
- package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
- package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
- package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
- package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
- package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
- package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
- package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
- package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
- package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
- package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
- package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
- package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
- package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
- package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
- package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
- package/Tests/Server/Utils/Browser.test.ts +162 -0
- package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
- package/Tests/Server/Utils/Express.test.ts +371 -0
- package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
- package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
- package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
- package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
- package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
- package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
- package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
- package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
- package/Tests/Types/Call/CallRequest.test.ts +189 -0
- package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
- package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
- package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
- package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
- package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
- package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
- package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
- package/Tests/Types/TextRandomGeneration.test.ts +211 -0
- package/Tests/UI/Components/Button.test.tsx +175 -1
- package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
- package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
- package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
- package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
- package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
- package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
- package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
- package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
- package/Tests/UI/Components/List.test.tsx +9 -2
- package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
- package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
- package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
- package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
- package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
- package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
- package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
- package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
- package/Tests/UI/Components/Skeleton.test.tsx +71 -0
- package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
- package/Tests/UI/Components/Tabs.test.tsx +24 -0
- package/Tests/UI/Components/Toast.test.tsx +130 -7
- package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
- package/Tests/UI/Utils/Dropdown.test.ts +423 -0
- package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
- package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
- package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
- package/Tests/Utils/API.test.ts +16 -2
- package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
- package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
- package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
- package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
- package/Types/Permission.ts +35 -0
- package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
- package/Types/PushNotification/PushNotificationMessage.ts +15 -0
- package/Types/PushNotification/PushNotificationRequest.ts +8 -16
- package/Types/Service/ServiceLanguage.ts +144 -0
- package/Types/Text.ts +111 -19
- package/UI/Components/Button/Button.tsx +27 -14
- package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
- package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
- package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
- package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
- package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
- package/UI/Components/CommandPalette/Types.ts +49 -0
- package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
- package/UI/Components/Dropdown/Dropdown.tsx +6 -1
- package/UI/Components/Feed/FeedItem.tsx +1 -1
- package/UI/Components/List/List.tsx +37 -5
- package/UI/Components/List/ListSkeleton.tsx +54 -0
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
- package/UI/Components/Modal/Modal.tsx +2 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
- package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
- package/UI/Components/Navbar/NavBar.tsx +14 -1
- package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
- package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
- package/UI/Components/SideOver/SideOver.tsx +37 -2
- package/UI/Components/Skeleton/Skeleton.tsx +53 -0
- package/UI/Components/Table/Table.tsx +74 -44
- package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
- package/UI/Components/Tabs/Tab.tsx +1 -1
- package/UI/Components/Toast/Toast.tsx +118 -53
- package/UI/Components/Toast/ToastInit.tsx +82 -32
- package/UI/Styles/Theme.css +40 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
- package/UI/Utils/ModelListCache.ts +188 -0
- package/UI/Utils/Project.ts +29 -0
- package/Utils/API.ts +6 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +69 -13
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +42 -38
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +42 -38
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
- package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
- package/build/dist/Server/API/UserPushAPI.js +95 -2
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +42 -38
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
- package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +74 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +5 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +69 -30
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +43 -7
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +54 -22
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +78 -17
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
- package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +77 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +50 -17
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
- package/build/dist/Server/Utils/ChannelVerification.js +348 -0
- package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/TotpAuth.js +127 -8
- package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
- package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
- package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
- package/build/dist/Server/Utils/VerificationCode.js +120 -0
- package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +33 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
- package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
- package/build/dist/Types/Service/ServiceLanguage.js +96 -0
- package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
- package/build/dist/Types/Text.js +82 -17
- package/build/dist/Types/Text.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +22 -14
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
- package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
- package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
- package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
- package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
- package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
- package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
- package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
- package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
- package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/List/List.js +19 -5
- package/build/dist/UI/Components/List/List.js.map +1 -1
- package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
- package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
- package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +35 -24
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
- package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
- package/build/dist/UI/Components/Tabs/Tab.js +1 -1
- package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
- package/build/dist/UI/Components/Toast/Toast.js +77 -23
- package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
- package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelListCache.js +126 -0
- package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
- package/build/dist/UI/Utils/Project.js +24 -1
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/Utils/API.js +6 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/jest.config.json +1 -1
- package/package.json +2 -3
- package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
- package/Tests/__mocks__/otpauth.js +0 -30
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SERVICE_LANGUAGE_DISPLAY_NAMES,
|
|
3
|
+
ServiceLanguage,
|
|
4
|
+
detectServiceLanguage,
|
|
5
|
+
} from "../../../Types/Service/ServiceLanguage";
|
|
6
|
+
import TechStack from "../../../Types/Service/TechStack";
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Which runtime a service is written in, decided from three sources of
|
|
10
|
+
* evidence of decreasing trustworthiness.
|
|
11
|
+
*
|
|
12
|
+
* This used to only pick which charts to draw on the service overview, where
|
|
13
|
+
* a wrong answer is a chart with no data in it. It now also picks which
|
|
14
|
+
* monitors OneUptime recommends, where a wrong answer is a monitor that
|
|
15
|
+
* queries a metric the service never emits and therefore never fires — a
|
|
16
|
+
* silent gap that reads as coverage. Every case below is about the difference
|
|
17
|
+
* between "we know" and "we are guessing".
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const ALL_LANGUAGES: Array<ServiceLanguage> = Object.keys(
|
|
21
|
+
SERVICE_LANGUAGE_DISPLAY_NAMES,
|
|
22
|
+
) as Array<ServiceLanguage>;
|
|
23
|
+
|
|
24
|
+
describe("detectServiceLanguage", () => {
|
|
25
|
+
describe("precedence between the three sources", () => {
|
|
26
|
+
/*
|
|
27
|
+
* The order is not arbitrary. `telemetry.sdk.language` is stamped by the
|
|
28
|
+
* SDK inside the process, so it describes what the process actually runs
|
|
29
|
+
* on. `process.runtime.name` is the same information one hop later.
|
|
30
|
+
* The tech stack is a human's answer typed into a form, which can be
|
|
31
|
+
* months out of date. Each test below sets up a deliberate disagreement
|
|
32
|
+
* so the winner is unambiguous.
|
|
33
|
+
*/
|
|
34
|
+
it("prefers the SDK language over the runtime name", () => {
|
|
35
|
+
expect(
|
|
36
|
+
detectServiceLanguage({
|
|
37
|
+
telemetrySdkLanguage: "go",
|
|
38
|
+
runtimeName: "OpenJDK Runtime Environment",
|
|
39
|
+
}),
|
|
40
|
+
).toBe("go");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("prefers the SDK language over the tech stack", () => {
|
|
44
|
+
expect(
|
|
45
|
+
detectServiceLanguage({
|
|
46
|
+
telemetrySdkLanguage: "python",
|
|
47
|
+
techStack: [TechStack.Java],
|
|
48
|
+
}),
|
|
49
|
+
).toBe("python");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("prefers the runtime name over the tech stack", () => {
|
|
53
|
+
expect(
|
|
54
|
+
detectServiceLanguage({
|
|
55
|
+
runtimeName: "node",
|
|
56
|
+
techStack: [TechStack.Java],
|
|
57
|
+
}),
|
|
58
|
+
).toBe("nodejs");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("falls through to the tech stack when telemetry says nothing", () => {
|
|
62
|
+
expect(
|
|
63
|
+
detectServiceLanguage({
|
|
64
|
+
techStack: [TechStack.Rust],
|
|
65
|
+
}),
|
|
66
|
+
).toBe("rust");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("the SDK language attribute", () => {
|
|
71
|
+
it.each(ALL_LANGUAGES)("recognises %s", (language: ServiceLanguage) => {
|
|
72
|
+
expect(detectServiceLanguage({ telemetrySdkLanguage: language })).toBe(
|
|
73
|
+
language,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("is matched case-insensitively and ignores surrounding whitespace", () => {
|
|
78
|
+
expect(
|
|
79
|
+
detectServiceLanguage({ telemetrySdkLanguage: " DotNet " }),
|
|
80
|
+
).toBe("dotnet");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* The important negative case. An unrecognised value must NOT be returned
|
|
85
|
+
* verbatim — it would be a string that looks like a `ServiceLanguage` to
|
|
86
|
+
* every consumer and matches no template set, so the recommendations page
|
|
87
|
+
* would show nothing at all rather than the language-agnostic set.
|
|
88
|
+
*/
|
|
89
|
+
it("does not pass an unrecognised SDK language through", () => {
|
|
90
|
+
expect(
|
|
91
|
+
detectServiceLanguage({ telemetrySdkLanguage: "cobol" }),
|
|
92
|
+
).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("falls through to the other sources when the SDK language is unknown", () => {
|
|
96
|
+
expect(
|
|
97
|
+
detectServiceLanguage({
|
|
98
|
+
telemetrySdkLanguage: "cobol",
|
|
99
|
+
techStack: [TechStack.Go],
|
|
100
|
+
}),
|
|
101
|
+
).toBe("go");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("the runtime name markers", () => {
|
|
106
|
+
it.each([
|
|
107
|
+
["OpenJDK 64-Bit Server VM", "java"],
|
|
108
|
+
["GraalVM CE", "java"],
|
|
109
|
+
["Java HotSpot(TM) 64-Bit Server VM", "java"],
|
|
110
|
+
[".NET 9.0.0", "dotnet"],
|
|
111
|
+
["dotnet", "dotnet"],
|
|
112
|
+
["node", "nodejs"],
|
|
113
|
+
["Deno", "nodejs"],
|
|
114
|
+
["bun", "nodejs"],
|
|
115
|
+
["CPython", "python"],
|
|
116
|
+
["PyPy", "python"],
|
|
117
|
+
["BEAM", "erlang"],
|
|
118
|
+
["Erlang/OTP 26", "erlang"],
|
|
119
|
+
["ruby", "ruby"],
|
|
120
|
+
["PHP 8.3", "php"],
|
|
121
|
+
["rustc", "rust"],
|
|
122
|
+
["swift", "swift"],
|
|
123
|
+
["go", "go"],
|
|
124
|
+
])(
|
|
125
|
+
"maps runtime name %s to %s",
|
|
126
|
+
(runtimeName: string, expected: string) => {
|
|
127
|
+
expect(detectServiceLanguage({ runtimeName: runtimeName })).toBe(
|
|
128
|
+
expected,
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
* `go` is a two-letter substring, so it appears inside plenty of words —
|
|
135
|
+
* "GraalVM" does not contain it, but the ordering still matters and this
|
|
136
|
+
* pins it: the JVM markers are checked first, so a runtime string that
|
|
137
|
+
* contains both loses to the more specific one.
|
|
138
|
+
*/
|
|
139
|
+
it("checks specific markers before the two-letter go marker", () => {
|
|
140
|
+
expect(
|
|
141
|
+
detectServiceLanguage({ runtimeName: "GraalVM for Go interop" }),
|
|
142
|
+
).toBe("java");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("returns null for a runtime name it does not recognise", () => {
|
|
146
|
+
expect(detectServiceLanguage({ runtimeName: "SomeVM 1.0" })).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("the tech stack fallback", () => {
|
|
151
|
+
it("maps Kotlin to java, because the runtime is what matters", () => {
|
|
152
|
+
expect(detectServiceLanguage({ techStack: [TechStack.Kotlin] })).toBe(
|
|
153
|
+
"java",
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("maps C# to dotnet", () => {
|
|
158
|
+
expect(detectServiceLanguage({ techStack: [TechStack.CSharp] })).toBe(
|
|
159
|
+
"dotnet",
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it.each([TechStack.TypeScript, TechStack.JavaScript, TechStack.NodeJS])(
|
|
164
|
+
"maps %s to nodejs",
|
|
165
|
+
(techStack: TechStack) => {
|
|
166
|
+
expect(detectServiceLanguage({ techStack: [techStack] })).toBe(
|
|
167
|
+
"nodejs",
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
it("maps React to webjs rather than to nodejs", () => {
|
|
173
|
+
expect(detectServiceLanguage({ techStack: [TechStack.React] })).toBe(
|
|
174
|
+
"webjs",
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("takes the first entry that maps to a runtime", () => {
|
|
179
|
+
expect(
|
|
180
|
+
detectServiceLanguage({
|
|
181
|
+
techStack: [TechStack.Shell, TechStack.Markdown, TechStack.Go],
|
|
182
|
+
}),
|
|
183
|
+
).toBe("go");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it.each([TechStack.Shell, TechStack.Markdown, TechStack.Other])(
|
|
187
|
+
"does not invent a runtime for %s",
|
|
188
|
+
(techStack: TechStack) => {
|
|
189
|
+
expect(detectServiceLanguage({ techStack: [techStack] })).toBeNull();
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("when nothing identifies the runtime", () => {
|
|
195
|
+
/*
|
|
196
|
+
* Null, never a default. A default would hand every unidentified service
|
|
197
|
+
* one language's recommendations — monitors on metrics it does not emit,
|
|
198
|
+
* presented as if OneUptime had established what it runs on.
|
|
199
|
+
*/
|
|
200
|
+
it("returns null for no input at all", () => {
|
|
201
|
+
expect(detectServiceLanguage({})).toBeNull();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("returns null for empty strings and an empty tech stack", () => {
|
|
205
|
+
expect(
|
|
206
|
+
detectServiceLanguage({
|
|
207
|
+
telemetrySdkLanguage: "",
|
|
208
|
+
runtimeName: "",
|
|
209
|
+
techStack: [],
|
|
210
|
+
}),
|
|
211
|
+
).toBeNull();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("returns null for whitespace-only telemetry values", () => {
|
|
215
|
+
expect(
|
|
216
|
+
detectServiceLanguage({
|
|
217
|
+
telemetrySdkLanguage: " ",
|
|
218
|
+
runtimeName: " ",
|
|
219
|
+
}),
|
|
220
|
+
).toBeNull();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("returns null for explicitly undefined fields", () => {
|
|
224
|
+
expect(
|
|
225
|
+
detectServiceLanguage({
|
|
226
|
+
telemetrySdkLanguage: undefined,
|
|
227
|
+
runtimeName: undefined,
|
|
228
|
+
techStack: undefined,
|
|
229
|
+
}),
|
|
230
|
+
).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe("SERVICE_LANGUAGE_DISPLAY_NAMES", () => {
|
|
236
|
+
it("names every language, non-blank", () => {
|
|
237
|
+
expect(ALL_LANGUAGES.length).toBeGreaterThan(0);
|
|
238
|
+
|
|
239
|
+
for (const language of ALL_LANGUAGES) {
|
|
240
|
+
expect(
|
|
241
|
+
SERVICE_LANGUAGE_DISPLAY_NAMES[language].trim().length,
|
|
242
|
+
).toBeGreaterThan(0);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("gives each language a distinct display name", () => {
|
|
247
|
+
const names: Array<string> = ALL_LANGUAGES.map(
|
|
248
|
+
(language: ServiceLanguage) => {
|
|
249
|
+
return SERVICE_LANGUAGE_DISPLAY_NAMES[language];
|
|
250
|
+
},
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
expect(new Set<string>(names).size).toBe(names.length);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import Text from "../../Types/Text";
|
|
2
|
+
import { describe, expect, it } from "@jest/globals";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* GHSA-5cr8-vph4-3hrf, CWE-330.
|
|
6
|
+
*
|
|
7
|
+
* Text.generateRandomNumber is what mints notification-channel verification
|
|
8
|
+
* codes. It used to draw from the literal string "12134567890" using
|
|
9
|
+
* Math.random, which is wrong twice over: the alphabet has "1" in it twice so
|
|
10
|
+
* the digits are not equally likely, and Math.random is a non-cryptographic
|
|
11
|
+
* PRNG whose state is recoverable from its own output.
|
|
12
|
+
*
|
|
13
|
+
* Neither property can be observed by looking at one value, which is why they
|
|
14
|
+
* survived. The tests below observe them the only ways available: by counting
|
|
15
|
+
* a large sample for bias, and by watching which source the generator actually
|
|
16
|
+
* pulls from.
|
|
17
|
+
*/
|
|
18
|
+
describe("Text random generation", () => {
|
|
19
|
+
describe("generateRandomNumber", () => {
|
|
20
|
+
it("emits only digits, at the requested length", () => {
|
|
21
|
+
for (let length: number = 1; length <= 12; length++) {
|
|
22
|
+
const value: string = Text.generateRandomNumber(length);
|
|
23
|
+
|
|
24
|
+
expect(value).toHaveLength(length);
|
|
25
|
+
expect(value).toMatch(/^[0-9]+$/);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("defaults to ten characters", () => {
|
|
30
|
+
expect(Text.generateRandomNumber()).toHaveLength(10);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* The direct regression. With the old alphabet the digit "1" appeared
|
|
35
|
+
* twice in an eleven-character string, so it came up 2/11 of the time
|
|
36
|
+
* against 1/11 for every other digit — and "8" appeared once, like the
|
|
37
|
+
* rest, only because the typo happened to leave it in.
|
|
38
|
+
*
|
|
39
|
+
* 60,000 digits puts the expected count per digit at 6,000 with a
|
|
40
|
+
* standard deviation of about 73. The old generator produced ~10,909 ones
|
|
41
|
+
* and ~5,454 of everything else, so the bound below is enormously loose
|
|
42
|
+
* for a fair generator and enormously tight for the broken one.
|
|
43
|
+
*/
|
|
44
|
+
it("draws every digit with equal probability", () => {
|
|
45
|
+
const sampleSize: number = 10000;
|
|
46
|
+
const digitsPerSample: number = 6;
|
|
47
|
+
const counts: Record<string, number> = {};
|
|
48
|
+
|
|
49
|
+
for (let i: number = 0; i < sampleSize; i++) {
|
|
50
|
+
for (const digit of Text.generateRandomNumber(digitsPerSample)) {
|
|
51
|
+
counts[digit] = (counts[digit] || 0) + 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const totalDigits: number = sampleSize * digitsPerSample;
|
|
56
|
+
const expectedPerDigit: number = totalDigits / 10;
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Every digit must actually appear — "8" was reachable before, but a
|
|
60
|
+
* future typo in the alphabet would show up here.
|
|
61
|
+
*/
|
|
62
|
+
expect(Object.keys(counts).sort()).toEqual([
|
|
63
|
+
"0",
|
|
64
|
+
"1",
|
|
65
|
+
"2",
|
|
66
|
+
"3",
|
|
67
|
+
"4",
|
|
68
|
+
"5",
|
|
69
|
+
"6",
|
|
70
|
+
"7",
|
|
71
|
+
"8",
|
|
72
|
+
"9",
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
for (const digit of Object.keys(counts)) {
|
|
76
|
+
const count: number = counts[digit] as number;
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* Six sigma either way: a fair generator fails this about once in
|
|
80
|
+
* 500 million runs, the old one fails it every time.
|
|
81
|
+
*/
|
|
82
|
+
expect(count).toBeGreaterThan(expectedPerDigit * 0.9);
|
|
83
|
+
expect(count).toBeLessThan(expectedPerDigit * 1.1);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Leading zeros are part of the code space. Dropping them — by going
|
|
89
|
+
* through a number, say — would quietly remove a tenth of it.
|
|
90
|
+
*/
|
|
91
|
+
it("can produce codes with leading zeros", () => {
|
|
92
|
+
let sawLeadingZero: boolean = false;
|
|
93
|
+
|
|
94
|
+
for (let i: number = 0; i < 500; i++) {
|
|
95
|
+
if (Text.generateRandomNumber(6).startsWith("0")) {
|
|
96
|
+
sawLeadingZero = true;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
expect(sawLeadingZero).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* The other half of CWE-330. Math.random is seeded per process and its
|
|
106
|
+
* internal state can be recovered from a handful of outputs, after which
|
|
107
|
+
* every future value is known exactly — no guessing required. Asserting
|
|
108
|
+
* on the SOURCE is the only way to pin this: any specific output is a
|
|
109
|
+
* legal output of either generator.
|
|
110
|
+
*/
|
|
111
|
+
it("does not fall back to Math.random when a CSPRNG is available", () => {
|
|
112
|
+
const mathRandomSpy: jest.SpyInstance = jest.spyOn(Math, "random");
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
for (let i: number = 0; i < 100; i++) {
|
|
116
|
+
Text.generateRandomNumber(6);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
expect(mathRandomSpy).not.toHaveBeenCalled();
|
|
120
|
+
} finally {
|
|
121
|
+
mathRandomSpy.mockRestore();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("takes its bytes from crypto.getRandomValues", () => {
|
|
126
|
+
const getRandomValuesSpy: jest.SpyInstance = jest.spyOn(
|
|
127
|
+
globalThis.crypto,
|
|
128
|
+
"getRandomValues",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
Text.generateRandomNumber(6);
|
|
133
|
+
|
|
134
|
+
expect(getRandomValuesSpy).toHaveBeenCalled();
|
|
135
|
+
} finally {
|
|
136
|
+
getRandomValuesSpy.mockRestore();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("produces a different value practically every time", () => {
|
|
141
|
+
const seen: Set<string> = new Set<string>();
|
|
142
|
+
|
|
143
|
+
for (let i: number = 0; i < 1000; i++) {
|
|
144
|
+
seen.add(Text.generateRandomNumber(10));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* Ten digits, a thousand draws: collisions are vanishingly unlikely,
|
|
149
|
+
* and a stuck generator would collapse this to 1.
|
|
150
|
+
*/
|
|
151
|
+
expect(seen.size).toBe(1000);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("generateRandomText", () => {
|
|
156
|
+
it("emits only letters, at the requested length", () => {
|
|
157
|
+
for (let length: number = 1; length <= 40; length += 7) {
|
|
158
|
+
const value: string = Text.generateRandomText(length);
|
|
159
|
+
|
|
160
|
+
expect(value).toHaveLength(length);
|
|
161
|
+
expect(value).toMatch(/^[A-Za-z]+$/);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("defaults to ten characters", () => {
|
|
166
|
+
expect(Text.generateRandomText()).toHaveLength(10);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("does not fall back to Math.random when a CSPRNG is available", () => {
|
|
170
|
+
const mathRandomSpy: jest.SpyInstance = jest.spyOn(Math, "random");
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
for (let i: number = 0; i < 100; i++) {
|
|
174
|
+
Text.generateRandomText(20);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
expect(mathRandomSpy).not.toHaveBeenCalled();
|
|
178
|
+
} finally {
|
|
179
|
+
mathRandomSpy.mockRestore();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* Both alphabets have 52 characters, which does NOT divide 256 — so
|
|
185
|
+
* `byte % 52` would be biased towards the first 48 letters. The rejection
|
|
186
|
+
* sampling is what removes that, and this is the test that would notice
|
|
187
|
+
* if somebody "simplified" it away.
|
|
188
|
+
*/
|
|
189
|
+
it("draws every letter with roughly equal probability", () => {
|
|
190
|
+
const counts: Record<string, number> = {};
|
|
191
|
+
const totalCharacters: number = 52 * 2000;
|
|
192
|
+
|
|
193
|
+
for (let i: number = 0; i < 2000; i++) {
|
|
194
|
+
for (const character of Text.generateRandomText(52)) {
|
|
195
|
+
counts[character] = (counts[character] || 0) + 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
expect(Object.keys(counts)).toHaveLength(52);
|
|
200
|
+
|
|
201
|
+
const expectedPerCharacter: number = totalCharacters / 52;
|
|
202
|
+
|
|
203
|
+
for (const character of Object.keys(counts)) {
|
|
204
|
+
const count: number = counts[character] as number;
|
|
205
|
+
|
|
206
|
+
expect(count).toBeGreaterThan(expectedPerCharacter * 0.85);
|
|
207
|
+
expect(count).toBeLessThan(expectedPerCharacter * 1.15);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
@@ -9,7 +9,7 @@ import "@testing-library/jest-dom";
|
|
|
9
9
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
10
10
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
11
11
|
import React from "react";
|
|
12
|
-
import { describe, expect, jest } from "@jest/globals";
|
|
12
|
+
import { afterEach, describe, expect, jest } from "@jest/globals";
|
|
13
13
|
|
|
14
14
|
describe("Button", () => {
|
|
15
15
|
test("it should render correctly with title and icon", () => {
|
|
@@ -479,3 +479,177 @@ describe("Button disabled behaviour", () => {
|
|
|
479
479
|
expect(handleClick).not.toBeCalled();
|
|
480
480
|
});
|
|
481
481
|
});
|
|
482
|
+
|
|
483
|
+
describe("Button motion", () => {
|
|
484
|
+
/*
|
|
485
|
+
* Hover/focus colour changes ease in instead of snapping. The classes are
|
|
486
|
+
* appended once at the shared point every variant funnels through, so every
|
|
487
|
+
* style must carry them.
|
|
488
|
+
*/
|
|
489
|
+
test.each(ALL_BUTTON_STYLES)(
|
|
490
|
+
"%s carries the shared colour-transition classes",
|
|
491
|
+
(_name: string, buttonStyle: ButtonStyleType) => {
|
|
492
|
+
expect(renderButton({ buttonStyle })).toHaveClass(
|
|
493
|
+
"transition-colors",
|
|
494
|
+
"duration-150",
|
|
495
|
+
"ease-out",
|
|
496
|
+
);
|
|
497
|
+
},
|
|
498
|
+
);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
describe("Button link focus rings", () => {
|
|
502
|
+
/*
|
|
503
|
+
* LINK and SECONDARY_LINK used to have no ring at all, leaving keyboard
|
|
504
|
+
* users with no focus indicator on text links. focus-visible (rather than
|
|
505
|
+
* focus:) so mouse clicks do not flash a ring.
|
|
506
|
+
*/
|
|
507
|
+
const LINK_STYLES: Array<[string, ButtonStyleType]> = [
|
|
508
|
+
["LINK", ButtonStyleType.LINK],
|
|
509
|
+
["SECONDARY_LINK", ButtonStyleType.SECONDARY_LINK],
|
|
510
|
+
];
|
|
511
|
+
|
|
512
|
+
test.each(LINK_STYLES)(
|
|
513
|
+
"%s carries a focus-visible ring",
|
|
514
|
+
(_name: string, buttonStyle: ButtonStyleType) => {
|
|
515
|
+
expect(renderButton({ buttonStyle })).toHaveClass(
|
|
516
|
+
"focus:outline-none",
|
|
517
|
+
"focus-visible:ring-2",
|
|
518
|
+
"focus-visible:ring-indigo-500",
|
|
519
|
+
"focus-visible:ring-offset-2",
|
|
520
|
+
);
|
|
521
|
+
},
|
|
522
|
+
);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
interface ListenerCall {
|
|
526
|
+
handler: unknown;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
type FindKeydownCallsFunction = (
|
|
530
|
+
spy: ReturnType<typeof jest.spyOn>,
|
|
531
|
+
) => Array<ListenerCall>;
|
|
532
|
+
|
|
533
|
+
const findKeydownCalls: FindKeydownCallsFunction = (
|
|
534
|
+
spy: ReturnType<typeof jest.spyOn>,
|
|
535
|
+
): Array<ListenerCall> => {
|
|
536
|
+
const calls: Array<Array<unknown>> = spy.mock.calls as unknown as Array<
|
|
537
|
+
Array<unknown>
|
|
538
|
+
>;
|
|
539
|
+
|
|
540
|
+
return calls
|
|
541
|
+
.filter((call: Array<unknown>): boolean => {
|
|
542
|
+
return call[0] === "keydown";
|
|
543
|
+
})
|
|
544
|
+
.map((call: Array<unknown>): ListenerCall => {
|
|
545
|
+
return { handler: call[1] };
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
describe("Button shortcut listener lifecycle", () => {
|
|
550
|
+
afterEach(() => {
|
|
551
|
+
jest.restoreAllMocks();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
/*
|
|
555
|
+
* The regression this suite exists for: the shortcut effect had no
|
|
556
|
+
* dependency array and its cleanup passed a NEW arrow function to
|
|
557
|
+
* removeEventListener, which therefore never matched the one that was
|
|
558
|
+
* added — every render leaked a live window keydown listener.
|
|
559
|
+
*/
|
|
560
|
+
test("unmount removes the same keydown handler that was added", () => {
|
|
561
|
+
const addSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
562
|
+
window,
|
|
563
|
+
"addEventListener",
|
|
564
|
+
);
|
|
565
|
+
const removeSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
566
|
+
window,
|
|
567
|
+
"removeEventListener",
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const { unmount } = render(
|
|
571
|
+
<Button dataTestId="test-id" shortcutKey={ShortcutKey.Settings} />,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
const adds: Array<ListenerCall> = findKeydownCalls(addSpy);
|
|
575
|
+
|
|
576
|
+
expect(adds).toHaveLength(1);
|
|
577
|
+
expect(findKeydownCalls(removeSpy)).toHaveLength(0);
|
|
578
|
+
|
|
579
|
+
unmount();
|
|
580
|
+
|
|
581
|
+
const removes: Array<ListenerCall> = findKeydownCalls(removeSpy);
|
|
582
|
+
|
|
583
|
+
expect(removes).toHaveLength(1);
|
|
584
|
+
/*
|
|
585
|
+
* removeEventListener only detaches when the handler identity matches the
|
|
586
|
+
* add; counting calls alone would let the old always-leaking cleanup pass.
|
|
587
|
+
*/
|
|
588
|
+
expect(removes[0]?.handler).toBe(adds[0]?.handler);
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
test("re-rendering with the same props does not stack extra listeners", () => {
|
|
592
|
+
const addSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
593
|
+
window,
|
|
594
|
+
"addEventListener",
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
const { rerender } = render(
|
|
598
|
+
<Button dataTestId="test-id" shortcutKey={ShortcutKey.Settings} />,
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
rerender(
|
|
602
|
+
<Button dataTestId="test-id" shortcutKey={ShortcutKey.Settings} />,
|
|
603
|
+
);
|
|
604
|
+
rerender(
|
|
605
|
+
<Button dataTestId="test-id" shortcutKey={ShortcutKey.Settings} />,
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
expect(findKeydownCalls(addSpy)).toHaveLength(1);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
test("a button without a shortcut registers no keydown listener", () => {
|
|
612
|
+
const addSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
613
|
+
window,
|
|
614
|
+
"addEventListener",
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
render(<Button dataTestId="test-id" />);
|
|
618
|
+
|
|
619
|
+
expect(findKeydownCalls(addSpy)).toHaveLength(0);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
test("the shortcut key pressed on the page body fires onClick", () => {
|
|
623
|
+
const handleClick: () => void = jest.fn();
|
|
624
|
+
|
|
625
|
+
render(
|
|
626
|
+
<Button
|
|
627
|
+
dataTestId="test-id"
|
|
628
|
+
shortcutKey={ShortcutKey.Settings}
|
|
629
|
+
onClick={handleClick}
|
|
630
|
+
/>,
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
fireEvent.keyDown(document.body, { key: "s" });
|
|
634
|
+
|
|
635
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
test("the shortcut stops firing after unmount", () => {
|
|
639
|
+
const handleClick: () => void = jest.fn();
|
|
640
|
+
|
|
641
|
+
const { unmount } = render(
|
|
642
|
+
<Button
|
|
643
|
+
dataTestId="test-id"
|
|
644
|
+
shortcutKey={ShortcutKey.Settings}
|
|
645
|
+
onClick={handleClick}
|
|
646
|
+
/>,
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
unmount();
|
|
650
|
+
|
|
651
|
+
fireEvent.keyDown(document.body, { key: "s" });
|
|
652
|
+
|
|
653
|
+
expect(handleClick).not.toBeCalled();
|
|
654
|
+
});
|
|
655
|
+
});
|