@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,347 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
ComponentArgument,
|
|
4
|
+
ComponentInputType,
|
|
5
|
+
EntityFilterModelType,
|
|
6
|
+
} from "../../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
7
|
+
import DashboardBaseComponent from "../../../../Types/Dashboard/DashboardComponents/DashboardBaseComponent";
|
|
8
|
+
import {
|
|
9
|
+
KubernetesCommonArgumentsOptions,
|
|
10
|
+
KubernetesDisplaySection,
|
|
11
|
+
KubernetesFiltersSection,
|
|
12
|
+
getKubernetesCommonArguments,
|
|
13
|
+
} from "../../../../Utils/Dashboard/Components/DashboardKubernetesResourceListShared";
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* getKubernetesCommonArguments builds the shared config-form fields for every
|
|
17
|
+
* Kubernetes resource-list widget (pods, deployments, services, ...). It is a
|
|
18
|
+
* fixed sequence of args.push() calls with exactly one conditional branch: the
|
|
19
|
+
* "Namespaces" text filter is appended only when
|
|
20
|
+
* options.includeNamespaceFilter is true.
|
|
21
|
+
*
|
|
22
|
+
* The failure modes worth guarding are the quiet ones the compiler cannot see:
|
|
23
|
+
* - the namespace branch leaking into a widget that has no namespaces, or
|
|
24
|
+
* being dropped from one that does;
|
|
25
|
+
* - an argument being mis-sectioned so a Display option renders under Filters
|
|
26
|
+
* (or vice-versa), since section is what the editor groups fields by;
|
|
27
|
+
* - a wrong ComponentInputType or a wrong entityFilterModelType, which would
|
|
28
|
+
* render the wrong editor or wire the cluster picker to the wrong model;
|
|
29
|
+
* - a duplicated id, which silently makes the second field overwrite the
|
|
30
|
+
* first's stored value;
|
|
31
|
+
* - the function returning a shared/mutated array across calls, which would
|
|
32
|
+
* let one widget's edited config bleed into the next.
|
|
33
|
+
*
|
|
34
|
+
* All inputs are pure booleans, so the whole suite is deterministic with no
|
|
35
|
+
* mocks, network, DB, clock, or randomness.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
type Args = Array<ComponentArgument<DashboardBaseComponent>>;
|
|
39
|
+
|
|
40
|
+
const withNamespace: KubernetesCommonArgumentsOptions = {
|
|
41
|
+
includeNamespaceFilter: true,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const withoutNamespace: KubernetesCommonArgumentsOptions = {
|
|
45
|
+
includeNamespaceFilter: false,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function build(withNamespaceFilter: boolean): Args {
|
|
49
|
+
return getKubernetesCommonArguments<DashboardBaseComponent>({
|
|
50
|
+
includeNamespaceFilter: withNamespaceFilter,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function findById(
|
|
55
|
+
args: Args,
|
|
56
|
+
id: string,
|
|
57
|
+
): ComponentArgument<DashboardBaseComponent> {
|
|
58
|
+
const match: ComponentArgument<DashboardBaseComponent> | undefined =
|
|
59
|
+
args.find((a: ComponentArgument<DashboardBaseComponent>) => {
|
|
60
|
+
return String(a.id) === id;
|
|
61
|
+
});
|
|
62
|
+
if (!match) {
|
|
63
|
+
throw new Error(`Expected an argument with id "${id}" but none was found`);
|
|
64
|
+
}
|
|
65
|
+
return match;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("getKubernetesCommonArguments", () => {
|
|
69
|
+
describe("argument count and namespace branch", () => {
|
|
70
|
+
test("includes the namespace filter as a 5th argument when requested", () => {
|
|
71
|
+
const args: Args = build(true);
|
|
72
|
+
expect(args).toHaveLength(5);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("omits the namespace filter when not requested", () => {
|
|
76
|
+
const args: Args = build(false);
|
|
77
|
+
expect(args).toHaveLength(4);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("the only structural difference between the two modes is the Namespaces field", () => {
|
|
81
|
+
const withNs: Args = build(true);
|
|
82
|
+
const withoutNs: Args = build(false);
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* The first four arguments must be identical in both modes; the branch
|
|
86
|
+
* appends, it must never re-order or alter the fields before it.
|
|
87
|
+
*/
|
|
88
|
+
expect(withNs.slice(0, 4)).toEqual(withoutNs);
|
|
89
|
+
|
|
90
|
+
const extra: ComponentArgument<DashboardBaseComponent> | undefined =
|
|
91
|
+
withNs[4];
|
|
92
|
+
expect(extra).toBeDefined();
|
|
93
|
+
expect(String(extra!.id)).toBe("namespaces");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("the namespaces argument is absent (not merely undefined) when excluded", () => {
|
|
97
|
+
const args: Args = build(false);
|
|
98
|
+
const ids: Array<string> = args.map(
|
|
99
|
+
(a: ComponentArgument<DashboardBaseComponent>) => {
|
|
100
|
+
return String(a.id);
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
expect(ids).not.toContain("namespaces");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("argument order and identity", () => {
|
|
108
|
+
test("arguments are emitted in a stable, documented order (with namespace)", () => {
|
|
109
|
+
const ids: Array<string> = build(true).map(
|
|
110
|
+
(a: ComponentArgument<DashboardBaseComponent>) => {
|
|
111
|
+
return String(a.id);
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
expect(ids).toEqual([
|
|
115
|
+
"title",
|
|
116
|
+
"maxRows",
|
|
117
|
+
"viewMode",
|
|
118
|
+
"kubernetesClusterIds",
|
|
119
|
+
"namespaces",
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("arguments are emitted in a stable order (without namespace)", () => {
|
|
124
|
+
const ids: Array<string> = build(false).map(
|
|
125
|
+
(a: ComponentArgument<DashboardBaseComponent>) => {
|
|
126
|
+
return String(a.id);
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
expect(ids).toEqual([
|
|
130
|
+
"title",
|
|
131
|
+
"maxRows",
|
|
132
|
+
"viewMode",
|
|
133
|
+
"kubernetesClusterIds",
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("no argument id is duplicated in either mode", () => {
|
|
138
|
+
for (const args of [build(true), build(false)]) {
|
|
139
|
+
const ids: Array<string> = args.map(
|
|
140
|
+
(a: ComponentArgument<DashboardBaseComponent>) => {
|
|
141
|
+
return String(a.id);
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
expect(new Set<string>(ids).size).toBe(ids.length);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("Title argument", () => {
|
|
150
|
+
test("is an optional text field in the Display section", () => {
|
|
151
|
+
const title: ComponentArgument<DashboardBaseComponent> = findById(
|
|
152
|
+
build(true),
|
|
153
|
+
"title",
|
|
154
|
+
);
|
|
155
|
+
expect(title.name).toBe("Title");
|
|
156
|
+
expect(title.type).toBe(ComponentInputType.Text);
|
|
157
|
+
expect(title.required).toBe(false);
|
|
158
|
+
expect(title.section).toBe(KubernetesDisplaySection);
|
|
159
|
+
expect(title.description.length).toBeGreaterThan(0);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("Max Rows argument", () => {
|
|
164
|
+
test("is an optional number field with a placeholder in the Display section", () => {
|
|
165
|
+
const maxRows: ComponentArgument<DashboardBaseComponent> = findById(
|
|
166
|
+
build(true),
|
|
167
|
+
"maxRows",
|
|
168
|
+
);
|
|
169
|
+
expect(maxRows.name).toBe("Max Rows");
|
|
170
|
+
expect(maxRows.type).toBe(ComponentInputType.Number);
|
|
171
|
+
expect(maxRows.required).toBe(false);
|
|
172
|
+
expect(maxRows.placeholder).toBe("25");
|
|
173
|
+
expect(maxRows.section).toBe(KubernetesDisplaySection);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("View Mode argument", () => {
|
|
178
|
+
test("is the shared dropdown offering list and honeycomb views", () => {
|
|
179
|
+
const viewMode: ComponentArgument<DashboardBaseComponent> = findById(
|
|
180
|
+
build(true),
|
|
181
|
+
"viewMode",
|
|
182
|
+
);
|
|
183
|
+
expect(viewMode.name).toBe("View Mode");
|
|
184
|
+
expect(viewMode.type).toBe(ComponentInputType.Dropdown);
|
|
185
|
+
expect(viewMode.required).toBe(false);
|
|
186
|
+
expect(viewMode.section).toBe(KubernetesDisplaySection);
|
|
187
|
+
|
|
188
|
+
const values: Array<unknown> = (viewMode.dropdownOptions ?? []).map(
|
|
189
|
+
(o: { value: unknown }) => {
|
|
190
|
+
return o.value;
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
expect(values).toEqual(["list", "honeycomb"]);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("Clusters argument", () => {
|
|
198
|
+
test("is an entity multi-select bound to the KubernetesCluster model in the Filters section", () => {
|
|
199
|
+
const clusters: ComponentArgument<DashboardBaseComponent> = findById(
|
|
200
|
+
build(true),
|
|
201
|
+
"kubernetesClusterIds",
|
|
202
|
+
);
|
|
203
|
+
expect(clusters.name).toBe("Clusters");
|
|
204
|
+
expect(clusters.type).toBe(ComponentInputType.EntityMultiSelectDropdown);
|
|
205
|
+
expect(clusters.required).toBe(false);
|
|
206
|
+
expect(clusters.placeholder).toBe("All clusters");
|
|
207
|
+
expect(clusters.section).toBe(KubernetesFiltersSection);
|
|
208
|
+
expect(clusters.entityFilterModelType).toBe(
|
|
209
|
+
EntityFilterModelType.KubernetesCluster,
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("Namespaces argument", () => {
|
|
215
|
+
test("is an optional text field with an example placeholder in the Filters section", () => {
|
|
216
|
+
const namespaces: ComponentArgument<DashboardBaseComponent> = findById(
|
|
217
|
+
build(true),
|
|
218
|
+
"namespaces",
|
|
219
|
+
);
|
|
220
|
+
expect(namespaces.name).toBe("Namespaces");
|
|
221
|
+
expect(namespaces.type).toBe(ComponentInputType.Text);
|
|
222
|
+
expect(namespaces.required).toBe(false);
|
|
223
|
+
expect(namespaces.placeholder).toBe("default, kube-system");
|
|
224
|
+
expect(namespaces.section).toBe(KubernetesFiltersSection);
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* The cluster picker is an entity dropdown; the namespaces field is a
|
|
228
|
+
* free-text list, so it must NOT carry an entityFilterModelType.
|
|
229
|
+
*/
|
|
230
|
+
expect(namespaces.entityFilterModelType).toBeUndefined();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("section assignment and exported section constants", () => {
|
|
235
|
+
test("every argument is required=false (all fields are optional filters/options)", () => {
|
|
236
|
+
for (const args of [build(true), build(false)]) {
|
|
237
|
+
for (const arg of args) {
|
|
238
|
+
expect(arg.required).toBe(false);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("Display options come before Filters and are grouped by their sections", () => {
|
|
244
|
+
const args: Args = build(true);
|
|
245
|
+
|
|
246
|
+
const displayIds: Array<string> = args
|
|
247
|
+
.filter((a: ComponentArgument<DashboardBaseComponent>) => {
|
|
248
|
+
return a.section === KubernetesDisplaySection;
|
|
249
|
+
})
|
|
250
|
+
.map((a: ComponentArgument<DashboardBaseComponent>) => {
|
|
251
|
+
return String(a.id);
|
|
252
|
+
});
|
|
253
|
+
const filterIds: Array<string> = args
|
|
254
|
+
.filter((a: ComponentArgument<DashboardBaseComponent>) => {
|
|
255
|
+
return a.section === KubernetesFiltersSection;
|
|
256
|
+
})
|
|
257
|
+
.map((a: ComponentArgument<DashboardBaseComponent>) => {
|
|
258
|
+
return String(a.id);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
expect(displayIds).toEqual(["title", "maxRows", "viewMode"]);
|
|
262
|
+
expect(filterIds).toEqual(["kubernetesClusterIds", "namespaces"]);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("every argument references one of the two known sections", () => {
|
|
266
|
+
for (const args of [build(true), build(false)]) {
|
|
267
|
+
for (const arg of args) {
|
|
268
|
+
const isKnownSection: boolean =
|
|
269
|
+
arg.section === KubernetesDisplaySection ||
|
|
270
|
+
arg.section === KubernetesFiltersSection;
|
|
271
|
+
expect(isKnownSection).toBe(true);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("the exported sections carry the expected ordering and collapse behavior", () => {
|
|
277
|
+
expect(KubernetesDisplaySection.name).toBe("Display Options");
|
|
278
|
+
expect(KubernetesDisplaySection.order).toBe(1);
|
|
279
|
+
|
|
280
|
+
expect(KubernetesFiltersSection.name).toBe("Filters");
|
|
281
|
+
expect(KubernetesFiltersSection.order).toBe(2);
|
|
282
|
+
|
|
283
|
+
/*
|
|
284
|
+
* Filters render below Display, so they default to collapsed while
|
|
285
|
+
* Display stays open.
|
|
286
|
+
*/
|
|
287
|
+
expect(KubernetesFiltersSection.defaultCollapsed).toBe(true);
|
|
288
|
+
expect(KubernetesDisplaySection.defaultCollapsed).toBeUndefined();
|
|
289
|
+
expect(KubernetesFiltersSection.order).toBeGreaterThan(
|
|
290
|
+
KubernetesDisplaySection.order,
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe("well-formedness of every argument", () => {
|
|
296
|
+
const validInputTypes: Set<string> = new Set<string>(
|
|
297
|
+
Object.values(ComponentInputType),
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
test("each argument has a non-empty name, id, boolean required, and valid input type", () => {
|
|
301
|
+
for (const args of [build(true), build(false)]) {
|
|
302
|
+
for (const arg of args) {
|
|
303
|
+
expect(typeof arg.name).toBe("string");
|
|
304
|
+
expect(arg.name.length).toBeGreaterThan(0);
|
|
305
|
+
|
|
306
|
+
expect(typeof arg.id).toBe("string");
|
|
307
|
+
expect(String(arg.id).length).toBeGreaterThan(0);
|
|
308
|
+
|
|
309
|
+
expect(typeof arg.required).toBe("boolean");
|
|
310
|
+
|
|
311
|
+
expect(validInputTypes.has(arg.type)).toBe(true);
|
|
312
|
+
|
|
313
|
+
expect(typeof arg.description).toBe("string");
|
|
314
|
+
expect(arg.description.length).toBeGreaterThan(0);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe("determinism and array independence", () => {
|
|
321
|
+
test("repeated calls with the same options are deeply equal but not the same reference", () => {
|
|
322
|
+
const first: Args = build(true);
|
|
323
|
+
const second: Args = build(true);
|
|
324
|
+
expect(second).toEqual(first);
|
|
325
|
+
expect(second).not.toBe(first);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("mutating a returned array does not affect a subsequent call", () => {
|
|
329
|
+
const first: Args = build(true);
|
|
330
|
+
first.pop();
|
|
331
|
+
first[0]!.name = "MUTATED";
|
|
332
|
+
|
|
333
|
+
const fresh: Args = build(true);
|
|
334
|
+
expect(fresh).toHaveLength(5);
|
|
335
|
+
expect(fresh[0]!.name).toBe("Title");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("accepts the options object literals defined for both modes", () => {
|
|
339
|
+
expect(
|
|
340
|
+
getKubernetesCommonArguments<DashboardBaseComponent>(withNamespace),
|
|
341
|
+
).toHaveLength(5);
|
|
342
|
+
expect(
|
|
343
|
+
getKubernetesCommonArguments<DashboardBaseComponent>(withoutNamespace),
|
|
344
|
+
).toHaveLength(4);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
});
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
DashboardListViewMode,
|
|
4
|
+
getViewModeArgument,
|
|
5
|
+
} from "../../../../Utils/Dashboard/Components/DashboardListSharedArgs";
|
|
6
|
+
import {
|
|
7
|
+
ComponentArgument,
|
|
8
|
+
ComponentArgumentSection,
|
|
9
|
+
ComponentInputType,
|
|
10
|
+
} from "../../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
11
|
+
import DashboardBaseComponent from "../../../../Types/Dashboard/DashboardComponents/DashboardBaseComponent";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* getViewModeArgument is a pure factory: given the section a dashboard list
|
|
15
|
+
* widget's config form belongs to, it returns the single "viewMode" dropdown
|
|
16
|
+
* argument that lets an operator switch a list widget between the default table
|
|
17
|
+
* view and the status-colored honeycomb view.
|
|
18
|
+
*
|
|
19
|
+
* The contract that matters at runtime is machine-readable, not cosmetic. The
|
|
20
|
+
* `id` is the key the chosen value is persisted under, the `type` decides which
|
|
21
|
+
* editor the form renders, and the dropdown option `value`s are the exact
|
|
22
|
+
* strings ("list" / "honeycomb") the rendering code later switches on. If any
|
|
23
|
+
* of those drift, a widget silently renders the wrong view or stores a value no
|
|
24
|
+
* consumer understands, with no compiler error to catch it. This suite pins
|
|
25
|
+
* that contract, and also verifies the factory passes the caller's section
|
|
26
|
+
* through untouched and hands back independent objects on every call.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* A representative, fully-populated section. The factory should place exactly
|
|
31
|
+
* this reference on the returned argument without cloning or mutating it.
|
|
32
|
+
*/
|
|
33
|
+
const SAMPLE_SECTION: ComponentArgumentSection = {
|
|
34
|
+
name: "Appearance",
|
|
35
|
+
description: "How the widget looks.",
|
|
36
|
+
icon: "eye",
|
|
37
|
+
defaultCollapsed: false,
|
|
38
|
+
order: 3,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* The complete set of view modes the DashboardListViewMode union permits. Each
|
|
43
|
+
* dropdown option value must be one of these and, together, the options must
|
|
44
|
+
* cover all of them.
|
|
45
|
+
*/
|
|
46
|
+
const VALID_VIEW_MODES: Set<DashboardListViewMode> =
|
|
47
|
+
new Set<DashboardListViewMode>(["list", "honeycomb"]);
|
|
48
|
+
|
|
49
|
+
describe("getViewModeArgument", () => {
|
|
50
|
+
describe("static argument contract", () => {
|
|
51
|
+
test("uses the persisted key 'viewMode' as the argument id", () => {
|
|
52
|
+
/*
|
|
53
|
+
* The id is the key the selected value is stored under; if it is not
|
|
54
|
+
* exactly "viewMode" the value lands somewhere the widget never reads.
|
|
55
|
+
*/
|
|
56
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
57
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
58
|
+
|
|
59
|
+
expect(arg.id).toBe("viewMode");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("renders as a Dropdown editor", () => {
|
|
63
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
64
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
65
|
+
|
|
66
|
+
expect(arg.type).toBe(ComponentInputType.Dropdown);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("is optional, expressed as a real boolean false", () => {
|
|
70
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
71
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
72
|
+
|
|
73
|
+
/*
|
|
74
|
+
* `required` drives form validation, so it must be an actual boolean and
|
|
75
|
+
* specifically false — not undefined coerced as falsy.
|
|
76
|
+
*/
|
|
77
|
+
expect(typeof arg.required).toBe("boolean");
|
|
78
|
+
expect(arg.required).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("has a non-empty human label", () => {
|
|
82
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
83
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
84
|
+
|
|
85
|
+
expect(arg.name).toBe("View Mode");
|
|
86
|
+
expect(arg.name.length).toBeGreaterThan(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("describes both the list and honeycomb views", () => {
|
|
90
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
91
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* The description is what the operator reads to understand the toggle;
|
|
95
|
+
* it should mention both modes rather than pin exact prose.
|
|
96
|
+
*/
|
|
97
|
+
expect(typeof arg.description).toBe("string");
|
|
98
|
+
expect(arg.description.length).toBeGreaterThan(0);
|
|
99
|
+
|
|
100
|
+
const lowerDescription: string = arg.description.toLowerCase();
|
|
101
|
+
expect(lowerDescription).toContain("list");
|
|
102
|
+
expect(lowerDescription).toContain("honeycomb");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("dropdown options", () => {
|
|
107
|
+
test("offers exactly the List and Honeycomb options, in that order", () => {
|
|
108
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
109
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
110
|
+
|
|
111
|
+
expect(arg.dropdownOptions).toEqual([
|
|
112
|
+
{ label: "List", value: "list" },
|
|
113
|
+
{ label: "Honeycomb", value: "honeycomb" },
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("every option value is a valid DashboardListViewMode", () => {
|
|
118
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
119
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
120
|
+
|
|
121
|
+
const options: Array<{ label: unknown; value: unknown }> =
|
|
122
|
+
(arg.dropdownOptions ?? []) as Array<{
|
|
123
|
+
label: unknown;
|
|
124
|
+
value: unknown;
|
|
125
|
+
}>;
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* Guard against a vacuous pass: there must actually be options to check.
|
|
129
|
+
*/
|
|
130
|
+
expect(options.length).toBeGreaterThan(0);
|
|
131
|
+
|
|
132
|
+
for (const option of options) {
|
|
133
|
+
expect(typeof option.label).toBe("string");
|
|
134
|
+
expect((option.label as string).length).toBeGreaterThan(0);
|
|
135
|
+
expect(
|
|
136
|
+
VALID_VIEW_MODES.has(option.value as DashboardListViewMode),
|
|
137
|
+
).toBe(true);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("covers all view modes with no duplicate values", () => {
|
|
142
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
143
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
144
|
+
|
|
145
|
+
const values: Array<string> = (arg.dropdownOptions ?? []).map(
|
|
146
|
+
(option: { value: unknown }): string => {
|
|
147
|
+
return String(option.value);
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
* A duplicate value would make two options indistinguishable to the
|
|
153
|
+
* consumer; a missing one would strand a supported view behind no
|
|
154
|
+
* selectable option.
|
|
155
|
+
*/
|
|
156
|
+
const uniqueValues: Set<string> = new Set<string>(values);
|
|
157
|
+
expect(uniqueValues.size).toBe(values.length);
|
|
158
|
+
expect(uniqueValues).toEqual(
|
|
159
|
+
new Set<string>(Array.from(VALID_VIEW_MODES)),
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("section handling", () => {
|
|
165
|
+
test("attaches the caller's section by reference, unmodified", () => {
|
|
166
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
167
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
168
|
+
|
|
169
|
+
/*
|
|
170
|
+
* The factory must not clone the section — the editor relies on the same
|
|
171
|
+
* section instance being shared across the arguments that belong to it.
|
|
172
|
+
*/
|
|
173
|
+
expect(arg.section).toBe(SAMPLE_SECTION);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("does not mutate a frozen section", () => {
|
|
177
|
+
const frozenSection: ComponentArgumentSection = Object.freeze({
|
|
178
|
+
name: "Layout",
|
|
179
|
+
order: 0,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
/*
|
|
183
|
+
* Passing a frozen section proves the factory never writes back into it;
|
|
184
|
+
* any attempted mutation would throw here in strict mode.
|
|
185
|
+
*/
|
|
186
|
+
expect(() => {
|
|
187
|
+
return getViewModeArgument<DashboardBaseComponent>(frozenSection);
|
|
188
|
+
}).not.toThrow();
|
|
189
|
+
|
|
190
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
191
|
+
getViewModeArgument<DashboardBaseComponent>(frozenSection);
|
|
192
|
+
expect(arg.section).toBe(frozenSection);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test.each([
|
|
196
|
+
["zero order boundary", 0],
|
|
197
|
+
["negative order", -5],
|
|
198
|
+
["large order", 999999],
|
|
199
|
+
])(
|
|
200
|
+
"passes through a minimal section with %s",
|
|
201
|
+
(_label: string, order: number) => {
|
|
202
|
+
const minimalSection: ComponentArgumentSection = {
|
|
203
|
+
name: "",
|
|
204
|
+
order: order,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const arg: ComponentArgument<DashboardBaseComponent> =
|
|
208
|
+
getViewModeArgument<DashboardBaseComponent>(minimalSection);
|
|
209
|
+
|
|
210
|
+
/*
|
|
211
|
+
* The factory does not inspect or normalize the section, so even an
|
|
212
|
+
* empty name or an out-of-range order is forwarded verbatim.
|
|
213
|
+
*/
|
|
214
|
+
expect(arg.section).toBe(minimalSection);
|
|
215
|
+
expect(arg.section?.order).toBe(order);
|
|
216
|
+
expect(arg.section?.name).toBe("");
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("purity and independence", () => {
|
|
222
|
+
test("is deterministic for the same section", () => {
|
|
223
|
+
const first: ComponentArgument<DashboardBaseComponent> =
|
|
224
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
225
|
+
const second: ComponentArgument<DashboardBaseComponent> =
|
|
226
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
227
|
+
|
|
228
|
+
expect(second).toEqual(first);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("returns a fresh argument object and options array each call", () => {
|
|
232
|
+
const first: ComponentArgument<DashboardBaseComponent> =
|
|
233
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
234
|
+
const second: ComponentArgument<DashboardBaseComponent> =
|
|
235
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
236
|
+
|
|
237
|
+
/*
|
|
238
|
+
* Each rendering must get its own object and its own options array, so
|
|
239
|
+
* mutating one form's arguments cannot corrupt another's.
|
|
240
|
+
*/
|
|
241
|
+
expect(second).not.toBe(first);
|
|
242
|
+
expect(second.dropdownOptions).not.toBe(first.dropdownOptions);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("mutating a returned argument does not affect later calls", () => {
|
|
246
|
+
const first: ComponentArgument<DashboardBaseComponent> =
|
|
247
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
248
|
+
|
|
249
|
+
first.name = "Mutated";
|
|
250
|
+
first.dropdownOptions?.push({ label: "Injected", value: "list" });
|
|
251
|
+
|
|
252
|
+
const second: ComponentArgument<DashboardBaseComponent> =
|
|
253
|
+
getViewModeArgument<DashboardBaseComponent>(SAMPLE_SECTION);
|
|
254
|
+
|
|
255
|
+
expect(second.name).toBe("View Mode");
|
|
256
|
+
expect(second.dropdownOptions).toHaveLength(2);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
});
|