@oneuptime/common 11.7.3 → 11.7.4
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/AlertEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Index.ts +5 -0
- package/Models/DatabaseModels/LogDropFilter.ts +89 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
- package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
- package/Server/API/AlertAPI.ts +32 -17
- package/Server/API/BaseAPI.ts +31 -2
- package/Server/API/BaseAnalyticsAPI.ts +36 -2
- package/Server/API/CommonAPI.ts +57 -0
- package/Server/API/IncidentAPI.ts +60 -33
- package/Server/API/IncidentEpisodeAPI.ts +31 -16
- package/Server/API/MonitorAPI.ts +30 -0
- package/Server/API/MonitorTemplateAPI.ts +8 -0
- package/Server/API/ProbeAPI.ts +196 -15
- package/Server/API/ProjectAPI.ts +79 -1
- package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
- package/Server/API/StatusPageAPI.ts +74 -37
- package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Services/AlertEpisodeService.ts +47 -0
- package/Server/Services/AlertService.ts +125 -0
- package/Server/Services/BillingService.ts +98 -0
- package/Server/Services/DatabaseService.ts +184 -20
- package/Server/Services/IncidentEpisodeService.ts +47 -0
- package/Server/Services/IncidentService.ts +123 -0
- package/Server/Services/IncidentTemplateService.ts +123 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/LogDropFilterService.ts +133 -0
- package/Server/Services/MonitorProbeService.ts +94 -4
- package/Server/Services/MonitorService.ts +48 -43
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
- package/Server/Services/ProbeService.ts +66 -0
- package/Server/Services/ProjectService.ts +131 -7
- package/Server/Services/RecommendationDismissalService.ts +52 -0
- package/Server/Services/RunbookAgentJobService.ts +68 -7
- package/Server/Services/ScheduledMaintenanceService.ts +103 -0
- package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
- package/Server/Services/StatusPageGroupService.ts +186 -0
- package/Server/Services/TraceDropFilterService.ts +133 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
- package/Server/Types/Workflow/Components/API/Get.ts +12 -0
- package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
- package/Server/Types/Workflow/Components/API/Post.ts +12 -0
- package/Server/Types/Workflow/Components/API/Put.ts +13 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
- package/Server/Utils/DropFilterValidation.ts +127 -0
- package/Server/Utils/Errors.ts +5 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
- package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
- package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
- package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
- package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
- package/Tests/Server/API/ProbeAPI.test.ts +422 -53
- package/Tests/Server/API/ProjectAPI.test.ts +180 -0
- package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
- package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
- package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
- package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
- package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
- package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
- package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
- package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
- package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
- package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
- package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
- package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
- package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
- package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
- package/Tests/Types/DateUserTimezone.test.ts +52 -0
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
- package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
- package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
- package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
- package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
- package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
- package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
- package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
- package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
- package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
- package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
- package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
- package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
- package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
- package/Tests/UI/Components/InfoCard.test.tsx +358 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
- package/Tests/UI/Components/Modal.test.tsx +43 -5
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +427 -0
- package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
- package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
- package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
- package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
- package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
- package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
- package/Types/Date.ts +15 -6
- package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
- package/Types/Monitor/MonitorStep.ts +10 -1
- package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
- package/Types/Permission.ts +45 -0
- package/Types/Recommendation/RecommendationType.ts +20 -0
- package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
- package/Types/Runbook/RunbookStepTimeout.ts +202 -0
- package/Types/Telemetry/DropFilterSampling.ts +92 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
- package/UI/Components/Accordion/Accordion.tsx +10 -2
- package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
- package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
- package/UI/Components/Error/PageError.tsx +0 -4
- package/UI/Components/ErrorBoundary.tsx +252 -15
- package/UI/Components/InfoCard/InfoCard.tsx +58 -1
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
- package/UI/Components/Modal/Modal.tsx +16 -60
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
- package/UI/Components/ModelTable/BaseModelTable.tsx +116 -67
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +150 -47
- package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
- package/UI/Utils/DropdownAlignment.ts +162 -0
- package/UI/Utils/TableFilterUrlState.ts +59 -20
- package/Utils/API.ts +106 -2
- package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
- package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
- package/Utils/StatusPage/GroupTree.ts +307 -0
- package/Utils/StatusPage/ResourceUptime.ts +90 -17
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +22 -13
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +22 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +42 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +43 -26
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorAPI.js +23 -0
- package/build/dist/Server/API/MonitorAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
- package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +131 -15
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +62 -3
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +61 -33
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +95 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +71 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +140 -17
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +100 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogDropFilterService.js +93 -0
- package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +73 -5
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +34 -39
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +64 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +103 -7
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
- package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
- package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
- package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
- package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
- package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
- package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
- package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
- package/build/dist/Server/Utils/Errors.js +3 -0
- package/build/dist/Server/Utils/Errors.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
- package/build/dist/Types/Date.js +14 -4
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +6 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
- package/build/dist/Types/Permission.js +40 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
- package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
- package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Error/PageError.js +1 -3
- package/build/dist/UI/Components/Error/PageError.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +145 -11
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +4 -32
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +69 -33
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +73 -32
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
- package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
- package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
- package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
- package/build/dist/Utils/API.js +39 -2
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
- package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,50 +1,581 @@
|
|
|
1
|
-
import ErrorBoundary
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import ErrorBoundary, {
|
|
2
|
+
CHUNK_LOAD_RELOAD_COOLDOWN_IN_MS,
|
|
3
|
+
CHUNK_LOAD_RELOAD_STORAGE_KEY,
|
|
4
|
+
handleChunkLoadError,
|
|
5
|
+
hasRecentlyReloadedForChunkLoadError,
|
|
6
|
+
isChunkLoadError,
|
|
7
|
+
markChunkLoadErrorReload,
|
|
8
|
+
} from "../../../UI/Components/ErrorBoundary";
|
|
9
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
10
|
+
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The blank white screen users hit is React unmounting the entire tree when a
|
|
14
|
+
* render throws and nothing catches it. These tests pin down the containment
|
|
15
|
+
* contract: a throwing subtree must degrade to a readable, recoverable fallback
|
|
16
|
+
* while everything around it keeps rendering — never to an empty document.
|
|
17
|
+
*
|
|
18
|
+
* Assertions here deliberately use plain DOM checks rather than jest-dom
|
|
19
|
+
* matchers (toBeInTheDocument etc). Common's tsconfig does not list
|
|
20
|
+
* testing-library__jest-dom in "types", so those matchers do not type-check.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const ORIGINAL_LOCATION: Location = window.location;
|
|
24
|
+
|
|
25
|
+
let reloadMock: jest.Mock;
|
|
26
|
+
|
|
27
|
+
function mockReload(): void {
|
|
28
|
+
reloadMock = jest.fn();
|
|
29
|
+
|
|
30
|
+
Object.defineProperty(window, "location", {
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: { ...ORIGINAL_LOCATION, reload: reloadMock },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function restoreLocation(): void {
|
|
38
|
+
Object.defineProperty(window, "location", {
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: ORIGINAL_LOCATION,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Throws while rendering — the failure mode that blanks the screen. */
|
|
46
|
+
const ThrowOnRender: FunctionComponent<{ message?: string }> = (props: {
|
|
47
|
+
message?: string;
|
|
48
|
+
}): ReactElement => {
|
|
49
|
+
throw new Error(props.message || "Render exploded");
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Throws from an effect, after the first paint. */
|
|
53
|
+
const ThrowInEffect: FunctionComponent = (): ReactElement => {
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
throw new Error("Effect exploded");
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
return <div>Effect Throwing Component</div>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const FALLBACK_TEXT: string =
|
|
62
|
+
"An unexpected error has occurred. Please reload the page to continue";
|
|
5
63
|
|
|
6
64
|
describe("ErrorBoundary", () => {
|
|
7
|
-
const
|
|
65
|
+
const consoleErrorSpy: jest.SpyInstance = jest.spyOn(console, "error");
|
|
8
66
|
|
|
9
67
|
beforeAll(() => {
|
|
10
|
-
|
|
68
|
+
consoleErrorSpy.mockImplementation(() => {});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
consoleErrorSpy.mockClear();
|
|
73
|
+
window.sessionStorage.clear();
|
|
74
|
+
mockReload();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
restoreLocation();
|
|
11
79
|
});
|
|
12
80
|
|
|
13
81
|
afterAll(() => {
|
|
14
|
-
|
|
82
|
+
consoleErrorSpy.mockRestore();
|
|
15
83
|
});
|
|
16
84
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
85
|
+
describe("happy path", () => {
|
|
86
|
+
test("renders children when no error is thrown", () => {
|
|
87
|
+
render(
|
|
88
|
+
<ErrorBoundary>
|
|
89
|
+
<div>Child Component</div>
|
|
90
|
+
</ErrorBoundary>,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(screen.queryByText("Child Component")).not.toBeNull();
|
|
94
|
+
expect(screen.queryByTestId("error-boundary-fallback")).toBeNull();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("renders multiple children", () => {
|
|
98
|
+
render(
|
|
99
|
+
<ErrorBoundary>
|
|
100
|
+
<div>First</div>
|
|
101
|
+
<div>Second</div>
|
|
102
|
+
</ErrorBoundary>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(screen.queryByText("First")).not.toBeNull();
|
|
106
|
+
expect(screen.queryByText("Second")).not.toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("does not reload the page when nothing goes wrong", () => {
|
|
110
|
+
render(
|
|
111
|
+
<ErrorBoundary>
|
|
112
|
+
<div>Child Component</div>
|
|
113
|
+
</ErrorBoundary>,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(reloadMock).not.toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("containment", () => {
|
|
121
|
+
test("renders the fallback when a child throws during render", () => {
|
|
122
|
+
render(
|
|
123
|
+
<ErrorBoundary>
|
|
124
|
+
<ThrowOnRender />
|
|
125
|
+
</ErrorBoundary>,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
129
|
+
expect(screen.queryByTestId("error-boundary-fallback")).not.toBeNull();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("renders the fallback when a child throws from an effect", () => {
|
|
133
|
+
render(
|
|
134
|
+
<ErrorBoundary>
|
|
135
|
+
<ThrowInEffect />
|
|
136
|
+
</ErrorBoundary>,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("THE BLANK SCREEN: the document is never left empty", () => {
|
|
143
|
+
const { container } = render(
|
|
144
|
+
<ErrorBoundary>
|
|
145
|
+
<ThrowOnRender />
|
|
146
|
+
</ErrorBoundary>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* Before the boundary was wired up this subtree rendered to nothing at
|
|
151
|
+
* all — the whole app painted white, with no text and no way back.
|
|
152
|
+
*/
|
|
153
|
+
expect(container.textContent!.trim().length).toBeGreaterThan(0);
|
|
154
|
+
expect(container.querySelectorAll("button").length).toBeGreaterThan(0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("siblings outside the boundary keep rendering", () => {
|
|
158
|
+
render(
|
|
159
|
+
<div>
|
|
160
|
+
<div>Navigation Bar</div>
|
|
161
|
+
<ErrorBoundary>
|
|
162
|
+
<ThrowOnRender />
|
|
163
|
+
</ErrorBoundary>
|
|
164
|
+
<div>Footer</div>
|
|
165
|
+
</div>,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// One broken page must not take the shell down with it.
|
|
169
|
+
expect(screen.queryByText("Navigation Bar")).not.toBeNull();
|
|
170
|
+
expect(screen.queryByText("Footer")).not.toBeNull();
|
|
171
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("an inner boundary keeps the error away from an outer one", () => {
|
|
175
|
+
render(
|
|
176
|
+
<ErrorBoundary>
|
|
177
|
+
<div>
|
|
178
|
+
<div>Outer Content</div>
|
|
179
|
+
<ErrorBoundary>
|
|
180
|
+
<ThrowOnRender />
|
|
181
|
+
</ErrorBoundary>
|
|
182
|
+
</div>
|
|
183
|
+
</ErrorBoundary>,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(screen.queryByText("Outer Content")).not.toBeNull();
|
|
187
|
+
expect(screen.getAllByTestId("error-boundary-fallback")).toHaveLength(1);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("the fallback is announced to assistive technology", () => {
|
|
191
|
+
render(
|
|
192
|
+
<ErrorBoundary>
|
|
193
|
+
<ThrowOnRender />
|
|
194
|
+
</ErrorBoundary>,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(screen.queryByRole("alert")).not.toBeNull();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("shows the underlying error message for support", () => {
|
|
201
|
+
render(
|
|
202
|
+
<ErrorBoundary>
|
|
203
|
+
<ThrowOnRender message="Cannot read properties of undefined (reading 'queryConfigs')" />
|
|
204
|
+
</ErrorBoundary>,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(
|
|
208
|
+
screen.getByTestId("error-boundary-error-message").textContent,
|
|
209
|
+
).toContain("reading 'queryConfigs'");
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("reporting", () => {
|
|
214
|
+
test("calls the onError callback with the thrown error", () => {
|
|
215
|
+
const onError: jest.Mock = jest.fn();
|
|
216
|
+
|
|
217
|
+
render(
|
|
218
|
+
<ErrorBoundary onError={onError}>
|
|
219
|
+
<ThrowOnRender message="Boom" />
|
|
220
|
+
</ErrorBoundary>,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
224
|
+
expect((onError.mock.calls[0]![0] as Error).message).toBe("Boom");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("logs the error so it still reaches the console and RUM", () => {
|
|
228
|
+
render(
|
|
229
|
+
<ErrorBoundary>
|
|
230
|
+
<ThrowOnRender message="Boom" />
|
|
231
|
+
</ErrorBoundary>,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const loggedOurMessage: boolean = consoleErrorSpy.mock.calls.some(
|
|
235
|
+
(call: Array<unknown>) => {
|
|
236
|
+
return call[0] === "Uncaught error rendering the app:";
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
expect(loggedOurMessage).toBe(true);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("does not call onError when nothing throws", () => {
|
|
244
|
+
const onError: jest.Mock = jest.fn();
|
|
245
|
+
|
|
246
|
+
render(
|
|
247
|
+
<ErrorBoundary onError={onError}>
|
|
248
|
+
<div>Fine</div>
|
|
249
|
+
</ErrorBoundary>,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
expect(onError).not.toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("recovery", () => {
|
|
257
|
+
test("'Try again' re-renders the children", () => {
|
|
258
|
+
let shouldThrow: boolean = true;
|
|
259
|
+
|
|
260
|
+
const SometimesThrows: FunctionComponent = (): ReactElement => {
|
|
261
|
+
if (shouldThrow) {
|
|
262
|
+
throw new Error("Transient failure");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return <div>Recovered Content</div>;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
render(
|
|
269
|
+
<ErrorBoundary>
|
|
270
|
+
<SometimesThrows />
|
|
271
|
+
</ErrorBoundary>,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
275
|
+
|
|
276
|
+
shouldThrow = false;
|
|
277
|
+
fireEvent.click(screen.getByTestId("error-boundary-try-again"));
|
|
278
|
+
|
|
279
|
+
expect(screen.queryByText("Recovered Content")).not.toBeNull();
|
|
280
|
+
expect(screen.queryByTestId("error-boundary-fallback")).toBeNull();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("'Reload page' reloads the browser", () => {
|
|
284
|
+
render(
|
|
285
|
+
<ErrorBoundary>
|
|
286
|
+
<ThrowOnRender />
|
|
287
|
+
</ErrorBoundary>,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
fireEvent.click(screen.getByTestId("error-boundary-reload"));
|
|
291
|
+
|
|
292
|
+
expect(reloadMock).toHaveBeenCalledTimes(1);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("changing the resetKey clears the error (navigating away)", () => {
|
|
296
|
+
const { rerender } = render(
|
|
297
|
+
<ErrorBoundary resetKey="/dashboard/monitors/broken">
|
|
298
|
+
<ThrowOnRender />
|
|
299
|
+
</ErrorBoundary>,
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
303
|
+
|
|
304
|
+
// The user clicks another nav item; the route key changes.
|
|
305
|
+
rerender(
|
|
306
|
+
<ErrorBoundary resetKey="/dashboard/incidents">
|
|
307
|
+
<div>Incidents Page</div>
|
|
308
|
+
</ErrorBoundary>,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
expect(screen.queryByText("Incidents Page")).not.toBeNull();
|
|
312
|
+
expect(screen.queryByTestId("error-boundary-fallback")).toBeNull();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("keeping the same resetKey keeps showing the fallback", () => {
|
|
316
|
+
const { rerender } = render(
|
|
317
|
+
<ErrorBoundary resetKey="/dashboard/monitors/broken">
|
|
318
|
+
<ThrowOnRender />
|
|
319
|
+
</ErrorBoundary>,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
rerender(
|
|
323
|
+
<ErrorBoundary resetKey="/dashboard/monitors/broken">
|
|
324
|
+
<div>Should Not Show</div>
|
|
325
|
+
</ErrorBoundary>,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
329
|
+
expect(screen.queryByText("Should Not Show")).toBeNull();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("catches a second error after recovering from the first", () => {
|
|
333
|
+
const MaybeThrows: FunctionComponent<{ shouldThrow: boolean }> = (props: {
|
|
334
|
+
shouldThrow: boolean;
|
|
335
|
+
}): ReactElement => {
|
|
336
|
+
if (props.shouldThrow) {
|
|
337
|
+
throw new Error("Transient failure");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return <div>Recovered Content</div>;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const { rerender } = render(
|
|
344
|
+
<ErrorBoundary>
|
|
345
|
+
<MaybeThrows shouldThrow={true} />
|
|
346
|
+
</ErrorBoundary>,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
350
|
+
|
|
351
|
+
// The underlying cause clears, and the user retries.
|
|
352
|
+
rerender(
|
|
353
|
+
<ErrorBoundary>
|
|
354
|
+
<MaybeThrows shouldThrow={false} />
|
|
355
|
+
</ErrorBoundary>,
|
|
356
|
+
);
|
|
357
|
+
fireEvent.click(screen.getByTestId("error-boundary-try-again"));
|
|
358
|
+
expect(screen.queryByText("Recovered Content")).not.toBeNull();
|
|
359
|
+
|
|
360
|
+
// It breaks again. A boundary that only catches once is a blank screen.
|
|
361
|
+
rerender(
|
|
362
|
+
<ErrorBoundary>
|
|
363
|
+
<MaybeThrows shouldThrow={true} />
|
|
364
|
+
</ErrorBoundary>,
|
|
365
|
+
);
|
|
366
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe("isChunkLoadError", () => {
|
|
372
|
+
test.each([
|
|
373
|
+
["webpack", "Loading chunk 472 failed."],
|
|
374
|
+
["webpack css", "Loading CSS chunk 12 failed."],
|
|
375
|
+
["vite", "Failed to fetch dynamically imported module: /assets/App.js"],
|
|
376
|
+
["safari", "Importing a module script failed."],
|
|
377
|
+
["firefox", "error loading dynamically imported module"],
|
|
378
|
+
["vite preload", "Unable to preload CSS for /assets/index.css"],
|
|
379
|
+
])("detects the %s wording", (_browser: string, message: string) => {
|
|
380
|
+
expect(isChunkLoadError(new Error(message))).toBe(true);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("detects errors identified only by name", () => {
|
|
384
|
+
const error: Error = new Error("Something went wrong");
|
|
385
|
+
error.name = "ChunkLoadError";
|
|
386
|
+
|
|
387
|
+
expect(isChunkLoadError(error)).toBe(true);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test.each([
|
|
391
|
+
["the blank-screen TypeError", "Cannot read properties of undefined"],
|
|
392
|
+
["a network failure", "Network request failed"],
|
|
393
|
+
["an empty message", ""],
|
|
394
|
+
])("does not misfire on %s", (_label: string, message: string) => {
|
|
395
|
+
expect(isChunkLoadError(new Error(message))).toBe(false);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test.each([
|
|
399
|
+
["null", null],
|
|
400
|
+
["undefined", undefined],
|
|
401
|
+
["a number", 0],
|
|
402
|
+
["an empty string", ""],
|
|
403
|
+
])("is false for %s", (_label: string, value: unknown) => {
|
|
404
|
+
expect(isChunkLoadError(value)).toBe(false);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test("handles non-Error throwables without blowing up", () => {
|
|
408
|
+
expect(isChunkLoadError("Loading chunk 3 failed.")).toBe(true);
|
|
409
|
+
expect(isChunkLoadError({ nope: true })).toBe(false);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
describe("chunk load reload throttling", () => {
|
|
414
|
+
beforeEach(() => {
|
|
415
|
+
window.sessionStorage.clear();
|
|
416
|
+
mockReload();
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
afterEach(() => {
|
|
420
|
+
restoreLocation();
|
|
421
|
+
jest.restoreAllMocks();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("has not reloaded when storage is empty", () => {
|
|
425
|
+
expect(hasRecentlyReloadedForChunkLoadError(Date.now())).toBe(false);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test("reports a reload inside the cooldown window", () => {
|
|
429
|
+
const now: number = 1_000_000;
|
|
430
|
+
markChunkLoadErrorReload(now);
|
|
431
|
+
|
|
432
|
+
expect(hasRecentlyReloadedForChunkLoadError(now)).toBe(true);
|
|
433
|
+
expect(
|
|
434
|
+
hasRecentlyReloadedForChunkLoadError(
|
|
435
|
+
now + CHUNK_LOAD_RELOAD_COOLDOWN_IN_MS - 1,
|
|
436
|
+
),
|
|
437
|
+
).toBe(true);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test("the cooldown expires", () => {
|
|
441
|
+
const now: number = 1_000_000;
|
|
442
|
+
markChunkLoadErrorReload(now);
|
|
443
|
+
|
|
444
|
+
expect(
|
|
445
|
+
hasRecentlyReloadedForChunkLoadError(
|
|
446
|
+
now + CHUNK_LOAD_RELOAD_COOLDOWN_IN_MS,
|
|
447
|
+
),
|
|
448
|
+
).toBe(false);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test("ignores a corrupt timestamp", () => {
|
|
452
|
+
window.sessionStorage.setItem(
|
|
453
|
+
CHUNK_LOAD_RELOAD_STORAGE_KEY,
|
|
454
|
+
"not-a-number",
|
|
22
455
|
);
|
|
23
456
|
|
|
24
|
-
|
|
457
|
+
expect(hasRecentlyReloadedForChunkLoadError(Date.now())).toBe(false);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test("survives sessionStorage being unavailable", () => {
|
|
461
|
+
jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
|
|
462
|
+
throw new Error("SecurityError");
|
|
463
|
+
});
|
|
464
|
+
jest.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
|
|
465
|
+
throw new Error("SecurityError");
|
|
466
|
+
});
|
|
25
467
|
|
|
26
|
-
expect(
|
|
468
|
+
expect(() => {
|
|
469
|
+
markChunkLoadErrorReload(Date.now());
|
|
470
|
+
}).not.toThrow();
|
|
471
|
+
expect(hasRecentlyReloadedForChunkLoadError(Date.now())).toBe(false);
|
|
27
472
|
});
|
|
473
|
+
});
|
|
28
474
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
475
|
+
describe("handleChunkLoadError", () => {
|
|
476
|
+
beforeEach(() => {
|
|
477
|
+
window.sessionStorage.clear();
|
|
478
|
+
mockReload();
|
|
479
|
+
});
|
|
34
480
|
|
|
35
|
-
|
|
481
|
+
afterEach(() => {
|
|
482
|
+
restoreLocation();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
test("reloads once for a stale bundle", () => {
|
|
486
|
+
expect(handleChunkLoadError(new Error("Loading chunk 5 failed."))).toBe(
|
|
487
|
+
true,
|
|
488
|
+
);
|
|
489
|
+
expect(reloadMock).toHaveBeenCalledTimes(1);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("NO RELOAD LOOP: does not reload again inside the cooldown", () => {
|
|
493
|
+
handleChunkLoadError(new Error("Loading chunk 5 failed."));
|
|
494
|
+
expect(reloadMock).toHaveBeenCalledTimes(1);
|
|
495
|
+
|
|
496
|
+
/*
|
|
497
|
+
* If the asset is genuinely gone the reload will not fix it. Reloading
|
|
498
|
+
* again would spin forever, so the second attempt must fall through to the
|
|
499
|
+
* fallback UI instead.
|
|
500
|
+
*/
|
|
501
|
+
expect(handleChunkLoadError(new Error("Loading chunk 5 failed."))).toBe(
|
|
502
|
+
false,
|
|
503
|
+
);
|
|
504
|
+
expect(reloadMock).toHaveBeenCalledTimes(1);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test("never reloads for an ordinary render error", () => {
|
|
508
|
+
expect(
|
|
509
|
+
handleChunkLoadError(
|
|
510
|
+
new Error(
|
|
511
|
+
"Cannot read properties of undefined (reading 'queryConfigs')",
|
|
512
|
+
),
|
|
513
|
+
),
|
|
514
|
+
).toBe(false);
|
|
515
|
+
expect(reloadMock).not.toHaveBeenCalled();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("records the attempt so a later boundary sees it", () => {
|
|
519
|
+
handleChunkLoadError(new Error("Loading chunk 5 failed."));
|
|
520
|
+
|
|
521
|
+
expect(
|
|
522
|
+
window.sessionStorage.getItem(CHUNK_LOAD_RELOAD_STORAGE_KEY),
|
|
523
|
+
).not.toBeNull();
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
describe("ErrorBoundary chunk load recovery", () => {
|
|
528
|
+
const consoleErrorSpy: jest.SpyInstance = jest.spyOn(console, "error");
|
|
529
|
+
|
|
530
|
+
beforeAll(() => {
|
|
531
|
+
consoleErrorSpy.mockImplementation(() => {});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
beforeEach(() => {
|
|
535
|
+
window.sessionStorage.clear();
|
|
536
|
+
mockReload();
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
afterEach(() => {
|
|
540
|
+
restoreLocation();
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
afterAll(() => {
|
|
544
|
+
consoleErrorSpy.mockRestore();
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test("auto-reloads when a lazy route fails to load", () => {
|
|
548
|
+
const ThrowChunkError: FunctionComponent = (): ReactElement => {
|
|
549
|
+
throw new Error(
|
|
550
|
+
"Failed to fetch dynamically imported module: /assets/AllRoutes.js",
|
|
551
|
+
);
|
|
36
552
|
};
|
|
37
553
|
|
|
38
554
|
render(
|
|
39
555
|
<ErrorBoundary>
|
|
40
|
-
<
|
|
556
|
+
<ThrowChunkError />
|
|
41
557
|
</ErrorBoundary>,
|
|
42
558
|
);
|
|
43
559
|
|
|
44
|
-
|
|
45
|
-
|
|
560
|
+
expect(reloadMock).toHaveBeenCalledTimes(1);
|
|
561
|
+
// The fallback still renders so the user is never looking at a blank page.
|
|
562
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test("shows the fallback without reloading when a reload already happened", () => {
|
|
566
|
+
markChunkLoadErrorReload(Date.now());
|
|
567
|
+
|
|
568
|
+
const ThrowChunkError: FunctionComponent = (): ReactElement => {
|
|
569
|
+
throw new Error("Loading chunk 9 failed.");
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
render(
|
|
573
|
+
<ErrorBoundary>
|
|
574
|
+
<ThrowChunkError />
|
|
575
|
+
</ErrorBoundary>,
|
|
46
576
|
);
|
|
47
577
|
|
|
48
|
-
expect(
|
|
578
|
+
expect(reloadMock).not.toHaveBeenCalled();
|
|
579
|
+
expect(screen.queryByText(FALLBACK_TEXT)).not.toBeNull();
|
|
49
580
|
});
|
|
50
581
|
});
|