@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,279 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
3
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import Table, { BulkActionProps } from "../../../UI/Components/Table/Table";
|
|
6
|
+
import Columns from "../../../UI/Components/Table/Types/Columns";
|
|
7
|
+
import FieldType from "../../../UI/Components/Types/FieldType";
|
|
8
|
+
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
|
|
9
|
+
import { ButtonStyleType } from "../../../UI/Components/Button/Button";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* The table's four loading-adjacent faces, and the order they win in.
|
|
13
|
+
*
|
|
14
|
+
* A first load has nothing to show, so it shows the SHAPE of what is coming:
|
|
15
|
+
* skeleton rows whose cell structure mirrors the header exactly (drag handle,
|
|
16
|
+
* bulk checkbox, one cell per visible column). A refetch DOES have something
|
|
17
|
+
* to show - the parent never clears `data` while fetching - so the stale rows
|
|
18
|
+
* stay on screen, dimmed and unclickable, instead of being torn down to
|
|
19
|
+
* placeholders. Error and empty only speak once loading has finished; a stale
|
|
20
|
+
* error surfacing mid-refetch over live rows was the bug this priority
|
|
21
|
+
* prevents.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
jest.mock("react-i18next", () => {
|
|
25
|
+
return {
|
|
26
|
+
useTranslation: () => {
|
|
27
|
+
return {
|
|
28
|
+
t: (key: string, opts?: { defaultValue?: string }): string => {
|
|
29
|
+
return opts?.defaultValue ?? key;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
interface Row {
|
|
37
|
+
_id?: string | undefined;
|
|
38
|
+
name?: string | undefined;
|
|
39
|
+
status?: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const columns: Columns<Row> = [
|
|
43
|
+
{ title: "Name", type: FieldType.Text, key: "name" },
|
|
44
|
+
{ title: "Status", type: FieldType.Text, key: "status", hideOnMobile: true },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const data: Array<Row> = [
|
|
48
|
+
{ _id: "1", name: "Alpha", status: "Up" },
|
|
49
|
+
{ _id: "2", name: "Beta", status: "Down" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const bulkActions: BulkActionProps<Row> = {
|
|
53
|
+
buttons: [
|
|
54
|
+
{
|
|
55
|
+
title: "Archive",
|
|
56
|
+
buttonStyleType: ButtonStyleType.NORMAL,
|
|
57
|
+
onClick: (): Promise<void> => {
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
interface RenderTableOptions {
|
|
65
|
+
data?: Array<Row> | undefined;
|
|
66
|
+
isLoading?: boolean | undefined;
|
|
67
|
+
error?: string | undefined;
|
|
68
|
+
itemsOnPage?: number | undefined;
|
|
69
|
+
enableDragAndDrop?: boolean | undefined;
|
|
70
|
+
withBulkActions?: boolean | undefined;
|
|
71
|
+
noItemsMessage?: string | undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type RenderTableFunction = (options: RenderTableOptions) => void;
|
|
75
|
+
|
|
76
|
+
const renderTable: RenderTableFunction = (
|
|
77
|
+
options: RenderTableOptions,
|
|
78
|
+
): void => {
|
|
79
|
+
render(
|
|
80
|
+
<Table<Row>
|
|
81
|
+
id="test-table"
|
|
82
|
+
data={options.data ?? data}
|
|
83
|
+
columns={columns}
|
|
84
|
+
currentPageNumber={1}
|
|
85
|
+
totalItemsCount={(options.data ?? data).length}
|
|
86
|
+
itemsOnPage={options.itemsOnPage ?? 10}
|
|
87
|
+
error={options.error ?? ""}
|
|
88
|
+
isLoading={options.isLoading ?? false}
|
|
89
|
+
singularLabel="Monitor"
|
|
90
|
+
pluralLabel="Monitors"
|
|
91
|
+
sortOrder={SortOrder.Ascending}
|
|
92
|
+
sortBy={null}
|
|
93
|
+
onSortChanged={() => {}}
|
|
94
|
+
onNavigateToPage={() => {}}
|
|
95
|
+
noItemsMessage={options.noItemsMessage}
|
|
96
|
+
enableDragAndDrop={options.enableDragAndDrop}
|
|
97
|
+
matchBulkSelectedItemByField={options.withBulkActions ? "_id" : undefined}
|
|
98
|
+
bulkActions={options.withBulkActions ? bulkActions : undefined}
|
|
99
|
+
bulkSelectedItems={options.withBulkActions ? [] : undefined}
|
|
100
|
+
/>,
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* The table decides mobile vs desktop from window.innerWidth at mount
|
|
106
|
+
* (< 768 = mobile). jsdom defaults to 1024, so desktop is the baseline and
|
|
107
|
+
* mobile tests must set the width BEFORE rendering.
|
|
108
|
+
*/
|
|
109
|
+
const setViewportWidth: (width: number) => void = (width: number): void => {
|
|
110
|
+
Object.defineProperty(window, "innerWidth", {
|
|
111
|
+
configurable: true,
|
|
112
|
+
writable: true,
|
|
113
|
+
value: width,
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
afterEach(() => {
|
|
118
|
+
cleanup();
|
|
119
|
+
setViewportWidth(1024);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("first load (nothing to show yet)", () => {
|
|
123
|
+
test("renders skeleton rows instead of a spinner", () => {
|
|
124
|
+
renderTable({ isLoading: true, data: [] });
|
|
125
|
+
|
|
126
|
+
expect(screen.getByTestId("table-skeleton-loader")).toBeInTheDocument();
|
|
127
|
+
expect(screen.queryByTestId("component-loader")).toBeNull();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("announces itself as a polite status with a Loading label", () => {
|
|
131
|
+
renderTable({ isLoading: true, data: [] });
|
|
132
|
+
|
|
133
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
134
|
+
expect(loader).toHaveAttribute("role", "status");
|
|
135
|
+
expect(loader).toHaveAttribute("aria-live", "polite");
|
|
136
|
+
expect(loader).toHaveTextContent("Loading");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("renders one skeleton row per item on the page", () => {
|
|
140
|
+
renderTable({ isLoading: true, data: [], itemsOnPage: 5 });
|
|
141
|
+
|
|
142
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
143
|
+
expect(loader.querySelectorAll("tr")).toHaveLength(5);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("caps skeleton rows at 10 even for a 25-row page", () => {
|
|
147
|
+
renderTable({ isLoading: true, data: [], itemsOnPage: 25 });
|
|
148
|
+
|
|
149
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
150
|
+
expect(loader.querySelectorAll("tr")).toHaveLength(10);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("a plain table gets one skeleton cell per column", () => {
|
|
154
|
+
renderTable({ isLoading: true, data: [] });
|
|
155
|
+
|
|
156
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
157
|
+
const firstRow: Element = loader.querySelectorAll("tr")[0] as Element;
|
|
158
|
+
expect(firstRow.querySelectorAll("td")).toHaveLength(columns.length);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
/*
|
|
162
|
+
* The header renders extra leading cells for the drag handle and the bulk
|
|
163
|
+
* checkbox. Skeleton rows must mirror them or every placeholder sits one
|
|
164
|
+
* or two columns to the left of the header it is standing in for.
|
|
165
|
+
*/
|
|
166
|
+
test("drag and bulk each add their leading skeleton cell", () => {
|
|
167
|
+
renderTable({
|
|
168
|
+
isLoading: true,
|
|
169
|
+
data: [],
|
|
170
|
+
enableDragAndDrop: true,
|
|
171
|
+
withBulkActions: true,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
175
|
+
const firstRow: Element = loader.querySelectorAll("tr")[0] as Element;
|
|
176
|
+
expect(firstRow.querySelectorAll("td")).toHaveLength(columns.length + 2);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("skeleton cells carry the real rows' padding classes", () => {
|
|
180
|
+
renderTable({ isLoading: true, data: [] });
|
|
181
|
+
|
|
182
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
183
|
+
const cells: NodeListOf<Element> = loader
|
|
184
|
+
.querySelectorAll("tr")[0]!
|
|
185
|
+
.querySelectorAll("td");
|
|
186
|
+
|
|
187
|
+
// Same classes as TableRow's cells: last cell gets pr-6, the rest pr-3.
|
|
188
|
+
expect(cells[0]).toHaveClass("py-4", "pl-4", "pr-3", "whitespace-nowrap");
|
|
189
|
+
expect(cells[cells.length - 1]).toHaveClass("pr-6");
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe("mobile first load", () => {
|
|
194
|
+
test("renders a card-shaped stack, not table markup inside a div", () => {
|
|
195
|
+
setViewportWidth(500);
|
|
196
|
+
renderTable({ isLoading: true, data: [], itemsOnPage: 5 });
|
|
197
|
+
|
|
198
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
199
|
+
expect(loader.tagName).toBe("DIV");
|
|
200
|
+
/*
|
|
201
|
+
* The old loader emitted a <tbody> into the mobile <div> wrapper -
|
|
202
|
+
* invalid HTML that React happened to tolerate. Nothing table-shaped
|
|
203
|
+
* may remain on mobile.
|
|
204
|
+
*/
|
|
205
|
+
expect(document.querySelector("tbody")).toBeNull();
|
|
206
|
+
expect(loader.querySelectorAll(":scope > div")).toHaveLength(5);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("hideOnMobile columns get no placeholder line", () => {
|
|
210
|
+
setViewportWidth(500);
|
|
211
|
+
renderTable({ isLoading: true, data: [], itemsOnPage: 3 });
|
|
212
|
+
|
|
213
|
+
const loader: HTMLElement = screen.getByTestId("table-skeleton-loader");
|
|
214
|
+
const firstCard: Element = loader.querySelectorAll(
|
|
215
|
+
":scope > div",
|
|
216
|
+
)[0] as Element;
|
|
217
|
+
|
|
218
|
+
// One label/value group per VISIBLE column: Status is hideOnMobile.
|
|
219
|
+
expect(firstCard.querySelectorAll(".flex.flex-col")).toHaveLength(1);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("refetch with rows already on screen", () => {
|
|
224
|
+
test("keeps the real rows and shows no skeletons", () => {
|
|
225
|
+
renderTable({ isLoading: true });
|
|
226
|
+
|
|
227
|
+
expect(screen.getByText("Alpha")).toBeInTheDocument();
|
|
228
|
+
expect(screen.getByText("Beta")).toBeInTheDocument();
|
|
229
|
+
expect(screen.queryByTestId("table-skeleton-loader")).toBeNull();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("dims the stale rows and blocks pointer events on them", () => {
|
|
233
|
+
renderTable({ isLoading: true });
|
|
234
|
+
|
|
235
|
+
const content: HTMLElement = screen.getByTestId("table-content");
|
|
236
|
+
expect(content).toHaveClass("opacity-60");
|
|
237
|
+
expect(content).toHaveClass("pointer-events-none");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("an idle table is neither dimmed nor blocked", () => {
|
|
241
|
+
renderTable({});
|
|
242
|
+
|
|
243
|
+
const content: HTMLElement = screen.getByTestId("table-content");
|
|
244
|
+
expect(content).not.toHaveClass("opacity-60");
|
|
245
|
+
expect(content).not.toHaveClass("pointer-events-none");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* Loading outranks error: mid-refetch the error prop can only be stale
|
|
250
|
+
* (the parent clears it before every fetch), so surfacing it over live
|
|
251
|
+
* rows would report a failure that is not happening.
|
|
252
|
+
*/
|
|
253
|
+
test("a stale error does not interrupt the dimmed rows", () => {
|
|
254
|
+
renderTable({ isLoading: true, error: "stale failure" });
|
|
255
|
+
|
|
256
|
+
expect(screen.getByText("Alpha")).toBeInTheDocument();
|
|
257
|
+
expect(screen.queryByText("stale failure")).toBeNull();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe("after loading finishes", () => {
|
|
262
|
+
test("an error replaces the rows", () => {
|
|
263
|
+
renderTable({ isLoading: false, error: "Something went wrong" });
|
|
264
|
+
|
|
265
|
+
expect(screen.getByText("Something went wrong")).toBeInTheDocument();
|
|
266
|
+
expect(screen.queryByTestId("table-skeleton-loader")).toBeNull();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("an empty result shows the no-items message, not skeletons", () => {
|
|
270
|
+
renderTable({
|
|
271
|
+
isLoading: false,
|
|
272
|
+
data: [],
|
|
273
|
+
noItemsMessage: "No monitors here",
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(screen.getByText("No monitors here")).toBeInTheDocument();
|
|
277
|
+
expect(screen.queryByTestId("table-skeleton-loader")).toBeNull();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
@@ -69,6 +69,30 @@ describe("Tabs", () => {
|
|
|
69
69
|
expect(getByTestId("tab-tab2")).toHaveClass(activeClass);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
test("tabs ease colour changes and carry a focus-visible ring", () => {
|
|
73
|
+
const onTabChange: MockFunction = getJestMockFunction();
|
|
74
|
+
|
|
75
|
+
const { getByTestId } = render(
|
|
76
|
+
<Tabs tabs={tabs} onTabChange={onTabChange} />,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* Selected and unselected tabs share the base classes, so asserting one
|
|
81
|
+
* of each covers the whole strip. The ring is focus-visible (not focus:)
|
|
82
|
+
* so mouse clicks do not flash it.
|
|
83
|
+
*/
|
|
84
|
+
for (const tabTestId of ["tab-tab1", "tab-tab2"]) {
|
|
85
|
+
expect(getByTestId(tabTestId)).toHaveClass(
|
|
86
|
+
"transition-colors",
|
|
87
|
+
"duration-150",
|
|
88
|
+
"focus:outline-none",
|
|
89
|
+
"focus-visible:ring-2",
|
|
90
|
+
"focus-visible:ring-indigo-500",
|
|
91
|
+
"focus-visible:ring-offset-2",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
72
96
|
test("it should handle empty tabs array gracefully", () => {
|
|
73
97
|
const tabs: Array<Tab> = [];
|
|
74
98
|
const onTabChange: MockFunction = getJestMockFunction();
|
|
@@ -1,19 +1,77 @@
|
|
|
1
1
|
import Toast, { ToastType } from "../../../UI/Components/Toast/Toast";
|
|
2
2
|
import "@testing-library/jest-dom";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
act,
|
|
5
|
+
fireEvent,
|
|
6
|
+
render,
|
|
7
|
+
screen,
|
|
8
|
+
waitFor,
|
|
9
|
+
} from "@testing-library/react";
|
|
4
10
|
import userEvent from "@testing-library/user-event";
|
|
5
11
|
import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup";
|
|
6
12
|
import * as React from "react";
|
|
7
|
-
import { describe, expect, test } from "@jest/globals";
|
|
13
|
+
import { describe, expect, jest, test } from "@jest/globals";
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* The toast mounts closed (faded, nudged down) and opens on the next
|
|
17
|
+
* animation frame, so anything asserting the settled look has to let that
|
|
18
|
+
* frame run first — the same choreography Modal.test.tsx flushes.
|
|
19
|
+
*/
|
|
20
|
+
type FlushEntranceFrameFunction = () => Promise<void>;
|
|
21
|
+
|
|
22
|
+
const flushEntranceFrame: FlushEntranceFrameFunction =
|
|
23
|
+
async (): Promise<void> => {
|
|
24
|
+
await act(async () => {
|
|
25
|
+
await new Promise<void>((resolve: () => void) => {
|
|
26
|
+
requestAnimationFrame(() => {
|
|
27
|
+
resolve();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
};
|
|
8
32
|
|
|
9
33
|
describe("Test for Toast.tsx", () => {
|
|
10
|
-
test("should render the
|
|
34
|
+
test("should render the toast card", () => {
|
|
11
35
|
render(
|
|
12
36
|
<Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
|
|
13
37
|
);
|
|
14
|
-
|
|
15
|
-
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* The card itself, not a fixed strip: positioning now belongs to the ONE
|
|
41
|
+
* region container ToastInit renders (see ToastStacking.test.tsx), so
|
|
42
|
+
* stacked toasts no longer paint on top of each other at top-0.
|
|
43
|
+
*/
|
|
44
|
+
const toast: HTMLElement = screen.getByTestId("toast");
|
|
45
|
+
|
|
46
|
+
expect(toast).toHaveClass(
|
|
47
|
+
"pointer-events-auto",
|
|
48
|
+
"w-full",
|
|
49
|
+
"max-w-sm",
|
|
50
|
+
"rounded-lg",
|
|
51
|
+
"bg-white",
|
|
52
|
+
"shadow-lg",
|
|
16
53
|
);
|
|
54
|
+
expect(toast).not.toHaveClass("fixed");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("enters by fading and lifting in from a closed state", async () => {
|
|
58
|
+
render(
|
|
59
|
+
<Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const toast: HTMLElement = screen.getByTestId("toast");
|
|
63
|
+
|
|
64
|
+
expect(toast).toHaveClass("opacity-0", "translate-y-2");
|
|
65
|
+
|
|
66
|
+
await flushEntranceFrame();
|
|
67
|
+
|
|
68
|
+
expect(toast).toHaveClass("opacity-100");
|
|
69
|
+
/*
|
|
70
|
+
* A settled toast carries no transform class — a lingering translate
|
|
71
|
+
* would make the card the containing block for position:fixed
|
|
72
|
+
* descendants.
|
|
73
|
+
*/
|
|
74
|
+
expect(toast.className).not.toMatch(/(^|\s)(scale|translate)-/);
|
|
17
75
|
});
|
|
18
76
|
|
|
19
77
|
test("should have close button", () => {
|
|
@@ -59,16 +117,81 @@ describe("Test for Toast.tsx", () => {
|
|
|
59
117
|
render(<Toast type={ToastType.NORMAL} title="Spread" description="Love" />);
|
|
60
118
|
expect(screen.getByTestId("toast-icon")).toHaveClass("text-gray-400");
|
|
61
119
|
});
|
|
62
|
-
|
|
120
|
+
|
|
121
|
+
test("clicking close fades the toast out and only then unmounts it", async () => {
|
|
63
122
|
const user: UserEvent = userEvent.setup();
|
|
64
123
|
|
|
65
124
|
render(
|
|
66
125
|
<Toast type={ToastType.SUCCESS} title="Spread" description="Love" />,
|
|
67
126
|
);
|
|
68
127
|
|
|
128
|
+
await flushEntranceFrame();
|
|
129
|
+
|
|
69
130
|
const closeButton: HTMLButtonElement = screen.getByTestId("close-button");
|
|
70
131
|
await user.click(closeButton);
|
|
71
132
|
|
|
72
|
-
|
|
133
|
+
/*
|
|
134
|
+
* The exit runs as a fade with a delayed unmount, so immediately after
|
|
135
|
+
* the click the card is still mounted, at zero opacity.
|
|
136
|
+
*/
|
|
137
|
+
expect(screen.getByTestId("toast")).toHaveClass("opacity-0");
|
|
138
|
+
|
|
139
|
+
await waitFor(() => {
|
|
140
|
+
expect(screen.queryByTestId("toast")).toBeFalsy();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* Owners (ToastInit) may hand the toast a NEW onClose identity on any
|
|
146
|
+
* re-render. The exit timer must not restart because of that — it keys off
|
|
147
|
+
* isLeaving alone and reads the latest onClose through a ref — otherwise a
|
|
148
|
+
* re-render mid-fade would leave an invisible pointer-events-auto card
|
|
149
|
+
* intercepting clicks past the original deadline.
|
|
150
|
+
*/
|
|
151
|
+
test("a re-render with a new onClose mid-exit neither restarts the timer nor calls a stale handler", () => {
|
|
152
|
+
jest.useFakeTimers();
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const staleOnClose: () => void = jest.fn();
|
|
156
|
+
const latestOnClose: () => void = jest.fn();
|
|
157
|
+
|
|
158
|
+
const { rerender } = render(
|
|
159
|
+
<Toast
|
|
160
|
+
type={ToastType.SUCCESS}
|
|
161
|
+
title="Spread"
|
|
162
|
+
description="Love"
|
|
163
|
+
onClose={staleOnClose}
|
|
164
|
+
/>,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
fireEvent.click(screen.getByTestId("close-button"));
|
|
168
|
+
|
|
169
|
+
// 100ms into the 150ms exit fade the owner re-renders the toast…
|
|
170
|
+
act(() => {
|
|
171
|
+
jest.advanceTimersByTime(100);
|
|
172
|
+
});
|
|
173
|
+
rerender(
|
|
174
|
+
<Toast
|
|
175
|
+
type={ToastType.SUCCESS}
|
|
176
|
+
title="Spread"
|
|
177
|
+
description="Love"
|
|
178
|
+
onClose={latestOnClose}
|
|
179
|
+
/>,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// …the card is still mid-fade…
|
|
183
|
+
expect(screen.getByTestId("toast")).toHaveClass("opacity-0");
|
|
184
|
+
|
|
185
|
+
// …and the ORIGINAL 150ms deadline still unmounts it 50ms later.
|
|
186
|
+
act(() => {
|
|
187
|
+
jest.advanceTimersByTime(50);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(screen.queryByTestId("toast")).toBeFalsy();
|
|
191
|
+
expect(latestOnClose).toHaveBeenCalledTimes(1);
|
|
192
|
+
expect(staleOnClose).not.toHaveBeenCalled();
|
|
193
|
+
} finally {
|
|
194
|
+
jest.useRealTimers();
|
|
195
|
+
}
|
|
73
196
|
});
|
|
74
197
|
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import ToastLayout, {
|
|
2
|
+
ShowToastNotification,
|
|
3
|
+
} from "../../../UI/Components/Toast/ToastInit";
|
|
4
|
+
import { ToastType } from "../../../UI/Components/Toast/Toast";
|
|
5
|
+
import "@testing-library/jest-dom";
|
|
6
|
+
import {
|
|
7
|
+
act,
|
|
8
|
+
cleanup,
|
|
9
|
+
fireEvent,
|
|
10
|
+
render,
|
|
11
|
+
screen,
|
|
12
|
+
waitFor,
|
|
13
|
+
} from "@testing-library/react";
|
|
14
|
+
import React from "react";
|
|
15
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* ToastInit used to render every toast as its own fixed strip at top-0 (the
|
|
19
|
+
* index prop that was supposed to offset them was never passed), so stacked
|
|
20
|
+
* toasts painted on top of each other and only the last one was readable.
|
|
21
|
+
* The contract pinned here: ONE fixed region, cards stacked in a flex column
|
|
22
|
+
* in insertion order.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
type ShowToastFunction = (title: string, onClose?: () => void) => void;
|
|
26
|
+
|
|
27
|
+
const showToast: ShowToastFunction = (
|
|
28
|
+
title: string,
|
|
29
|
+
onClose?: () => void,
|
|
30
|
+
): void => {
|
|
31
|
+
act(() => {
|
|
32
|
+
ShowToastNotification({
|
|
33
|
+
title,
|
|
34
|
+
description: `${title} description`,
|
|
35
|
+
type: ToastType.SUCCESS,
|
|
36
|
+
onClose,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe("Toast stacking", () => {
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
cleanup();
|
|
44
|
+
jest.clearAllMocks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("renders one fixed region that keeps its slot in the z-index ladder", () => {
|
|
48
|
+
render(<ToastLayout />);
|
|
49
|
+
|
|
50
|
+
const region: HTMLElement = screen.getByTestId("toast-region");
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* z-40 is deliberate: above SideOver (z-30), below Modal (z-50). The
|
|
54
|
+
* region itself takes no pointer events; only the cards inside do.
|
|
55
|
+
*/
|
|
56
|
+
expect(region).toHaveClass(
|
|
57
|
+
"pointer-events-none",
|
|
58
|
+
"fixed",
|
|
59
|
+
"z-40",
|
|
60
|
+
"top-0",
|
|
61
|
+
"left-0",
|
|
62
|
+
"right-0",
|
|
63
|
+
);
|
|
64
|
+
expect(region).toHaveClass("sm:items-start", "sm:p-6");
|
|
65
|
+
expect(region).toHaveAttribute("aria-live", "assertive");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("stacks multiple toasts in one region, in insertion order", () => {
|
|
69
|
+
render(<ToastLayout />);
|
|
70
|
+
|
|
71
|
+
showToast("First");
|
|
72
|
+
showToast("Second");
|
|
73
|
+
showToast("Third");
|
|
74
|
+
|
|
75
|
+
const toasts: Array<HTMLElement> = screen.getAllByTestId("toast");
|
|
76
|
+
|
|
77
|
+
expect(toasts).toHaveLength(3);
|
|
78
|
+
|
|
79
|
+
// All three cards live inside the ONE region, not three fixed strips.
|
|
80
|
+
const region: HTMLElement = screen.getByTestId("toast-region");
|
|
81
|
+
|
|
82
|
+
for (const toast of toasts) {
|
|
83
|
+
expect(region).toContainElement(toast);
|
|
84
|
+
expect(toast).not.toHaveClass("fixed");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The stack is a flex column with a gap, so cards never overlap.
|
|
88
|
+
const stack: HTMLElement = toasts[0]?.parentElement as HTMLElement;
|
|
89
|
+
|
|
90
|
+
expect(stack).toHaveClass("flex", "flex-col", "gap-4");
|
|
91
|
+
expect(toasts[1]?.parentElement).toBe(stack);
|
|
92
|
+
expect(toasts[2]?.parentElement).toBe(stack);
|
|
93
|
+
|
|
94
|
+
const titles: Array<string | null> = screen
|
|
95
|
+
.getAllByTestId("title")
|
|
96
|
+
.map((title: HTMLElement) => {
|
|
97
|
+
return title.textContent;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(titles).toEqual(["First", "Second", "Third"]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("dismissing one toast removes only that toast and calls its onClose", async () => {
|
|
104
|
+
const onCloseFirst: () => void = jest.fn();
|
|
105
|
+
|
|
106
|
+
render(<ToastLayout />);
|
|
107
|
+
|
|
108
|
+
showToast("First", onCloseFirst);
|
|
109
|
+
showToast("Second");
|
|
110
|
+
|
|
111
|
+
const closeButtons: Array<HTMLElement> =
|
|
112
|
+
screen.getAllByTestId("close-button");
|
|
113
|
+
|
|
114
|
+
expect(closeButtons).toHaveLength(2);
|
|
115
|
+
|
|
116
|
+
fireEvent.click(closeButtons[0] as HTMLElement);
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* The exit fade delays the unmount, so removal is awaited rather than
|
|
120
|
+
* asserted synchronously.
|
|
121
|
+
*/
|
|
122
|
+
await waitFor(() => {
|
|
123
|
+
expect(screen.getAllByTestId("toast")).toHaveLength(1);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(onCloseFirst).toHaveBeenCalledTimes(1);
|
|
127
|
+
expect(screen.getByTestId("title")).toHaveTextContent("Second");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("a toast dispatched after a dismissal still stacks below the survivors", async () => {
|
|
131
|
+
render(<ToastLayout />);
|
|
132
|
+
|
|
133
|
+
showToast("First");
|
|
134
|
+
showToast("Second");
|
|
135
|
+
|
|
136
|
+
fireEvent.click(screen.getAllByTestId("close-button")[0] as HTMLElement);
|
|
137
|
+
|
|
138
|
+
await waitFor(() => {
|
|
139
|
+
expect(screen.getAllByTestId("toast")).toHaveLength(1);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
showToast("Third");
|
|
143
|
+
|
|
144
|
+
const titles: Array<string | null> = screen
|
|
145
|
+
.getAllByTestId("title")
|
|
146
|
+
.map((title: HTMLElement) => {
|
|
147
|
+
return title.textContent;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(titles).toEqual(["Second", "Third"]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* Regression: dispatching a toast re-renders the whole stack, and the
|
|
155
|
+
* layout used to hand every card a freshly created onClose on each render.
|
|
156
|
+
* A dismissed card mid-exit-fade saw the new prop, cleared its unmount
|
|
157
|
+
* timer, and re-armed it — so a burst of toasts could keep an invisible
|
|
158
|
+
* pointer-events-auto card alive indefinitely, intercepting clicks.
|
|
159
|
+
*/
|
|
160
|
+
test("a toast dispatched during another toast's exit fade does not restart its unmount timer", () => {
|
|
161
|
+
jest.useFakeTimers();
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const onCloseFirst: () => void = jest.fn();
|
|
165
|
+
|
|
166
|
+
render(<ToastLayout />);
|
|
167
|
+
|
|
168
|
+
showToast("First", onCloseFirst);
|
|
169
|
+
|
|
170
|
+
fireEvent.click(screen.getByTestId("close-button"));
|
|
171
|
+
|
|
172
|
+
// 100ms into the 150ms exit fade a second toast re-renders the stack.
|
|
173
|
+
act(() => {
|
|
174
|
+
jest.advanceTimersByTime(100);
|
|
175
|
+
});
|
|
176
|
+
showToast("Second");
|
|
177
|
+
|
|
178
|
+
expect(screen.getAllByTestId("toast")).toHaveLength(2);
|
|
179
|
+
|
|
180
|
+
// The ORIGINAL 150ms deadline holds: 50ms later the first card is gone.
|
|
181
|
+
act(() => {
|
|
182
|
+
jest.advanceTimersByTime(50);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(onCloseFirst).toHaveBeenCalledTimes(1);
|
|
186
|
+
|
|
187
|
+
const titles: Array<string | null> = screen
|
|
188
|
+
.getAllByTestId("title")
|
|
189
|
+
.map((title: HTMLElement) => {
|
|
190
|
+
return title.textContent;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(titles).toEqual(["Second"]);
|
|
194
|
+
} finally {
|
|
195
|
+
jest.useRealTimers();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|