@oneuptime/common 12.0.5 → 12.0.6
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/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/StatusPageAPI.ts +11 -1
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +49 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +446 -0
- package/Server/Utils/DataSource/HttpFetch.ts +221 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +14 -0
- package/Server/Utils/Response.ts +12 -1
- package/Server/Utils/SSRFProtection.ts +237 -0
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
- package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +6 -1
- package/UI/Components/Modal/Modal.tsx +267 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +11 -1
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.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/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.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/MonitorService.js +45 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +9 -1
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +192 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +178 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +5 -1
- package/tsconfig.json +12 -10
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, RenderResult, screen, within } from "@testing-library/react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { describe, expect, test } from "@jest/globals";
|
|
5
|
+
import ValueWidgetView, {
|
|
6
|
+
ValueWidgetViewProps,
|
|
7
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/Dashboard/Components/ValueWidgetView";
|
|
8
|
+
import GaugeWidgetView, {
|
|
9
|
+
GaugeWidgetViewProps,
|
|
10
|
+
splitFormattedDisplay,
|
|
11
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/Dashboard/Components/GaugeWidgetView";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* ValueWidgetView and GaugeWidgetView are the only two presentational surfaces
|
|
15
|
+
* shared by the OneUptime metric widgets (DashboardValueComponent /
|
|
16
|
+
* DashboardGaugeComponent) and the new external Data Source widgets
|
|
17
|
+
* (DashboardDataSourceValueComponent / DashboardDataSourceGaugeComponent).
|
|
18
|
+
* Four widget types, two renderers — a regression here is a regression on
|
|
19
|
+
* every board at once, and the two families silently drift apart the moment
|
|
20
|
+
* one of them starts rendering a state the other does not.
|
|
21
|
+
*
|
|
22
|
+
* The state machine is the part worth pinning. Both views are handed a
|
|
23
|
+
* `value: number | null`, and null exists precisely so an empty result cannot
|
|
24
|
+
* be drawn as a confident "0": on the RUM template that made an untouched
|
|
25
|
+
* dashboard read as a flawless site ("AVG LCP 0"). A test that only checked
|
|
26
|
+
* "the no-data message appears" would still pass if a 0 were rendered beside
|
|
27
|
+
* it, so the assertions below check for the ABSENCE of the number too.
|
|
28
|
+
*
|
|
29
|
+
* Everything either view needs arrives as a prop — no fetching, no context —
|
|
30
|
+
* so these render straight into jsdom with nothing mocked.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const VALUE_SETUP_TITLE: string = "Value";
|
|
34
|
+
const VALUE_SETUP_MESSAGE: string = "Click to configure this widget";
|
|
35
|
+
const NO_DATA_MESSAGE: string = "No data for this query";
|
|
36
|
+
|
|
37
|
+
const baseValueProps: ValueWidgetViewProps = {
|
|
38
|
+
widthInPx: 400,
|
|
39
|
+
heightInPx: 200,
|
|
40
|
+
isEditMode: false,
|
|
41
|
+
isLoading: false,
|
|
42
|
+
hasEverLoaded: true,
|
|
43
|
+
error: null,
|
|
44
|
+
value: 42,
|
|
45
|
+
points: [],
|
|
46
|
+
noDataMessage: NO_DATA_MESSAGE,
|
|
47
|
+
isConfigured: true,
|
|
48
|
+
setupTitle: VALUE_SETUP_TITLE,
|
|
49
|
+
setupMessage: VALUE_SETUP_MESSAGE,
|
|
50
|
+
title: "Requests",
|
|
51
|
+
rawUnit: "",
|
|
52
|
+
metricName: "",
|
|
53
|
+
hideUnit: false,
|
|
54
|
+
warningThreshold: undefined,
|
|
55
|
+
criticalThreshold: undefined,
|
|
56
|
+
trendDirection: undefined,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const GAUGE_SETUP_TITLE: string = "Gauge";
|
|
60
|
+
const GAUGE_SETUP_MESSAGE: string = "Click to configure this gauge";
|
|
61
|
+
|
|
62
|
+
const baseGaugeProps: GaugeWidgetViewProps = {
|
|
63
|
+
widthInPx: 400,
|
|
64
|
+
heightInPx: 260,
|
|
65
|
+
componentId: "gauge-a",
|
|
66
|
+
isLoading: false,
|
|
67
|
+
hasEverLoaded: true,
|
|
68
|
+
error: null,
|
|
69
|
+
value: 42,
|
|
70
|
+
noDataMessage: NO_DATA_MESSAGE,
|
|
71
|
+
isConfigured: true,
|
|
72
|
+
setupTitle: GAUGE_SETUP_TITLE,
|
|
73
|
+
setupMessage: GAUGE_SETUP_MESSAGE,
|
|
74
|
+
title: "CPU",
|
|
75
|
+
rawUnit: "",
|
|
76
|
+
metricName: "",
|
|
77
|
+
minValue: 0,
|
|
78
|
+
maxValue: 100,
|
|
79
|
+
warningThreshold: undefined,
|
|
80
|
+
criticalThreshold: undefined,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type RenderValueFunction = (
|
|
84
|
+
overrides: Partial<ValueWidgetViewProps>,
|
|
85
|
+
) => HTMLElement;
|
|
86
|
+
|
|
87
|
+
const renderValue: RenderValueFunction = (
|
|
88
|
+
overrides: Partial<ValueWidgetViewProps>,
|
|
89
|
+
): HTMLElement => {
|
|
90
|
+
const result: RenderResult = render(
|
|
91
|
+
<ValueWidgetView {...baseValueProps} {...overrides} />,
|
|
92
|
+
);
|
|
93
|
+
return result.container;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
type RenderGaugeFunction = (
|
|
97
|
+
overrides: Partial<GaugeWidgetViewProps>,
|
|
98
|
+
) => HTMLElement;
|
|
99
|
+
|
|
100
|
+
const renderGauge: RenderGaugeFunction = (
|
|
101
|
+
overrides: Partial<GaugeWidgetViewProps>,
|
|
102
|
+
): HTMLElement => {
|
|
103
|
+
const result: RenderResult = render(
|
|
104
|
+
<GaugeWidgetView {...baseGaugeProps} {...overrides} />,
|
|
105
|
+
);
|
|
106
|
+
return result.container;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type GetElementFunction = (container: HTMLElement) => HTMLElement | null;
|
|
110
|
+
|
|
111
|
+
/** The big-number element, or null when the view is in a non-numeric state. */
|
|
112
|
+
const getValueNumber: GetElementFunction = (
|
|
113
|
+
container: HTMLElement,
|
|
114
|
+
): HTMLElement | null => {
|
|
115
|
+
return container.querySelector<HTMLElement>("div.tabular-nums.truncate");
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** The gauge's centre number, or null when the gauge is not drawn. */
|
|
119
|
+
const getGaugeNumber: GetElementFunction = (
|
|
120
|
+
container: HTMLElement,
|
|
121
|
+
): HTMLElement | null => {
|
|
122
|
+
return container.querySelector<HTMLElement>("div.font-bold.tabular-nums");
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type IsSkeletonFunction = (container: HTMLElement) => boolean;
|
|
126
|
+
|
|
127
|
+
const isSkeleton: IsSkeletonFunction = (container: HTMLElement): boolean => {
|
|
128
|
+
return container.querySelector(".animate-pulse") !== null;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
type GetTickLabelsFunction = (container: HTMLElement) => Array<string>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Labels on the gauge's threshold tick marks. These are SVG <title> children
|
|
135
|
+
* of <line>, which getByTitle does not reach (it only looks at `svg > title`).
|
|
136
|
+
*/
|
|
137
|
+
const getTickLabels: GetTickLabelsFunction = (
|
|
138
|
+
container: HTMLElement,
|
|
139
|
+
): Array<string> => {
|
|
140
|
+
return Array.from(container.querySelectorAll("line > title")).map(
|
|
141
|
+
(node: Element): string => {
|
|
142
|
+
return node.textContent || "";
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
describe("ValueWidgetView", () => {
|
|
148
|
+
describe("state machine precedence", () => {
|
|
149
|
+
test("the first-load skeleton outranks every other state", () => {
|
|
150
|
+
const container: HTMLElement = renderValue({
|
|
151
|
+
isLoading: true,
|
|
152
|
+
hasEverLoaded: false,
|
|
153
|
+
error: "Query failed",
|
|
154
|
+
isConfigured: false,
|
|
155
|
+
value: null,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(isSkeleton(container)).toBe(true);
|
|
159
|
+
expect(screen.queryByText("Query failed")).not.toBeInTheDocument();
|
|
160
|
+
expect(screen.queryByText(VALUE_SETUP_MESSAGE)).not.toBeInTheDocument();
|
|
161
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("a reload over existing data skips the skeleton and dims instead", () => {
|
|
165
|
+
const container: HTMLElement = renderValue({
|
|
166
|
+
isLoading: true,
|
|
167
|
+
hasEverLoaded: true,
|
|
168
|
+
value: 42,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(isSkeleton(container)).toBe(false);
|
|
172
|
+
expect(getValueNumber(container)).toHaveTextContent("42");
|
|
173
|
+
expect(container.firstElementChild).toHaveStyle({ opacity: "0.5" });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/*
|
|
177
|
+
* The setup prompt beats a leftover error, not the other way round.
|
|
178
|
+
* Clearing a widget's query is exactly how a user escapes a failing
|
|
179
|
+
* query, and the fetch that would clear the error is the one the
|
|
180
|
+
* missing query stops from running — so an error-first order would pin
|
|
181
|
+
* the dead message on screen with no way back short of a page reload.
|
|
182
|
+
*/
|
|
183
|
+
test("the setup prompt outranks a stale error and the no-data state", () => {
|
|
184
|
+
const container: HTMLElement = renderValue({
|
|
185
|
+
isLoading: true,
|
|
186
|
+
hasEverLoaded: true,
|
|
187
|
+
error: "Query failed",
|
|
188
|
+
isConfigured: false,
|
|
189
|
+
value: null,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(isSkeleton(container)).toBe(false);
|
|
193
|
+
expect(screen.getByText(VALUE_SETUP_MESSAGE)).toBeInTheDocument();
|
|
194
|
+
expect(screen.queryByText("Query failed")).not.toBeInTheDocument();
|
|
195
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("a configured widget's error outranks the no-data state", () => {
|
|
199
|
+
renderValue({
|
|
200
|
+
isLoading: true,
|
|
201
|
+
hasEverLoaded: true,
|
|
202
|
+
error: "Query failed",
|
|
203
|
+
isConfigured: true,
|
|
204
|
+
value: null,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
expect(screen.getByText("Query failed")).toBeInTheDocument();
|
|
208
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("the setup prompt outranks the no-data state", () => {
|
|
212
|
+
renderValue({ error: null, isConfigured: false, value: null });
|
|
213
|
+
|
|
214
|
+
expect(screen.getByText(VALUE_SETUP_MESSAGE)).toBeInTheDocument();
|
|
215
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("a configured widget with a value renders the number", () => {
|
|
219
|
+
const container: HTMLElement = renderValue({ value: 42 });
|
|
220
|
+
|
|
221
|
+
expect(getValueNumber(container)).toHaveTextContent("42");
|
|
222
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
223
|
+
expect(screen.queryByText(VALUE_SETUP_MESSAGE)).not.toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("null value", () => {
|
|
228
|
+
test("renders the title and the no-data message, never a substituted 0", () => {
|
|
229
|
+
const container: HTMLElement = renderValue({
|
|
230
|
+
value: null,
|
|
231
|
+
title: "P95 Latency",
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
expect(screen.getByText("P95 Latency")).toBeInTheDocument();
|
|
235
|
+
expect(screen.getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
|
236
|
+
expect(getValueNumber(container)).toBeNull();
|
|
237
|
+
expect(screen.queryByText("0")).not.toBeInTheDocument();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("stays on the no-data state through an auto-refresh", () => {
|
|
241
|
+
/*
|
|
242
|
+
* No `!isLoading` term in the null branch: adding one would flash the
|
|
243
|
+
* removed "0" back on screen for the duration of every poll of a query
|
|
244
|
+
* that is still empty.
|
|
245
|
+
*/
|
|
246
|
+
const container: HTMLElement = renderValue({
|
|
247
|
+
value: null,
|
|
248
|
+
isLoading: true,
|
|
249
|
+
hasEverLoaded: true,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(screen.getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
|
253
|
+
expect(getValueNumber(container)).toBeNull();
|
|
254
|
+
expect(screen.queryByText("0")).not.toBeInTheDocument();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("a measured zero still renders a zero", () => {
|
|
258
|
+
const container: HTMLElement = renderValue({ value: 0 });
|
|
259
|
+
|
|
260
|
+
expect(getValueNumber(container)).toHaveTextContent("0");
|
|
261
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe("setup prompt", () => {
|
|
266
|
+
test("falls back to setupTitle when the widget has no title of its own", () => {
|
|
267
|
+
const missing: HTMLElement = renderValue({
|
|
268
|
+
isConfigured: false,
|
|
269
|
+
title: undefined,
|
|
270
|
+
});
|
|
271
|
+
expect(within(missing).getByText(VALUE_SETUP_TITLE)).toBeInTheDocument();
|
|
272
|
+
|
|
273
|
+
const blank: HTMLElement = renderValue({
|
|
274
|
+
isConfigured: false,
|
|
275
|
+
title: "",
|
|
276
|
+
});
|
|
277
|
+
expect(within(blank).getByText(VALUE_SETUP_TITLE)).toBeInTheDocument();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("prefers the widget's own title when it has one", () => {
|
|
281
|
+
renderValue({ isConfigured: false, title: "Checkout errors" });
|
|
282
|
+
|
|
283
|
+
expect(screen.getByText("Checkout errors")).toBeInTheDocument();
|
|
284
|
+
expect(screen.queryByText(VALUE_SETUP_TITLE)).not.toBeInTheDocument();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/*
|
|
288
|
+
* A title of spaces is what a user who typed one and deleted it leaves
|
|
289
|
+
* behind. Treated as a real title it renders a blank line where the
|
|
290
|
+
* widget should be naming itself, so the fallback has to survive it.
|
|
291
|
+
*/
|
|
292
|
+
test("falls back to setupTitle for a whitespace-only title", () => {
|
|
293
|
+
renderValue({ isConfigured: false, title: " " });
|
|
294
|
+
|
|
295
|
+
expect(screen.getByText(VALUE_SETUP_TITLE)).toBeInTheDocument();
|
|
296
|
+
expect(screen.getByText(VALUE_SETUP_MESSAGE)).toBeInTheDocument();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe("thresholds", () => {
|
|
301
|
+
const withThresholds: Partial<ValueWidgetViewProps> = {
|
|
302
|
+
warningThreshold: 50,
|
|
303
|
+
criticalThreshold: 80,
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
test("paints the number red at and above the critical threshold", () => {
|
|
307
|
+
expect(
|
|
308
|
+
getValueNumber(renderValue({ ...withThresholds, value: 90 })),
|
|
309
|
+
).toHaveClass("text-red-600");
|
|
310
|
+
expect(
|
|
311
|
+
getValueNumber(renderValue({ ...withThresholds, value: 80 })),
|
|
312
|
+
).toHaveClass("text-red-600");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("paints the number amber at and above the warning threshold", () => {
|
|
316
|
+
expect(
|
|
317
|
+
getValueNumber(renderValue({ ...withThresholds, value: 60 })),
|
|
318
|
+
).toHaveClass("text-amber-600");
|
|
319
|
+
expect(
|
|
320
|
+
getValueNumber(renderValue({ ...withThresholds, value: 50 })),
|
|
321
|
+
).toHaveClass("text-amber-600");
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("leaves the number neutral below both thresholds", () => {
|
|
325
|
+
expect(
|
|
326
|
+
getValueNumber(renderValue({ ...withThresholds, value: 10 })),
|
|
327
|
+
).toHaveClass("text-gray-900");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("stays neutral at any magnitude when no thresholds are configured", () => {
|
|
331
|
+
const container: HTMLElement = renderValue({
|
|
332
|
+
value: 9999,
|
|
333
|
+
warningThreshold: undefined,
|
|
334
|
+
criticalThreshold: undefined,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(getValueNumber(container)).toHaveClass("text-gray-900");
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("compares fraction metrics on the percent scale the user authored", () => {
|
|
341
|
+
/*
|
|
342
|
+
* OTel ratio metrics arrive as [0, 1] with unit "1" while thresholds are
|
|
343
|
+
* typed in as 0-100, so 0.9 has to read as 90 — not as a value sitting
|
|
344
|
+
* far below a warning threshold of 50.
|
|
345
|
+
*/
|
|
346
|
+
const container: HTMLElement = renderValue({
|
|
347
|
+
value: 0.9,
|
|
348
|
+
rawUnit: "1",
|
|
349
|
+
metricName: "system.cpu.utilization",
|
|
350
|
+
warningThreshold: 50,
|
|
351
|
+
criticalThreshold: 80,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(getValueNumber(container)).toHaveClass("text-red-600");
|
|
355
|
+
expect(getValueNumber(container)).toHaveTextContent("90.00%");
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe("units", () => {
|
|
360
|
+
test("renders the unit as a suffix beside the number", () => {
|
|
361
|
+
const container: HTMLElement = renderValue({
|
|
362
|
+
value: 23.5,
|
|
363
|
+
rawUnit: "Cel",
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(getValueNumber(container)?.textContent).toBe("23.5 °C");
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("joins a percent unit with no separating space", () => {
|
|
370
|
+
const container: HTMLElement = renderValue({ value: 42, rawUnit: "%" });
|
|
371
|
+
|
|
372
|
+
expect(getValueNumber(container)?.textContent).toBe("42.00%");
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test("hideUnit drops the suffix and keeps the number", () => {
|
|
376
|
+
const container: HTMLElement = renderValue({
|
|
377
|
+
value: 23.5,
|
|
378
|
+
rawUnit: "Cel",
|
|
379
|
+
hideUnit: true,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
expect(getValueNumber(container)?.textContent).toBe("23.5");
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe("extreme values", () => {
|
|
387
|
+
test("abbreviates negative and very large values instead of overflowing", () => {
|
|
388
|
+
expect(getValueNumber(renderValue({ value: -1234.5 }))).toHaveTextContent(
|
|
389
|
+
"-1.23K",
|
|
390
|
+
);
|
|
391
|
+
expect(
|
|
392
|
+
getValueNumber(renderValue({ value: 987654321 })),
|
|
393
|
+
).toHaveTextContent("987.65M");
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("survives NaN and Infinity, comparing each against the thresholds", () => {
|
|
397
|
+
const notANumber: HTMLElement = renderValue({
|
|
398
|
+
value: Number.NaN,
|
|
399
|
+
warningThreshold: 50,
|
|
400
|
+
criticalThreshold: 80,
|
|
401
|
+
});
|
|
402
|
+
// Every NaN comparison is false, so it can only fall through to neutral.
|
|
403
|
+
expect(getValueNumber(notANumber)).toHaveClass("text-gray-900");
|
|
404
|
+
|
|
405
|
+
const infinite: HTMLElement = renderValue({
|
|
406
|
+
value: Number.POSITIVE_INFINITY,
|
|
407
|
+
warningThreshold: 50,
|
|
408
|
+
criticalThreshold: 80,
|
|
409
|
+
});
|
|
410
|
+
/*
|
|
411
|
+
* Only the threshold classification is asserted: the digits themselves
|
|
412
|
+
* come out of ValueFormatter as "NaN" and "InfinityP" — see SOURCE BUGS
|
|
413
|
+
* FOUND — and pinning those strings here would cement them.
|
|
414
|
+
*/
|
|
415
|
+
expect(getValueNumber(infinite)).toHaveClass("text-red-600");
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("renders a zero-sized tile without throwing", () => {
|
|
419
|
+
const container: HTMLElement = renderValue({
|
|
420
|
+
value: 42,
|
|
421
|
+
widthInPx: 0,
|
|
422
|
+
heightInPx: 0,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
expect(getValueNumber(container)).toHaveTextContent("42");
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
describe("GaugeWidgetView", () => {
|
|
431
|
+
describe("state machine precedence", () => {
|
|
432
|
+
test("the first-load skeleton outranks every other state", () => {
|
|
433
|
+
const container: HTMLElement = renderGauge({
|
|
434
|
+
isLoading: true,
|
|
435
|
+
hasEverLoaded: false,
|
|
436
|
+
error: "Gauge query failed",
|
|
437
|
+
isConfigured: false,
|
|
438
|
+
value: null,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
expect(isSkeleton(container)).toBe(true);
|
|
442
|
+
expect(screen.queryByText("Gauge query failed")).not.toBeInTheDocument();
|
|
443
|
+
expect(screen.queryByText(GAUGE_SETUP_MESSAGE)).not.toBeInTheDocument();
|
|
444
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Same precedence as the Value widget — see the note there.
|
|
448
|
+
test("the setup prompt outranks a stale error and the no-data state", () => {
|
|
449
|
+
renderGauge({
|
|
450
|
+
isLoading: true,
|
|
451
|
+
hasEverLoaded: true,
|
|
452
|
+
error: "Gauge query failed",
|
|
453
|
+
isConfigured: false,
|
|
454
|
+
value: null,
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
expect(screen.getByText(GAUGE_SETUP_MESSAGE)).toBeInTheDocument();
|
|
458
|
+
expect(screen.queryByText("Gauge query failed")).not.toBeInTheDocument();
|
|
459
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test("a configured widget's error outranks the no-data state", () => {
|
|
463
|
+
renderGauge({
|
|
464
|
+
isLoading: false,
|
|
465
|
+
hasEverLoaded: true,
|
|
466
|
+
error: "Gauge query failed",
|
|
467
|
+
isConfigured: true,
|
|
468
|
+
value: null,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
expect(screen.getByText("Gauge query failed")).toBeInTheDocument();
|
|
472
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test("the setup prompt outranks no-data and falls back to setupTitle", () => {
|
|
476
|
+
renderGauge({ isConfigured: false, value: null, title: undefined });
|
|
477
|
+
|
|
478
|
+
expect(screen.getByText(GAUGE_SETUP_TITLE)).toBeInTheDocument();
|
|
479
|
+
expect(screen.getByText(GAUGE_SETUP_MESSAGE)).toBeInTheDocument();
|
|
480
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test("a settled null renders the no-data state, never a 0 / Healthy gauge", () => {
|
|
484
|
+
const container: HTMLElement = renderGauge({
|
|
485
|
+
value: null,
|
|
486
|
+
isLoading: false,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
expect(screen.getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
|
|
490
|
+
expect(getGaugeNumber(container)).toBeNull();
|
|
491
|
+
expect(screen.queryByText("Healthy")).not.toBeInTheDocument();
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("a null mid-refresh falls through to a dimmed arc rather than flickering", () => {
|
|
495
|
+
/*
|
|
496
|
+
* Deliberately different from ValueWidgetView: the gauge keeps drawing
|
|
497
|
+
* its arc while a refresh over an empty result is in flight, so an
|
|
498
|
+
* auto-refreshing board does not blink between the arc and a message.
|
|
499
|
+
*/
|
|
500
|
+
const container: HTMLElement = renderGauge({
|
|
501
|
+
value: null,
|
|
502
|
+
isLoading: true,
|
|
503
|
+
hasEverLoaded: true,
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
expect(screen.queryByText(NO_DATA_MESSAGE)).not.toBeInTheDocument();
|
|
507
|
+
expect(getGaugeNumber(container)).toHaveTextContent("0");
|
|
508
|
+
expect(screen.getByText("Healthy")).toBeInTheDocument();
|
|
509
|
+
expect(container.firstElementChild).toHaveStyle({ opacity: "0.5" });
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
describe("status label", () => {
|
|
514
|
+
const withThresholds: Partial<GaugeWidgetViewProps> = {
|
|
515
|
+
warningThreshold: 50,
|
|
516
|
+
criticalThreshold: 80,
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
test("reads Healthy below both thresholds and with none configured", () => {
|
|
520
|
+
const below: HTMLElement = renderGauge({ ...withThresholds, value: 10 });
|
|
521
|
+
expect(within(below).getByText("Healthy")).toBeInTheDocument();
|
|
522
|
+
|
|
523
|
+
const unbounded: HTMLElement = renderGauge({
|
|
524
|
+
value: 99999,
|
|
525
|
+
warningThreshold: undefined,
|
|
526
|
+
criticalThreshold: undefined,
|
|
527
|
+
});
|
|
528
|
+
expect(within(unbounded).getByText("Healthy")).toBeInTheDocument();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("reads Warning at the warning threshold", () => {
|
|
532
|
+
renderGauge({ ...withThresholds, value: 50 });
|
|
533
|
+
|
|
534
|
+
expect(screen.getByText("Warning")).toBeInTheDocument();
|
|
535
|
+
expect(screen.queryByText("Healthy")).not.toBeInTheDocument();
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test("reads Critical at the critical threshold", () => {
|
|
539
|
+
renderGauge({ ...withThresholds, value: 80 });
|
|
540
|
+
|
|
541
|
+
expect(screen.getByText("Critical")).toBeInTheDocument();
|
|
542
|
+
expect(screen.queryByText("Warning")).not.toBeInTheDocument();
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test("scales fraction metrics onto the percent scale thresholds use", () => {
|
|
546
|
+
const container: HTMLElement = renderGauge({
|
|
547
|
+
value: 0.9,
|
|
548
|
+
rawUnit: "1",
|
|
549
|
+
metricName: "system.cpu.utilization",
|
|
550
|
+
warningThreshold: 50,
|
|
551
|
+
criticalThreshold: 80,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
expect(screen.getByText("Critical")).toBeInTheDocument();
|
|
555
|
+
expect(getGaugeNumber(container)).toHaveTextContent("90.00%");
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
describe("arc geometry", () => {
|
|
560
|
+
test("draws the value arc and dot only once the value leaves the minimum", () => {
|
|
561
|
+
const atMinimum: HTMLElement = renderGauge({
|
|
562
|
+
value: 0,
|
|
563
|
+
minValue: 0,
|
|
564
|
+
maxValue: 100,
|
|
565
|
+
});
|
|
566
|
+
// Only the background track — no value arc, no indicator dot.
|
|
567
|
+
expect(atMinimum.querySelectorAll("path")).toHaveLength(1);
|
|
568
|
+
expect(atMinimum.querySelectorAll("circle")).toHaveLength(0);
|
|
569
|
+
|
|
570
|
+
const halfway: HTMLElement = renderGauge({
|
|
571
|
+
value: 50,
|
|
572
|
+
minValue: 0,
|
|
573
|
+
maxValue: 100,
|
|
574
|
+
});
|
|
575
|
+
expect(halfway.querySelectorAll("path")).toHaveLength(2);
|
|
576
|
+
expect(halfway.querySelectorAll("circle")).toHaveLength(1);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test("renders a labelled tick per configured threshold", () => {
|
|
580
|
+
const container: HTMLElement = renderGauge({
|
|
581
|
+
value: 30,
|
|
582
|
+
warningThreshold: 50,
|
|
583
|
+
criticalThreshold: 80,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
expect(getTickLabels(container)).toEqual(["50", "80"]);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test("drops the ticks and the arc when min and max collapse to one point", () => {
|
|
590
|
+
const container: HTMLElement = renderGauge({
|
|
591
|
+
value: 30,
|
|
592
|
+
minValue: 100,
|
|
593
|
+
maxValue: 100,
|
|
594
|
+
warningThreshold: 50,
|
|
595
|
+
criticalThreshold: 80,
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// A zero range has no position to put a tick at, and no arc to sweep.
|
|
599
|
+
expect(getTickLabels(container)).toEqual([]);
|
|
600
|
+
expect(container.querySelectorAll("path")).toHaveLength(1);
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
describe("min/max footer", () => {
|
|
605
|
+
test("formats the bounds through ValueFormatter, not as raw numbers", () => {
|
|
606
|
+
renderGauge({
|
|
607
|
+
value: 3600,
|
|
608
|
+
rawUnit: "seconds",
|
|
609
|
+
minValue: 0,
|
|
610
|
+
maxValue: 7200,
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
expect(screen.getByText("0 sec")).toBeInTheDocument();
|
|
614
|
+
expect(screen.getByText("2 hours")).toBeInTheDocument();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
test("converts fraction-metric bounds back out of the percent scale", () => {
|
|
618
|
+
renderGauge({
|
|
619
|
+
value: 0.25,
|
|
620
|
+
rawUnit: "1",
|
|
621
|
+
metricName: "system.cpu.utilization",
|
|
622
|
+
minValue: 0,
|
|
623
|
+
maxValue: 100,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
expect(screen.getByText("0.00%")).toBeInTheDocument();
|
|
627
|
+
expect(screen.getByText("100.00%")).toBeInTheDocument();
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
describe("gradient namespacing", () => {
|
|
632
|
+
test("two gauges on one board get distinct ids and reference their own", () => {
|
|
633
|
+
const first: HTMLElement = renderGauge({
|
|
634
|
+
componentId: "widget-one",
|
|
635
|
+
value: 50,
|
|
636
|
+
});
|
|
637
|
+
const second: HTMLElement = renderGauge({
|
|
638
|
+
componentId: "widget-two",
|
|
639
|
+
value: 50,
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
const firstId: string | null = first
|
|
643
|
+
.querySelector("linearGradient")!
|
|
644
|
+
.getAttribute("id");
|
|
645
|
+
const secondId: string | null = second
|
|
646
|
+
.querySelector("linearGradient")!
|
|
647
|
+
.getAttribute("id");
|
|
648
|
+
|
|
649
|
+
expect(firstId).toBe("gauge-gradient-widget-one");
|
|
650
|
+
expect(secondId).toBe("gauge-gradient-widget-two");
|
|
651
|
+
expect(firstId).not.toBe(secondId);
|
|
652
|
+
|
|
653
|
+
// The value arc is the second path — the first is the background track.
|
|
654
|
+
expect(first.querySelectorAll("path")[1]!.getAttribute("stroke")).toBe(
|
|
655
|
+
`url(#${firstId})`,
|
|
656
|
+
);
|
|
657
|
+
expect(second.querySelectorAll("path")[1]!.getAttribute("stroke")).toBe(
|
|
658
|
+
`url(#${secondId})`,
|
|
659
|
+
);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
test("falls back to a default id when the component has no id yet", () => {
|
|
663
|
+
const container: HTMLElement = renderGauge({ componentId: "" });
|
|
664
|
+
|
|
665
|
+
expect(
|
|
666
|
+
container.querySelector("linearGradient")!.getAttribute("id"),
|
|
667
|
+
).toBe("gauge-gradient-default");
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
describe("title", () => {
|
|
672
|
+
test("renders the title above the gauge and omits the row without one", () => {
|
|
673
|
+
const titled: HTMLElement = renderGauge({ title: "Memory pressure" });
|
|
674
|
+
expect(within(titled).getByText("Memory pressure")).toBeInTheDocument();
|
|
675
|
+
|
|
676
|
+
const untitled: HTMLElement = renderGauge({ title: undefined });
|
|
677
|
+
expect(untitled.querySelector(".uppercase.tracking-wider")).toBeNull();
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
describe("splitFormattedDisplay", () => {
|
|
683
|
+
test("peels a trailing percent sign off as the unit", () => {
|
|
684
|
+
expect(splitFormattedDisplay("25.00%")).toEqual({
|
|
685
|
+
value: "25.00",
|
|
686
|
+
unit: "%",
|
|
687
|
+
});
|
|
688
|
+
expect(splitFormattedDisplay("%")).toEqual({ value: "", unit: "%" });
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
test("splits a single-space value at its space", () => {
|
|
692
|
+
expect(splitFormattedDisplay("1.5 MB")).toEqual({
|
|
693
|
+
value: "1.5",
|
|
694
|
+
unit: "MB",
|
|
695
|
+
});
|
|
696
|
+
expect(splitFormattedDisplay("6.91 hours")).toEqual({
|
|
697
|
+
value: "6.91",
|
|
698
|
+
unit: "hours",
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
test("leaves a bare number with an empty unit", () => {
|
|
703
|
+
expect(splitFormattedDisplay("42")).toEqual({ value: "42", unit: "" });
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
test("handles an empty string without throwing", () => {
|
|
707
|
+
expect(splitFormattedDisplay("")).toEqual({ value: "", unit: "" });
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
test("splits a multi-word unit at its LAST space, stranding the rest", () => {
|
|
711
|
+
/*
|
|
712
|
+
* The bug ValueWidgetView documents and avoids by asking ValueFormatter
|
|
713
|
+
* for the two parts rather than splitting the joined string; the gauge
|
|
714
|
+
* still splits. Asserted as-is so a fix has to arrive with an intentional
|
|
715
|
+
* test change — see SOURCE BUGS FOUND.
|
|
716
|
+
*/
|
|
717
|
+
expect(splitFormattedDisplay("5 requests per Second")).toEqual({
|
|
718
|
+
value: "5 requests per",
|
|
719
|
+
unit: "Second",
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
test("does not split on a leading space", () => {
|
|
724
|
+
expect(splitFormattedDisplay(" 42")).toEqual({ value: " 42", unit: "" });
|
|
725
|
+
});
|
|
726
|
+
});
|