@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
|
@@ -8,10 +8,45 @@ import { describe, expect, it, test } from "@jest/globals";
|
|
|
8
8
|
* the whole suite is skipped before a single assertion runs.
|
|
9
9
|
*/
|
|
10
10
|
import "@testing-library/jest-dom";
|
|
11
|
-
import { fireEvent, render } from "@testing-library/react";
|
|
11
|
+
import { act, fireEvent, render, RenderResult } from "@testing-library/react";
|
|
12
12
|
import React from "react";
|
|
13
13
|
import getJestMockFunction, { MockFunction } from "../../../Tests/MockType";
|
|
14
14
|
|
|
15
|
+
/*
|
|
16
|
+
* The panel mounts closed and opens on the next animation frame, so anything
|
|
17
|
+
* asserting the settled look has to let that frame run first.
|
|
18
|
+
*/
|
|
19
|
+
type FlushEntranceFrameFunction = () => Promise<void>;
|
|
20
|
+
|
|
21
|
+
const flushEntranceFrame: FlushEntranceFrameFunction =
|
|
22
|
+
async (): Promise<void> => {
|
|
23
|
+
await act(async () => {
|
|
24
|
+
await new Promise<void>((resolve: () => void) => {
|
|
25
|
+
requestAnimationFrame(() => {
|
|
26
|
+
resolve();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* The backdrop a user actually clicks is the layer the panel is centred in,
|
|
34
|
+
* not the tinted sheet behind it, which is inert.
|
|
35
|
+
*/
|
|
36
|
+
type GetBackdropLayerFunction = (panel: HTMLElement) => HTMLElement;
|
|
37
|
+
|
|
38
|
+
const getBackdropLayer: GetBackdropLayerFunction = (
|
|
39
|
+
panel: HTMLElement,
|
|
40
|
+
): HTMLElement => {
|
|
41
|
+
return panel.parentElement!;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type GetHeaderFunction = (title: HTMLElement) => HTMLElement;
|
|
45
|
+
|
|
46
|
+
const getHeader: GetHeaderFunction = (title: HTMLElement): HTMLElement => {
|
|
47
|
+
return title.parentElement!.parentElement!;
|
|
48
|
+
};
|
|
49
|
+
|
|
15
50
|
describe("Modal", () => {
|
|
16
51
|
test("renders the modal with the title and description", () => {
|
|
17
52
|
const onSubmit: MockFunction = getJestMockFunction();
|
|
@@ -453,4 +488,249 @@ describe("Modal", () => {
|
|
|
453
488
|
|
|
454
489
|
expect(getByTestId("right-element")).toBeInTheDocument();
|
|
455
490
|
});
|
|
491
|
+
|
|
492
|
+
it("fades and lifts the panel in from a closed state", async () => {
|
|
493
|
+
const { getByTestId } = render(
|
|
494
|
+
<Modal title="Entrance Modal">
|
|
495
|
+
<div>Modal content</div>
|
|
496
|
+
</Modal>,
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
const panel: HTMLElement = getByTestId("modal");
|
|
500
|
+
const backdrop: HTMLElement = getByTestId("modal-backdrop");
|
|
501
|
+
|
|
502
|
+
expect(panel).toHaveClass("opacity-0", "translate-y-6", "sm:scale-95");
|
|
503
|
+
expect(backdrop).toHaveClass("opacity-0");
|
|
504
|
+
|
|
505
|
+
await flushEntranceFrame();
|
|
506
|
+
|
|
507
|
+
expect(panel).toHaveClass("opacity-100");
|
|
508
|
+
expect(backdrop).toHaveClass("opacity-100");
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
/*
|
|
512
|
+
* A `scale-100` left behind after the entrance would make the panel the
|
|
513
|
+
* containing block for `position: fixed`, and a modal opened from inside
|
|
514
|
+
* this one would then be laid out against the panel instead of the viewport.
|
|
515
|
+
*/
|
|
516
|
+
it("leaves no transform on the panel once it has settled", async () => {
|
|
517
|
+
const { getByTestId } = render(
|
|
518
|
+
<Modal title="Settled Modal">
|
|
519
|
+
<div>Modal content</div>
|
|
520
|
+
</Modal>,
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
await flushEntranceFrame();
|
|
524
|
+
|
|
525
|
+
expect(getByTestId("modal").className).not.toMatch(
|
|
526
|
+
/(^|\s)(sm:)?(scale|translate)-/,
|
|
527
|
+
);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it("closes on a click that both starts and ends on the backdrop", () => {
|
|
531
|
+
const onCloseMock: MockFunction = getJestMockFunction();
|
|
532
|
+
const { getByTestId } = render(
|
|
533
|
+
<Modal title="Dismissable Modal" onClose={onCloseMock}>
|
|
534
|
+
<div>Modal content</div>
|
|
535
|
+
</Modal>,
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
const backdropLayer: HTMLElement = getBackdropLayer(getByTestId("modal"));
|
|
539
|
+
|
|
540
|
+
fireEvent.mouseDown(backdropLayer);
|
|
541
|
+
fireEvent.click(backdropLayer);
|
|
542
|
+
|
|
543
|
+
expect(onCloseMock).toHaveBeenCalledTimes(1);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
/*
|
|
547
|
+
* Selecting text in the body and releasing past the edge of the panel is a
|
|
548
|
+
* drag, not a dismissal. Closing on it is the fastest way to lose a
|
|
549
|
+
* half-filled form.
|
|
550
|
+
*/
|
|
551
|
+
it("stays open when a drag that started inside the panel ends on the backdrop", () => {
|
|
552
|
+
const onCloseMock: MockFunction = getJestMockFunction();
|
|
553
|
+
const { getByText, getByTestId } = render(
|
|
554
|
+
<Modal title="Drag Modal" onClose={onCloseMock}>
|
|
555
|
+
<div>Modal content</div>
|
|
556
|
+
</Modal>,
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
fireEvent.mouseDown(getByText("Modal content"));
|
|
560
|
+
fireEvent.click(getBackdropLayer(getByTestId("modal")));
|
|
561
|
+
|
|
562
|
+
expect(onCloseMock).not.toHaveBeenCalled();
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("stays open on a backdrop click when backdrop dismissal is disabled", () => {
|
|
566
|
+
const onCloseMock: MockFunction = getJestMockFunction();
|
|
567
|
+
const { getByTestId } = render(
|
|
568
|
+
<Modal
|
|
569
|
+
title="Sticky Modal"
|
|
570
|
+
onClose={onCloseMock}
|
|
571
|
+
disableCloseOnBackdropClick={true}
|
|
572
|
+
>
|
|
573
|
+
<div>Modal content</div>
|
|
574
|
+
</Modal>,
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
const backdropLayer: HTMLElement = getBackdropLayer(getByTestId("modal"));
|
|
578
|
+
|
|
579
|
+
fireEvent.mouseDown(backdropLayer);
|
|
580
|
+
fireEvent.click(backdropLayer);
|
|
581
|
+
|
|
582
|
+
expect(onCloseMock).not.toHaveBeenCalled();
|
|
583
|
+
// The close button is still the way out.
|
|
584
|
+
fireEvent.click(getByTestId("close-button"));
|
|
585
|
+
expect(onCloseMock).toHaveBeenCalledTimes(1);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it("ignores a backdrop click on a modal that is not the topmost one", () => {
|
|
589
|
+
const onParentCloseMock: MockFunction = getJestMockFunction();
|
|
590
|
+
const onChildCloseMock: MockFunction = getJestMockFunction();
|
|
591
|
+
|
|
592
|
+
const NestedModalFixture: React.FunctionComponent =
|
|
593
|
+
(): React.ReactElement => {
|
|
594
|
+
const [isChildOpen, setIsChildOpen] = React.useState<boolean>(false);
|
|
595
|
+
|
|
596
|
+
return (
|
|
597
|
+
<Modal title="Parent Modal" onClose={onParentCloseMock}>
|
|
598
|
+
<>
|
|
599
|
+
<button
|
|
600
|
+
type="button"
|
|
601
|
+
onClick={() => {
|
|
602
|
+
setIsChildOpen(true);
|
|
603
|
+
}}
|
|
604
|
+
>
|
|
605
|
+
Open child
|
|
606
|
+
</button>
|
|
607
|
+
{isChildOpen && (
|
|
608
|
+
<Modal title="Child Modal" onClose={onChildCloseMock}>
|
|
609
|
+
<div>Child content</div>
|
|
610
|
+
</Modal>
|
|
611
|
+
)}
|
|
612
|
+
</>
|
|
613
|
+
</Modal>
|
|
614
|
+
);
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
const { getAllByTestId, getByRole } = render(<NestedModalFixture />);
|
|
618
|
+
|
|
619
|
+
fireEvent.click(getByRole("button", { name: "Open child" }));
|
|
620
|
+
|
|
621
|
+
const [parentPanel, childPanel] = getAllByTestId("modal") as [
|
|
622
|
+
HTMLElement,
|
|
623
|
+
HTMLElement,
|
|
624
|
+
];
|
|
625
|
+
const parentBackdropLayer: HTMLElement = getBackdropLayer(parentPanel);
|
|
626
|
+
|
|
627
|
+
fireEvent.mouseDown(parentBackdropLayer);
|
|
628
|
+
fireEvent.click(parentBackdropLayer);
|
|
629
|
+
|
|
630
|
+
expect(onParentCloseMock).not.toHaveBeenCalled();
|
|
631
|
+
expect(onChildCloseMock).not.toHaveBeenCalled();
|
|
632
|
+
|
|
633
|
+
const childBackdropLayer: HTMLElement = getBackdropLayer(childPanel);
|
|
634
|
+
|
|
635
|
+
fireEvent.mouseDown(childBackdropLayer);
|
|
636
|
+
fireEvent.click(childBackdropLayer);
|
|
637
|
+
|
|
638
|
+
expect(onChildCloseMock).toHaveBeenCalledTimes(1);
|
|
639
|
+
expect(onParentCloseMock).not.toHaveBeenCalled();
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("locks page scrolling for as long as any modal is open", () => {
|
|
643
|
+
document.body.style.overflow = "auto";
|
|
644
|
+
|
|
645
|
+
const parent: RenderResult = render(
|
|
646
|
+
<Modal title="First Modal">
|
|
647
|
+
<div>Modal content</div>
|
|
648
|
+
</Modal>,
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
expect(document.body.style.overflow).toBe("hidden");
|
|
652
|
+
|
|
653
|
+
const child: RenderResult = render(
|
|
654
|
+
<Modal title="Second Modal">
|
|
655
|
+
<div>Modal content</div>
|
|
656
|
+
</Modal>,
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
child.unmount();
|
|
660
|
+
|
|
661
|
+
// The first modal is still up, so scrolling must not come back yet.
|
|
662
|
+
expect(document.body.style.overflow).toBe("hidden");
|
|
663
|
+
|
|
664
|
+
parent.unmount();
|
|
665
|
+
|
|
666
|
+
expect(document.body.style.overflow).toBe("auto");
|
|
667
|
+
|
|
668
|
+
document.body.style.overflow = "";
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
it("shows a scroll shadow only on the side that content is hidden", () => {
|
|
672
|
+
const { getByTestId } = render(
|
|
673
|
+
<Modal title="Tall Modal" onSubmit={getJestMockFunction()}>
|
|
674
|
+
<div>Modal content</div>
|
|
675
|
+
</Modal>,
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
const content: HTMLElement = getByTestId("modal-content");
|
|
679
|
+
const header: HTMLElement = getHeader(getByTestId("modal-title"));
|
|
680
|
+
const footer: HTMLElement = getByTestId("modal-footer");
|
|
681
|
+
|
|
682
|
+
/*
|
|
683
|
+
* jsdom lays nothing out, so the body is given a size that overflows its
|
|
684
|
+
* container by hand.
|
|
685
|
+
*/
|
|
686
|
+
Object.defineProperty(content, "scrollHeight", {
|
|
687
|
+
configurable: true,
|
|
688
|
+
value: 600,
|
|
689
|
+
});
|
|
690
|
+
Object.defineProperty(content, "clientHeight", {
|
|
691
|
+
configurable: true,
|
|
692
|
+
value: 200,
|
|
693
|
+
});
|
|
694
|
+
Object.defineProperty(content, "scrollTop", {
|
|
695
|
+
configurable: true,
|
|
696
|
+
value: 0,
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
fireEvent.scroll(content);
|
|
700
|
+
|
|
701
|
+
expect(header.className).not.toMatch(/shadow-\[/);
|
|
702
|
+
expect(footer.className).toMatch(/shadow-\[0_-6px/);
|
|
703
|
+
|
|
704
|
+
Object.defineProperty(content, "scrollTop", {
|
|
705
|
+
configurable: true,
|
|
706
|
+
value: 400,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
fireEvent.scroll(content);
|
|
710
|
+
|
|
711
|
+
expect(header.className).toMatch(/shadow-\[0_6px/);
|
|
712
|
+
expect(footer.className).not.toMatch(/shadow-\[/);
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
/*
|
|
716
|
+
* The close button used to be positioned over the header, so a title long
|
|
717
|
+
* enough to reach the right edge ran underneath it.
|
|
718
|
+
*/
|
|
719
|
+
it("lays the close button out beside the title rather than over it", () => {
|
|
720
|
+
const { getByTestId } = render(
|
|
721
|
+
<Modal
|
|
722
|
+
title="A title long enough to reach the right edge of the header"
|
|
723
|
+
onClose={getJestMockFunction()}
|
|
724
|
+
>
|
|
725
|
+
<div>Modal content</div>
|
|
726
|
+
</Modal>,
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
const title: HTMLElement = getByTestId("modal-title");
|
|
730
|
+
const closeButton: HTMLElement = getByTestId("close-button");
|
|
731
|
+
|
|
732
|
+
expect(closeButton).not.toHaveClass("absolute");
|
|
733
|
+
expect(closeButton.parentElement).toBe(getHeader(title));
|
|
734
|
+
expect(title.parentElement).not.toHaveClass("pr-9");
|
|
735
|
+
});
|
|
456
736
|
});
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import FiltersForm from "../../../UI/Components/Filters/FiltersForm";
|
|
2
|
+
import ClassicFilter from "../../../UI/Components/Filters/Types/Filter";
|
|
3
|
+
import FilterData from "../../../UI/Components/Filters/Types/FilterData";
|
|
4
|
+
import ModelFilter from "../../../UI/Components/ModelFilter/Filter";
|
|
5
|
+
import buildQueryFromFilterData, {
|
|
6
|
+
getFilterKeys,
|
|
7
|
+
sanitizeFilterData,
|
|
8
|
+
} from "../../../UI/Components/ModelTable/FilterDataToQuery";
|
|
9
|
+
import FieldType from "../../../UI/Components/Types/FieldType";
|
|
10
|
+
import EqualTo from "../../../Types/BaseDatabase/EqualTo";
|
|
11
|
+
import NotEqual from "../../../Types/BaseDatabase/NotEqual";
|
|
12
|
+
import Query from "../../../Types/BaseDatabase/Query";
|
|
13
|
+
import Search from "../../../Types/BaseDatabase/Search";
|
|
14
|
+
import StartsWith from "../../../Types/BaseDatabase/StartsWith";
|
|
15
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
16
|
+
import JSONFunctions from "../../../Types/JSONFunctions";
|
|
17
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
18
|
+
import {
|
|
19
|
+
cleanup,
|
|
20
|
+
fireEvent,
|
|
21
|
+
render,
|
|
22
|
+
screen,
|
|
23
|
+
within,
|
|
24
|
+
} from "@testing-library/react";
|
|
25
|
+
import React from "react";
|
|
26
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
27
|
+
|
|
28
|
+
/*
|
|
29
|
+
* A master admin pastes a user id into the Users table's "User ID" filter and
|
|
30
|
+
* presses Apply. Between that keystroke and the rows coming back, the value
|
|
31
|
+
* crosses four separate pieces of machinery, each of which can drop it without
|
|
32
|
+
* anything visibly breaking:
|
|
33
|
+
*
|
|
34
|
+
* 1. FiltersForm picks a filter component by FieldType. ObjectID has to land
|
|
35
|
+
* on the text input; a type it does not recognise renders no control at
|
|
36
|
+
* all, and the modal shows a label with empty space beside it.
|
|
37
|
+
* 2. That component encodes the operator into a typed query value.
|
|
38
|
+
* 3. sanitizeFilterData drops every key the table did not declare - the
|
|
39
|
+
* trust boundary for URL-supplied filters - so `_id` has to be declared.
|
|
40
|
+
* 4. buildQueryFromFilterData copies recognised values into the query.
|
|
41
|
+
*
|
|
42
|
+
* These tests walk the whole path with the exact filter declaration the admin
|
|
43
|
+
* Users and Projects tables ship, rather than asserting on any one stage in
|
|
44
|
+
* isolation.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const USER_ID: string = "5f0e8e3a-1f24-4a2f-9e4c-2b1d3c4e5f60";
|
|
48
|
+
|
|
49
|
+
// Exactly what App/FeatureSet/AdminDashboard/src/Pages/Users/Index.tsx declares.
|
|
50
|
+
const ID_FILTER: ModelFilter<User> = {
|
|
51
|
+
field: { _id: true },
|
|
52
|
+
title: "User ID",
|
|
53
|
+
type: FieldType.ObjectID,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const NAME_FILTER: ModelFilter<User> = {
|
|
57
|
+
field: { name: true },
|
|
58
|
+
title: "Full Name",
|
|
59
|
+
type: FieldType.Text,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const MODEL_FILTERS: Array<ModelFilter<User>> = [ID_FILTER, NAME_FILTER];
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
* BaseModelTable converts its `field`-shaped filters into the `key`-shaped
|
|
66
|
+
* ones the filter form consumes. Mirror that conversion here so the form is
|
|
67
|
+
* driven by the page's real declaration.
|
|
68
|
+
*/
|
|
69
|
+
type ToClassicFunction = (filter: ModelFilter<User>) => ClassicFilter<User>;
|
|
70
|
+
|
|
71
|
+
const toClassic: ToClassicFunction = (
|
|
72
|
+
filter: ModelFilter<User>,
|
|
73
|
+
): ClassicFilter<User> => {
|
|
74
|
+
return {
|
|
75
|
+
title: filter.title,
|
|
76
|
+
key: Object.keys(filter.field)[0] as keyof User,
|
|
77
|
+
type: filter.type,
|
|
78
|
+
} as ClassicFilter<User>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The filter form is controlled by its parent - the real one being the filter
|
|
83
|
+
* modal, which holds the selections until Apply. This stands in for that
|
|
84
|
+
* parent, and publishes the current selections so assertions can read them.
|
|
85
|
+
*/
|
|
86
|
+
let filterData: FilterData<User> = {};
|
|
87
|
+
|
|
88
|
+
const FilterModalStandIn: React.FunctionComponent<{
|
|
89
|
+
initial: FilterData<User>;
|
|
90
|
+
onChange: (data: FilterData<User>) => void;
|
|
91
|
+
}> = (props: {
|
|
92
|
+
initial: FilterData<User>;
|
|
93
|
+
onChange: (data: FilterData<User>) => void;
|
|
94
|
+
}): React.ReactElement => {
|
|
95
|
+
const [data, setData] = React.useState<FilterData<User>>(props.initial);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<FiltersForm<User>
|
|
99
|
+
id="users-table-filter-form"
|
|
100
|
+
showFilter={true}
|
|
101
|
+
filters={MODEL_FILTERS.map(toClassic)}
|
|
102
|
+
filterData={data}
|
|
103
|
+
onFilterChanged={(next: FilterData<User>) => {
|
|
104
|
+
setData(next);
|
|
105
|
+
props.onChange(next);
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
type RenderFormFunction = (initial?: FilterData<User>) => void;
|
|
112
|
+
|
|
113
|
+
const renderForm: RenderFormFunction = (initial?: FilterData<User>): void => {
|
|
114
|
+
filterData = initial || {};
|
|
115
|
+
|
|
116
|
+
render(
|
|
117
|
+
<FilterModalStandIn
|
|
118
|
+
initial={filterData}
|
|
119
|
+
onChange={(next: FilterData<User>) => {
|
|
120
|
+
filterData = next;
|
|
121
|
+
}}
|
|
122
|
+
/>,
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The text input belonging to the User ID row, found by the placeholder the
|
|
128
|
+
* filter builds from its own title.
|
|
129
|
+
*/
|
|
130
|
+
type IdInputFunction = () => HTMLInputElement;
|
|
131
|
+
|
|
132
|
+
const idInput: IdInputFunction = (): HTMLInputElement => {
|
|
133
|
+
return screen.getByPlaceholderText("Filter by User ID") as HTMLInputElement;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The operator trigger on the User ID row. Rows are `grid` wrappers, so scope
|
|
138
|
+
* the lookup to the one whose label is "User ID" - otherwise the Full Name
|
|
139
|
+
* row's trigger is just as good a match.
|
|
140
|
+
*/
|
|
141
|
+
type IdOperatorTriggerFunction = () => HTMLButtonElement;
|
|
142
|
+
|
|
143
|
+
const idOperatorTrigger: IdOperatorTriggerFunction = (): HTMLButtonElement => {
|
|
144
|
+
const label: HTMLElement = screen.getByText("User ID");
|
|
145
|
+
const row: HTMLElement = label.closest("div.grid") as HTMLElement;
|
|
146
|
+
|
|
147
|
+
return within(row).getByRole("button", {
|
|
148
|
+
name: /contains|equals|starts with|ends with|does not/i,
|
|
149
|
+
}) as HTMLButtonElement;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
type ChooseOperatorFunction = (label: string) => void;
|
|
153
|
+
|
|
154
|
+
const chooseOperator: ChooseOperatorFunction = (label: string): void => {
|
|
155
|
+
const trigger: HTMLButtonElement = idOperatorTrigger();
|
|
156
|
+
|
|
157
|
+
fireEvent.mouseDown(trigger);
|
|
158
|
+
fireEvent.click(trigger);
|
|
159
|
+
|
|
160
|
+
const menuId: string = trigger.getAttribute("aria-controls") as string;
|
|
161
|
+
const menu: HTMLElement = document.getElementById(menuId) as HTMLElement;
|
|
162
|
+
|
|
163
|
+
fireEvent.click(within(menu).getByText(label));
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
type QueryFromFilterDataFunction = () => Query<User>;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The two stages that sit between the filter modal and the request: strip
|
|
170
|
+
* anything undeclared, then translate what is left into a query.
|
|
171
|
+
*/
|
|
172
|
+
const queryFromFilterData: QueryFromFilterDataFunction = (): Query<User> => {
|
|
173
|
+
return buildQueryFromFilterData<User>({
|
|
174
|
+
filterData: sanitizeFilterData<User>({
|
|
175
|
+
filterData: filterData,
|
|
176
|
+
filters: MODEL_FILTERS,
|
|
177
|
+
}),
|
|
178
|
+
filters: MODEL_FILTERS,
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
describe("filtering a model by its ObjectID", () => {
|
|
183
|
+
beforeEach(() => {
|
|
184
|
+
filterData = {};
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
afterEach(() => {
|
|
188
|
+
cleanup();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe("the control the admin actually gets", () => {
|
|
192
|
+
/*
|
|
193
|
+
* FiltersForm renders every filter component and each one returns an
|
|
194
|
+
* empty fragment for types it does not own. If ObjectID were missing from
|
|
195
|
+
* the text filter's list, the row would render its label and nothing
|
|
196
|
+
* else - a filter that looks present and cannot be used.
|
|
197
|
+
*/
|
|
198
|
+
test("renders a text input to paste an id into", () => {
|
|
199
|
+
renderForm();
|
|
200
|
+
|
|
201
|
+
expect(idInput()).toBeTruthy();
|
|
202
|
+
expect(idInput().tagName).toBe("INPUT");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("defaults to matching on part of an id", () => {
|
|
206
|
+
renderForm();
|
|
207
|
+
|
|
208
|
+
expect(idOperatorTrigger().textContent).toContain("contains");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* An id is usually copied whole, so exact match has to be reachable -
|
|
213
|
+
* and it is the one operator where a wrong encoding is invisible, since
|
|
214
|
+
* "contains <whole id>" and "equals <whole id>" return the same row.
|
|
215
|
+
*/
|
|
216
|
+
test("offers exact match as well as partial", () => {
|
|
217
|
+
renderForm();
|
|
218
|
+
|
|
219
|
+
const trigger: HTMLButtonElement = idOperatorTrigger();
|
|
220
|
+
|
|
221
|
+
fireEvent.mouseDown(trigger);
|
|
222
|
+
fireEvent.click(trigger);
|
|
223
|
+
|
|
224
|
+
const menu: HTMLElement = document.getElementById(
|
|
225
|
+
trigger.getAttribute("aria-controls") as string,
|
|
226
|
+
) as HTMLElement;
|
|
227
|
+
|
|
228
|
+
expect(
|
|
229
|
+
Array.from(menu.querySelectorAll("[role='option']")).map(
|
|
230
|
+
(option: Element) => {
|
|
231
|
+
return (option.textContent || "").trim();
|
|
232
|
+
},
|
|
233
|
+
),
|
|
234
|
+
).toEqual([
|
|
235
|
+
"contains",
|
|
236
|
+
"does not contain",
|
|
237
|
+
"equals",
|
|
238
|
+
"does not equal",
|
|
239
|
+
"starts with",
|
|
240
|
+
"ends with",
|
|
241
|
+
"is empty",
|
|
242
|
+
"is not empty",
|
|
243
|
+
]);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("what a typed id becomes", () => {
|
|
248
|
+
test("a partial id becomes a Search on _id", () => {
|
|
249
|
+
renderForm();
|
|
250
|
+
|
|
251
|
+
fireEvent.change(idInput(), { target: { value: "5f0e8e3a" } });
|
|
252
|
+
|
|
253
|
+
const query: Query<User> = queryFromFilterData();
|
|
254
|
+
|
|
255
|
+
expect(query._id).toBeInstanceOf(Search);
|
|
256
|
+
expect((query._id as Search<string>).toString()).toBe("5f0e8e3a");
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("a whole id under 'equals' becomes an EqualTo on _id", () => {
|
|
260
|
+
renderForm();
|
|
261
|
+
|
|
262
|
+
fireEvent.change(idInput(), { target: { value: USER_ID } });
|
|
263
|
+
chooseOperator("equals");
|
|
264
|
+
|
|
265
|
+
const query: Query<User> = queryFromFilterData();
|
|
266
|
+
|
|
267
|
+
expect(query._id).toBeInstanceOf(EqualTo);
|
|
268
|
+
expect((query._id as EqualTo<string>).toString()).toBe(USER_ID);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("'does not equal' becomes a NotEqual on _id", () => {
|
|
272
|
+
renderForm();
|
|
273
|
+
|
|
274
|
+
fireEvent.change(idInput(), { target: { value: USER_ID } });
|
|
275
|
+
chooseOperator("does not equal");
|
|
276
|
+
|
|
277
|
+
expect(queryFromFilterData()._id).toBeInstanceOf(NotEqual);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("'starts with' becomes a StartsWith on _id", () => {
|
|
281
|
+
renderForm();
|
|
282
|
+
|
|
283
|
+
fireEvent.change(idInput(), { target: { value: "5f0e8e3a" } });
|
|
284
|
+
chooseOperator("starts with");
|
|
285
|
+
|
|
286
|
+
expect(queryFromFilterData()._id).toBeInstanceOf(StartsWith);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
/*
|
|
290
|
+
* Clearing the box has to clear the filter, not send an empty match that
|
|
291
|
+
* every row satisfies - or, worse, an `_id = ''` the database rejects.
|
|
292
|
+
*/
|
|
293
|
+
test("clearing the box removes the filter entirely", () => {
|
|
294
|
+
renderForm();
|
|
295
|
+
|
|
296
|
+
fireEvent.change(idInput(), { target: { value: USER_ID } });
|
|
297
|
+
expect(queryFromFilterData()._id).toBeDefined();
|
|
298
|
+
|
|
299
|
+
fireEvent.change(idInput(), { target: { value: "" } });
|
|
300
|
+
expect(queryFromFilterData()._id).toBeUndefined();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("filtering by id leaves the other filters alone", () => {
|
|
304
|
+
renderForm();
|
|
305
|
+
|
|
306
|
+
fireEvent.change(idInput(), { target: { value: USER_ID } });
|
|
307
|
+
|
|
308
|
+
expect(Object.keys(queryFromFilterData())).toEqual(["_id"]);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe("the sanitizer that stands between the modal and the request", () => {
|
|
313
|
+
/*
|
|
314
|
+
* `sanitizeFilterData` keeps only keys the table declared a filter for.
|
|
315
|
+
* Before the page declared one, an `_id` in the filter data - restored
|
|
316
|
+
* from a URL, say - was dropped here and the table came back unfiltered.
|
|
317
|
+
*/
|
|
318
|
+
test("keeps _id now that the page declares a filter for it", () => {
|
|
319
|
+
expect(getFilterKeys<User>(MODEL_FILTERS)).toContain("_id");
|
|
320
|
+
|
|
321
|
+
expect(
|
|
322
|
+
sanitizeFilterData<User>({
|
|
323
|
+
filterData: { _id: new Search(USER_ID) } as FilterData<User>,
|
|
324
|
+
filters: MODEL_FILTERS,
|
|
325
|
+
})._id,
|
|
326
|
+
).toBeInstanceOf(Search);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("still drops a column the page does not offer a filter for", () => {
|
|
330
|
+
expect(
|
|
331
|
+
sanitizeFilterData<User>({
|
|
332
|
+
filterData: {
|
|
333
|
+
_id: new Search(USER_ID),
|
|
334
|
+
isMasterAdmin: true,
|
|
335
|
+
} as unknown as FilterData<User>,
|
|
336
|
+
filters: MODEL_FILTERS,
|
|
337
|
+
}),
|
|
338
|
+
).toEqual({ _id: new Search(USER_ID) });
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
/*
|
|
343
|
+
* Table filters are mirrored into the query string so a filtered view can be
|
|
344
|
+
* bookmarked and shared - which for an ID filter is the whole point, since
|
|
345
|
+
* "here is the user I mean" is exactly the link an admin wants to send.
|
|
346
|
+
* The values are class instances, so they only survive the round trip
|
|
347
|
+
* through JSONFunctions' typed (de)serialization.
|
|
348
|
+
*/
|
|
349
|
+
describe("sharing a filtered view by URL", () => {
|
|
350
|
+
type RoundTripFunction = (value: unknown) => unknown;
|
|
351
|
+
|
|
352
|
+
const roundTrip: RoundTripFunction = (value: unknown): unknown => {
|
|
353
|
+
const serialized: string = JSON.stringify(
|
|
354
|
+
JSONFunctions.serialize({ _id: value } as JSONObject),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
JSONFunctions.deserialize(JSON.parse(serialized) as JSONObject) as {
|
|
359
|
+
_id: unknown;
|
|
360
|
+
}
|
|
361
|
+
)._id;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
test("a 'contains' id filter survives the round trip", () => {
|
|
365
|
+
const restored: unknown = roundTrip(new Search(USER_ID));
|
|
366
|
+
|
|
367
|
+
expect(restored).toBeInstanceOf(Search);
|
|
368
|
+
expect((restored as Search<string>).toString()).toBe(USER_ID);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test("an 'equals' id filter survives the round trip", () => {
|
|
372
|
+
const restored: unknown = roundTrip(new EqualTo(USER_ID));
|
|
373
|
+
|
|
374
|
+
expect(restored).toBeInstanceOf(EqualTo);
|
|
375
|
+
expect((restored as EqualTo<string>).toString()).toBe(USER_ID);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
/*
|
|
379
|
+
* A restored filter also has to read back as the operator that produced
|
|
380
|
+
* it, or the modal reopens showing "contains" over an equality filter.
|
|
381
|
+
*/
|
|
382
|
+
test("a restored 'equals' filter reopens as 'equals'", () => {
|
|
383
|
+
renderForm({ _id: new EqualTo(USER_ID) } as FilterData<User>);
|
|
384
|
+
|
|
385
|
+
expect(idOperatorTrigger().textContent).toContain("equals");
|
|
386
|
+
expect(idInput().value).toBe(USER_ID);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test("a restored 'contains' filter reopens as 'contains'", () => {
|
|
390
|
+
renderForm({ _id: new Search("5f0e8e3a") } as FilterData<User>);
|
|
391
|
+
|
|
392
|
+
expect(idOperatorTrigger().textContent).toContain("contains");
|
|
393
|
+
expect(idInput().value).toBe("5f0e8e3a");
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
});
|