@oneuptime/common 12.0.23 → 12.0.24
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/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +27 -7
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +205 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
- package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
- package/Server/Views/Partials/Head.ejs +2 -1
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
- package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
- package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
- package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
- package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
- package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
- package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
- package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
- package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
- package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
- package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
- package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
- package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
- package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
- package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
- package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
- package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
- package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
- package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
- package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
- package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
- package/Tests/UI/Components/SideOver.test.tsx +1 -1
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
- package/Tests/UI/Components/Toggle.test.tsx +25 -0
- package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
- package/Types/Code/YamlSyntax.ts +396 -0
- package/Types/Exception/ExceptionCode.ts +1 -0
- package/Types/Exception/PayloadTooLargeException.ts +8 -0
- package/Types/Marketing/Attribution.ts +10 -0
- package/Types/Marketing/MarketingEvent.ts +4 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmMetricConventions.ts +124 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
- package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
- package/UI/Components/Detail/Detail.tsx +10 -0
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
- package/UI/Components/Forms/Fields/FormField.tsx +31 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
- package/UI/Components/Forms/Validation.ts +46 -0
- package/UI/Components/Input/Input.tsx +3 -0
- package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
- package/UI/Components/ModelTable/Column.ts +8 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
- package/UI/Components/SideOver/SideOver.tsx +5 -5
- package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
- package/UI/Components/StatusPage/ResourceList.tsx +197 -38
- package/UI/Components/Toggle/Toggle.tsx +9 -1
- package/UI/Components/Types/FieldType.ts +1 -0
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +27 -7
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +204 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- package/build/dist/Types/Code/YamlSyntax.js +212 -0
- package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +10 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
- package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +9 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +27 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Types/FieldType.js +1 -0
- package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/Tests/UI/Components/Toast.test.tsx +0 -197
- package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
- package/UI/Components/Toast/Toast.tsx +0 -189
- package/UI/Components/Toast/ToastInit.tsx +0 -110
- package/build/dist/UI/Components/Toast/Toast.js +0 -123
- package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
- package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import Monitor from "../../../../Models/DatabaseModels/Monitor";
|
|
2
|
+
import StatusPageResource from "../../../../Models/DatabaseModels/StatusPageResource";
|
|
3
|
+
import ResourceList from "../../../../UI/Components/StatusPage/ResourceList";
|
|
4
|
+
import "@testing-library/jest-dom";
|
|
5
|
+
import { fireEvent, render, screen, within } from "@testing-library/react";
|
|
6
|
+
import React, { ReactElement } from "react";
|
|
7
|
+
import { beforeEach, describe, expect, jest, test } from "@jest/globals";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Contract under test - the checkbox column on a status page group's resource
|
|
11
|
+
* list.
|
|
12
|
+
*
|
|
13
|
+
* A status page group holding twenty seven monitors could only be emptied one
|
|
14
|
+
* monitor at a time: open a row's overflow menu, confirm, wait for the group to
|
|
15
|
+
* reload, and start again on the next one (issue #3419). The list carries a box
|
|
16
|
+
* per row and a box at the top of it now, and the pane above turns whatever is
|
|
17
|
+
* ticked into one removal.
|
|
18
|
+
*
|
|
19
|
+
* The list itself holds no selection state - the pane does, because a selection
|
|
20
|
+
* has to survive the refetch that follows every write. So everything here is
|
|
21
|
+
* about what the list draws for a given selection and what it reports back, and
|
|
22
|
+
* nothing here is about what the pane then does with it.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
type MakeResourceFunction = (data: {
|
|
26
|
+
id?: string | undefined;
|
|
27
|
+
monitorName?: string | undefined;
|
|
28
|
+
displayName?: string | undefined;
|
|
29
|
+
}) => StatusPageResource;
|
|
30
|
+
|
|
31
|
+
const makeResource: MakeResourceFunction = (data: {
|
|
32
|
+
id?: string | undefined;
|
|
33
|
+
monitorName?: string | undefined;
|
|
34
|
+
displayName?: string | undefined;
|
|
35
|
+
}): StatusPageResource => {
|
|
36
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
37
|
+
|
|
38
|
+
if (data.id) {
|
|
39
|
+
resource._id = data.id;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (data.monitorName) {
|
|
43
|
+
const monitor: Monitor = new Monitor();
|
|
44
|
+
monitor._id = `monitor-${data.id || "none"}`;
|
|
45
|
+
monitor.name = data.monitorName;
|
|
46
|
+
resource.monitor = monitor;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (data.displayName) {
|
|
50
|
+
resource.displayName = data.displayName;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return resource;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const API_RESOURCE: StatusPageResource = makeResource({
|
|
57
|
+
id: "resource-api",
|
|
58
|
+
monitorName: "API",
|
|
59
|
+
});
|
|
60
|
+
const DATABASE_RESOURCE: StatusPageResource = makeResource({
|
|
61
|
+
id: "resource-database",
|
|
62
|
+
monitorName: "Database",
|
|
63
|
+
});
|
|
64
|
+
const CACHE_RESOURCE: StatusPageResource = makeResource({
|
|
65
|
+
id: "resource-cache",
|
|
66
|
+
monitorName: "Cache",
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const onToggleResourceSelected: jest.MockedFunction<any> = jest.fn();
|
|
70
|
+
const onToggleAllSelected: jest.MockedFunction<any> = jest.fn();
|
|
71
|
+
const onReorder: jest.MockedFunction<any> = jest.fn();
|
|
72
|
+
const onShowMore: jest.MockedFunction<any> = jest.fn();
|
|
73
|
+
|
|
74
|
+
interface RenderOptions {
|
|
75
|
+
statusPageResources?: Array<StatusPageResource> | undefined;
|
|
76
|
+
isSelectable?: boolean | undefined;
|
|
77
|
+
selectedResourceIds?: Set<string> | undefined;
|
|
78
|
+
isAllSelected?: boolean | undefined;
|
|
79
|
+
isSelectionIndeterminate?: boolean | undefined;
|
|
80
|
+
isReorderable?: boolean | undefined;
|
|
81
|
+
visibleCount?: number | undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type RenderListFunction = (
|
|
85
|
+
options?: RenderOptions,
|
|
86
|
+
) => ReturnType<typeof render>;
|
|
87
|
+
|
|
88
|
+
const renderList: RenderListFunction = (
|
|
89
|
+
options: RenderOptions = {},
|
|
90
|
+
): ReturnType<typeof render> => {
|
|
91
|
+
return render(
|
|
92
|
+
<ResourceList
|
|
93
|
+
statusPageResources={
|
|
94
|
+
options.statusPageResources || [
|
|
95
|
+
API_RESOURCE,
|
|
96
|
+
DATABASE_RESOURCE,
|
|
97
|
+
CACHE_RESOURCE,
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
listKey="test"
|
|
101
|
+
getResourceElement={(
|
|
102
|
+
statusPageResource: StatusPageResource,
|
|
103
|
+
): ReactElement => {
|
|
104
|
+
return <span>{statusPageResource.monitor?.name || "Unknown"}</span>;
|
|
105
|
+
}}
|
|
106
|
+
isEditable={true}
|
|
107
|
+
isDeleteable={true}
|
|
108
|
+
isReorderable={
|
|
109
|
+
options.isReorderable === undefined ? false : options.isReorderable
|
|
110
|
+
}
|
|
111
|
+
isSelectable={
|
|
112
|
+
options.isSelectable === undefined ? true : options.isSelectable
|
|
113
|
+
}
|
|
114
|
+
selectedResourceIds={options.selectedResourceIds || new Set<string>()}
|
|
115
|
+
isAllSelected={Boolean(options.isAllSelected)}
|
|
116
|
+
isSelectionIndeterminate={Boolean(options.isSelectionIndeterminate)}
|
|
117
|
+
onToggleResourceSelected={onToggleResourceSelected}
|
|
118
|
+
onToggleAllSelected={onToggleAllSelected}
|
|
119
|
+
onEdit={() => {}}
|
|
120
|
+
onDelete={() => {}}
|
|
121
|
+
onShowId={() => {}}
|
|
122
|
+
onReorder={onReorder}
|
|
123
|
+
visibleCount={
|
|
124
|
+
options.visibleCount === undefined ? 100 : options.visibleCount
|
|
125
|
+
}
|
|
126
|
+
onShowMore={onShowMore}
|
|
127
|
+
emptyState={<div data-testid="empty-state">Nothing here</div>}
|
|
128
|
+
/>,
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
type SelectAllBoxFunction = () => HTMLElement;
|
|
133
|
+
|
|
134
|
+
const selectAllBox: SelectAllBoxFunction = (): HTMLElement => {
|
|
135
|
+
return screen.getByTestId("status-page-resource-list-select-all");
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
type RowByNameFunction = (name: string) => HTMLElement;
|
|
139
|
+
|
|
140
|
+
const rowByName: RowByNameFunction = (name: string): HTMLElement => {
|
|
141
|
+
const row: HTMLElement | undefined = screen
|
|
142
|
+
.queryAllByTestId("status-page-resource-row")
|
|
143
|
+
.find((candidate: HTMLElement) => {
|
|
144
|
+
return (candidate.textContent || "").includes(name);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!row) {
|
|
148
|
+
throw new Error(`No row called ${name} is on screen`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return row;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
type RowBoxFunction = (name: string) => HTMLElement;
|
|
155
|
+
|
|
156
|
+
const rowBox: RowBoxFunction = (name: string): HTMLElement => {
|
|
157
|
+
return within(rowByName(name)).getByRole("checkbox");
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
describe("ResourceList - selecting rows", () => {
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
jest.clearAllMocks();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("when the operator may remove resources", () => {
|
|
166
|
+
test("every row carries a box", () => {
|
|
167
|
+
renderList();
|
|
168
|
+
|
|
169
|
+
expect(
|
|
170
|
+
screen.queryAllByTestId("status-page-resource-row-select").length,
|
|
171
|
+
).toBe(3);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
* A bare box in a row is announced as "checkbox" and nothing else, so a
|
|
176
|
+
* screen reader user hears the same word once per row with no way to tell
|
|
177
|
+
* which row they are on.
|
|
178
|
+
*/
|
|
179
|
+
test("a row's box is named after the monitor it selects", () => {
|
|
180
|
+
renderList();
|
|
181
|
+
|
|
182
|
+
expect(screen.getByLabelText("Select API")).toBeInTheDocument();
|
|
183
|
+
expect(screen.getByLabelText("Select Database")).toBeInTheDocument();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("the list is topped by a box that selects all of it", () => {
|
|
187
|
+
renderList();
|
|
188
|
+
|
|
189
|
+
expect(selectAllBox()).toBeInTheDocument();
|
|
190
|
+
expect(screen.getByLabelText("Select all resources")).toBe(
|
|
191
|
+
selectAllBox(),
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("ticking a row reports that row, not its index", () => {
|
|
196
|
+
renderList();
|
|
197
|
+
|
|
198
|
+
fireEvent.click(rowBox("Database"));
|
|
199
|
+
|
|
200
|
+
expect(onToggleResourceSelected).toHaveBeenCalledTimes(1);
|
|
201
|
+
expect(onToggleResourceSelected.mock.calls[0]![0]).toBe(
|
|
202
|
+
DATABASE_RESOURCE,
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("un-ticking a selected row reports it too", () => {
|
|
207
|
+
renderList({
|
|
208
|
+
selectedResourceIds: new Set<string>(["resource-database"]),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
fireEvent.click(rowBox("Database"));
|
|
212
|
+
|
|
213
|
+
expect(onToggleResourceSelected.mock.calls[0]![0]).toBe(
|
|
214
|
+
DATABASE_RESOURCE,
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("a selected row is ticked and says so to anything reading the DOM", () => {
|
|
219
|
+
renderList({ selectedResourceIds: new Set<string>(["resource-api"]) });
|
|
220
|
+
|
|
221
|
+
expect(rowBox("API")).toBeChecked();
|
|
222
|
+
expect(rowBox("Database")).not.toBeChecked();
|
|
223
|
+
|
|
224
|
+
expect(rowByName("API")).toHaveAttribute("data-selected", "true");
|
|
225
|
+
expect(rowByName("Database")).toHaveAttribute("data-selected", "false");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
/*
|
|
229
|
+
* A selection made near the top of a long list has to stay visible from the
|
|
230
|
+
* bottom of it: the bulk bar says how many, and the fill says which.
|
|
231
|
+
*/
|
|
232
|
+
test("a selected row is filled rather than left white", () => {
|
|
233
|
+
renderList({ selectedResourceIds: new Set<string>(["resource-api"]) });
|
|
234
|
+
|
|
235
|
+
expect(rowByName("API").className).toContain("bg-indigo-50");
|
|
236
|
+
expect(rowByName("Database").className).toContain("bg-white");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("the box at the top follows the state it is handed", () => {
|
|
240
|
+
const { rerender } = renderList({ isAllSelected: true });
|
|
241
|
+
|
|
242
|
+
expect(selectAllBox()).toBeChecked();
|
|
243
|
+
|
|
244
|
+
rerender(
|
|
245
|
+
<ResourceList
|
|
246
|
+
statusPageResources={[API_RESOURCE]}
|
|
247
|
+
listKey="test"
|
|
248
|
+
getResourceElement={(): ReactElement => {
|
|
249
|
+
return <span>API</span>;
|
|
250
|
+
}}
|
|
251
|
+
isEditable={true}
|
|
252
|
+
isDeleteable={true}
|
|
253
|
+
isReorderable={false}
|
|
254
|
+
isSelectable={true}
|
|
255
|
+
selectedResourceIds={new Set<string>()}
|
|
256
|
+
isAllSelected={false}
|
|
257
|
+
isSelectionIndeterminate={true}
|
|
258
|
+
onToggleResourceSelected={onToggleResourceSelected}
|
|
259
|
+
onToggleAllSelected={onToggleAllSelected}
|
|
260
|
+
onEdit={() => {}}
|
|
261
|
+
onDelete={() => {}}
|
|
262
|
+
onShowId={() => {}}
|
|
263
|
+
onReorder={onReorder}
|
|
264
|
+
visibleCount={100}
|
|
265
|
+
onShowMore={onShowMore}
|
|
266
|
+
emptyState={<div />}
|
|
267
|
+
/>,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
expect(selectAllBox()).not.toBeChecked();
|
|
271
|
+
expect((selectAllBox() as HTMLInputElement).indeterminate).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("ticking the box at the top asks for everything, un-ticking for nothing", () => {
|
|
275
|
+
const { rerender } = renderList();
|
|
276
|
+
|
|
277
|
+
fireEvent.click(selectAllBox());
|
|
278
|
+
|
|
279
|
+
expect(onToggleAllSelected).toHaveBeenCalledTimes(1);
|
|
280
|
+
expect(onToggleAllSelected.mock.calls[0]![0]).toBe(true);
|
|
281
|
+
|
|
282
|
+
rerender(
|
|
283
|
+
<ResourceList
|
|
284
|
+
statusPageResources={[API_RESOURCE]}
|
|
285
|
+
listKey="test"
|
|
286
|
+
getResourceElement={(): ReactElement => {
|
|
287
|
+
return <span>API</span>;
|
|
288
|
+
}}
|
|
289
|
+
isEditable={true}
|
|
290
|
+
isDeleteable={true}
|
|
291
|
+
isReorderable={false}
|
|
292
|
+
isSelectable={true}
|
|
293
|
+
selectedResourceIds={new Set<string>(["resource-api"])}
|
|
294
|
+
isAllSelected={true}
|
|
295
|
+
isSelectionIndeterminate={false}
|
|
296
|
+
onToggleResourceSelected={onToggleResourceSelected}
|
|
297
|
+
onToggleAllSelected={onToggleAllSelected}
|
|
298
|
+
onEdit={() => {}}
|
|
299
|
+
onDelete={() => {}}
|
|
300
|
+
onShowId={() => {}}
|
|
301
|
+
onReorder={onReorder}
|
|
302
|
+
visibleCount={100}
|
|
303
|
+
onShowMore={onShowMore}
|
|
304
|
+
emptyState={<div />}
|
|
305
|
+
/>,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
fireEvent.click(selectAllBox());
|
|
309
|
+
|
|
310
|
+
expect(onToggleAllSelected).toHaveBeenCalledTimes(2);
|
|
311
|
+
expect(onToggleAllSelected.mock.calls[1]![0]).toBe(false);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
/*
|
|
315
|
+
* The header row is the only place the select-all box can live, and below
|
|
316
|
+
* sm it was hidden because it was labelling columns that are not drawn
|
|
317
|
+
* there. A phone is exactly where removing twenty seven monitors one at a
|
|
318
|
+
* time hurts most, so the row itself is drawn at every width and the column
|
|
319
|
+
* headings are what is held back.
|
|
320
|
+
*/
|
|
321
|
+
test("the header is drawn at every width once there is a box in it", () => {
|
|
322
|
+
renderList();
|
|
323
|
+
|
|
324
|
+
const header: HTMLElement = screen.getByTestId(
|
|
325
|
+
"status-page-resource-list-header",
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
expect(header.className).toContain("flex");
|
|
329
|
+
expect(header.className).not.toContain("hidden sm:flex");
|
|
330
|
+
expect(within(header).getByText("Select all")).toBeInTheDocument();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
/*
|
|
334
|
+
* A focusable control inside an aria-hidden container is in the tab order
|
|
335
|
+
* and out of the accessibility tree at the same time, which is worse than
|
|
336
|
+
* not offering it at all.
|
|
337
|
+
*/
|
|
338
|
+
test("the header does not hide its own checkbox from a screen reader", () => {
|
|
339
|
+
renderList();
|
|
340
|
+
|
|
341
|
+
expect(
|
|
342
|
+
screen.getByTestId("status-page-resource-list-header"),
|
|
343
|
+
).not.toHaveAttribute("aria-hidden");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("a row with no id cannot be ticked", () => {
|
|
347
|
+
renderList({
|
|
348
|
+
statusPageResources: [makeResource({ monitorName: "Orphan" })],
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(rowBox("Orphan")).toBeDisabled();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
/*
|
|
355
|
+
* The box at the top means "everything this list was handed", and the rows
|
|
356
|
+
* behind "Show more" were handed to it - they are simply not drawn yet. The
|
|
357
|
+
* caller is the one that resolves that to ids, so all the list has to do is
|
|
358
|
+
* ask once.
|
|
359
|
+
*/
|
|
360
|
+
test("rows held back behind Show more still have a box waiting for them", () => {
|
|
361
|
+
renderList({ visibleCount: 2 });
|
|
362
|
+
|
|
363
|
+
expect(
|
|
364
|
+
screen.queryAllByTestId("status-page-resource-row-select").length,
|
|
365
|
+
).toBe(2);
|
|
366
|
+
|
|
367
|
+
fireEvent.click(selectAllBox());
|
|
368
|
+
|
|
369
|
+
expect(onToggleAllSelected).toHaveBeenCalledTimes(1);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
/*
|
|
373
|
+
* The grip is the drag handle; the box is not. A list that can be both
|
|
374
|
+
* reordered and selected has to keep the two apart, or every attempt to
|
|
375
|
+
* tick a row starts a drag.
|
|
376
|
+
*/
|
|
377
|
+
test("a reorderable list keeps its grip and its box apart", () => {
|
|
378
|
+
renderList({ isReorderable: true });
|
|
379
|
+
|
|
380
|
+
const row: HTMLElement = rowByName("API");
|
|
381
|
+
|
|
382
|
+
expect(
|
|
383
|
+
within(row).getByTestId("status-page-resource-row-drag-handle"),
|
|
384
|
+
).toBeInTheDocument();
|
|
385
|
+
expect(within(row).getByRole("checkbox")).toBeInTheDocument();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test("an empty list is still the empty state, not a header over nothing", () => {
|
|
389
|
+
renderList({ statusPageResources: [] });
|
|
390
|
+
|
|
391
|
+
expect(screen.getByTestId("empty-state")).toBeInTheDocument();
|
|
392
|
+
expect(
|
|
393
|
+
screen.queryByTestId("status-page-resource-list-select-all"),
|
|
394
|
+
).not.toBeInTheDocument();
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe("when the operator may not remove resources", () => {
|
|
399
|
+
/*
|
|
400
|
+
* Removing is the only thing a selection is for here, so without it the
|
|
401
|
+
* column would be a row of controls that lead nowhere - and it would cost
|
|
402
|
+
* every row a slice of the width its monitor's name is drawn in.
|
|
403
|
+
*/
|
|
404
|
+
test("there is no column of boxes at all", () => {
|
|
405
|
+
renderList({ isSelectable: false });
|
|
406
|
+
|
|
407
|
+
expect(
|
|
408
|
+
screen.queryAllByTestId("status-page-resource-row-select").length,
|
|
409
|
+
).toBe(0);
|
|
410
|
+
expect(screen.queryAllByRole("checkbox").length).toBe(0);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("the header goes back to being column labels from sm up", () => {
|
|
414
|
+
renderList({ isSelectable: false });
|
|
415
|
+
|
|
416
|
+
const header: HTMLElement = screen.getByTestId(
|
|
417
|
+
"status-page-resource-list-header",
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(header.className).toContain("hidden");
|
|
421
|
+
expect(header.className).toContain("sm:flex");
|
|
422
|
+
expect(within(header).queryByText("Select all")).not.toBeInTheDocument();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test("no row claims to be selected", () => {
|
|
426
|
+
renderList({
|
|
427
|
+
isSelectable: false,
|
|
428
|
+
/* Even handed a selection, which the pane would never do. */
|
|
429
|
+
selectedResourceIds: new Set<string>(["resource-api"]),
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
expect(rowByName("API")).toHaveAttribute("data-selected", "false");
|
|
433
|
+
expect(rowByName("API").className).toContain("bg-white");
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
});
|
|
@@ -142,8 +142,12 @@ describe("Table bulk CSV export", () => {
|
|
|
142
142
|
bulkSelectedItems: [data[0]!],
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
/*
|
|
146
|
+
* The bulk action bar is visible with the selected count - in the singular
|
|
147
|
+
* for one row, because that badge is always on screen and "1 Monitors
|
|
148
|
+
* Selected" reads as a bug in the count.
|
|
149
|
+
*/
|
|
150
|
+
expect(getByText("1 Monitor Selected")).toBeTruthy();
|
|
147
151
|
|
|
148
152
|
// Open the Bulk Actions menu.
|
|
149
153
|
fireEvent.click(getByText("Bulk Actions"));
|
|
@@ -46,6 +46,31 @@ describe("Toggle", () => {
|
|
|
46
46
|
expect(onChange).toHaveBeenCalledWith(true);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
test("calls onChange exactly once per click", () => {
|
|
50
|
+
/*
|
|
51
|
+
* The click handler used to call handleChange (which already calls
|
|
52
|
+
* props.onChange) and then props.onChange again, running every consumer's
|
|
53
|
+
* handler twice per click. Idempotent handlers never noticed; the monitor
|
|
54
|
+
* criteria switches, which seed a blank incident / alert row on the way
|
|
55
|
+
* on, were saved from a second row only by their own "is the array still
|
|
56
|
+
* empty" guard.
|
|
57
|
+
*/
|
|
58
|
+
const onChange: MockFunction = getJestMockFunction();
|
|
59
|
+
|
|
60
|
+
const { getByRole } = render(
|
|
61
|
+
<Toggle onChange={onChange} initialValue={false} />,
|
|
62
|
+
);
|
|
63
|
+
const toggle: HTMLElement = getByRole("switch");
|
|
64
|
+
|
|
65
|
+
fireEvent.click(toggle);
|
|
66
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(onChange).toHaveBeenNthCalledWith(1, true);
|
|
68
|
+
|
|
69
|
+
fireEvent.click(toggle);
|
|
70
|
+
expect(onChange).toHaveBeenCalledTimes(2);
|
|
71
|
+
expect(onChange).toHaveBeenNthCalledWith(2, false);
|
|
72
|
+
});
|
|
73
|
+
|
|
49
74
|
test("calls onFocus", () => {
|
|
50
75
|
const onFocus: MockFunction = getJestMockFunction();
|
|
51
76
|
|