@oneuptime/common 14.0.6 → 14.0.8
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/Incident.ts +267 -1
- package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
- package/Models/DatabaseModels/InventoryItem.ts +23 -12
- package/Models/DatabaseModels/StatusPage.ts +55 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
- package/Server/API/IncidentAPI.ts +162 -0
- package/Server/API/StatusPageAPI.ts +487 -171
- package/Server/API/StatusPageSubscriberAPI.ts +63 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
- package/Server/Infrastructure/Semaphore.ts +10 -0
- package/Server/Middleware/IdentityRateLimit.ts +108 -3
- package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
- package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
- package/Server/Services/AlertService.ts +32 -36
- package/Server/Services/DatabaseService.ts +93 -10
- package/Server/Services/IncidentAlertService.ts +609 -2
- package/Server/Services/IncidentCustomFieldService.ts +276 -1
- package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
- package/Server/Services/IncidentEpisodeService.ts +15 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
- package/Server/Services/IncidentPublicNoteService.ts +30 -1
- package/Server/Services/IncidentService.ts +1663 -103
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/InventoryItemService.ts +38 -0
- package/Server/Services/IoTDeviceCredentialService.ts +13 -4
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +118 -64
- package/Server/Services/SmsService.ts +10 -0
- package/Server/Services/StatusPageService.ts +65 -8
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
- package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
- package/Server/Services/StatusPageSubscriberService.ts +1161 -64
- package/Server/Services/UserNotificationRuleService.ts +55 -53
- package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
- package/Server/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
- package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
- package/Server/Utils/Response.ts +31 -0
- package/Server/Utils/SSRFProtection.ts +76 -16
- package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
- package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
- package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
- package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
- package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
- package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
- package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
- package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
- package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
- package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
- package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
- package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
- package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
- package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
- package/Server/Utils/StatusPageResource.ts +51 -20
- package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
- package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
- package/Server/Utils/Topology/TopologyRequest.ts +463 -0
- package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
- package/Server/Utils/Topology/TopologySql.ts +1024 -0
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
- package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
- package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
- package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
- package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
- package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
- package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
- package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
- package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
- package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
- package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
- package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
- package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
- package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
- package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
- package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +534 -6
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
- package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
- package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
- package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
- package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
- package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
- package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
- package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
- package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
- package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
- package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
- package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
- package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
- package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
- package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
- package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
- package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
- package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
- package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
- package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
- package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
- package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
- package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
- package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
- package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
- package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
- package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
- package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
- package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
- package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
- package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
- package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
- package/Tests/Server/Services/DomainService.test.ts +525 -0
- package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
- package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
- package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
- package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
- package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
- package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
- package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
- package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
- package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
- package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
- package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
- package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
- package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
- package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
- package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
- package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
- package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
- package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
- package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
- package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
- package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
- package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
- package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
- package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
- package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
- package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
- package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
- package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
- package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
- package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
- package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
- package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
- package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
- package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
- package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
- package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
- package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
- package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
- package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
- package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
- package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
- package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
- package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
- package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
- package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
- package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
- package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
- package/Tests/Types/SafeHtml.test.ts +102 -0
- package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
- package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
- package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
- package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
- package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
- package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
- package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
- package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
- package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
- package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
- package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
- package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
- package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
- package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
- package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
- package/Types/CustomField/CustomFieldOrder.ts +55 -0
- package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
- package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
- package/Types/CustomField/CustomFieldType.ts +14 -0
- package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
- package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
- package/Types/Incident/IncidentAlertLink.ts +19 -0
- package/Types/SafeHtml.ts +93 -0
- package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
- package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
- package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
- package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
- package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
- package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
- package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
- package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
- package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
- package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
- package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
- package/Types/Telemetry/InventoryLiveness.ts +26 -0
- package/Types/Topology/TopologyApi.ts +428 -0
- package/Types/Topology/TopologyTypeRules.ts +191 -0
- package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
- package/UI/Components/Forms/ModelForm.tsx +70 -8
- package/UI/Components/Forms/Validation.ts +1 -1
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
- package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
- package/UI/Components/ModelTable/ModelTable.tsx +6 -7
- package/UI/Utils/SensitiveUrlToken.ts +10 -5
- package/Utils/API.ts +247 -2
- package/Utils/Incident/IncidentFromAlerts.ts +78 -0
- package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
- package/Utils/Markdown/MarkdownEscape.ts +65 -0
- package/Utils/ProbeNetworkFailureUtil.ts +673 -0
- package/build/dist/Models/DatabaseModels/Incident.js +267 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +100 -1
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +417 -176
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
- package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +21 -33
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +457 -3
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1148 -42
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +31 -0
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
- package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +84 -57
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js +2 -22
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +47 -9
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +110 -6
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +30 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +48 -6
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageResource.js +39 -7
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
- package/build/dist/Types/SafeHtml.js +84 -0
- package/build/dist/Types/SafeHtml.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
- package/build/dist/Types/Topology/TopologyApi.js +55 -0
- package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +1 -1
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
- package/build/dist/Utils/API.js +177 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
- package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
- package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import EventStatusPanel, {
|
|
2
2
|
ComponentProps,
|
|
3
|
+
EventPanelAction,
|
|
3
4
|
EventStateAction,
|
|
4
5
|
EventStateItem,
|
|
5
6
|
} from "../../../../App/FeatureSet/Dashboard/src/Components/EventView/EventStatusPanel";
|
|
@@ -19,6 +20,9 @@ import { Black, Red500 } from "../../../Types/BrandColors";
|
|
|
19
20
|
import Color from "../../../Types/Color";
|
|
20
21
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
21
22
|
|
|
23
|
+
// React's warning for list items without a (unique) key.
|
|
24
|
+
const KEY_WARNING_PATTERN: RegExp = /unique "key"|same key/;
|
|
25
|
+
|
|
22
26
|
jest.mock("react-i18next", () => {
|
|
23
27
|
return {
|
|
24
28
|
useTranslation: () => {
|
|
@@ -542,7 +546,7 @@ describe("EventStatusPanel disabled behavior", () => {
|
|
|
542
546
|
});
|
|
543
547
|
|
|
544
548
|
describe("EventStatusPanel action-group layout", () => {
|
|
545
|
-
test("labels one responsive wrapping group for every action control", () => {
|
|
549
|
+
test("labels one responsive wrapping group for every action control (compact layout)", () => {
|
|
546
550
|
renderPanel();
|
|
547
551
|
|
|
548
552
|
const group: HTMLElement = getActionGroup();
|
|
@@ -607,11 +611,53 @@ describe("EventStatusPanel header layouts", () => {
|
|
|
607
611
|
|
|
608
612
|
expect(identifier).toHaveTextContent("INC-42");
|
|
609
613
|
expect(identifier).toHaveClass("bg-gray-100", "uppercase");
|
|
610
|
-
expect(headerRow).toHaveClass("
|
|
614
|
+
expect(headerRow).toHaveClass("xl:items-start", "xl:justify-between");
|
|
611
615
|
expect(headerRow).toContainElement(group);
|
|
612
616
|
expect(headerRow).not.toContainElement(screen.getByTestId("pill"));
|
|
613
617
|
});
|
|
614
618
|
|
|
619
|
+
/*
|
|
620
|
+
* Beside a side menu, a header that went side by side from md squeezed the
|
|
621
|
+
* title to a few characters and stacked the actions one per row, so the
|
|
622
|
+
* titled header stacks the actions under the title until xl.
|
|
623
|
+
*/
|
|
624
|
+
test("stacks the actions under the title until xl, and puts them beside it only from xl", () => {
|
|
625
|
+
renderPanel({ title: "Database connection failures" });
|
|
626
|
+
|
|
627
|
+
const heading: HTMLElement = screen.getByRole("heading", {
|
|
628
|
+
level: 2,
|
|
629
|
+
name: "Database connection failures",
|
|
630
|
+
});
|
|
631
|
+
const headerRow: HTMLElement = (heading.parentElement as HTMLElement)
|
|
632
|
+
.parentElement as HTMLElement;
|
|
633
|
+
|
|
634
|
+
expect(headerRow).toHaveClass(
|
|
635
|
+
"flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between",
|
|
636
|
+
{ exact: true },
|
|
637
|
+
);
|
|
638
|
+
expect(headerRow).not.toHaveClass("md:flex-row");
|
|
639
|
+
expect(headerRow).not.toHaveClass("md:items-start");
|
|
640
|
+
expect(headerRow).not.toHaveClass("md:justify-between");
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
test("keeps the titled header's action group full width until xl", () => {
|
|
644
|
+
renderPanel({ title: "Database connection failures" });
|
|
645
|
+
|
|
646
|
+
const group: HTMLElement = getActionGroup();
|
|
647
|
+
|
|
648
|
+
expect(group).toHaveClass(
|
|
649
|
+
"flex w-full flex-wrap items-center justify-end gap-2 xl:w-auto xl:shrink-0",
|
|
650
|
+
{ exact: true },
|
|
651
|
+
);
|
|
652
|
+
expect(group).not.toHaveClass("md:w-auto");
|
|
653
|
+
expect(group).toHaveAttribute("aria-label", "Event actions");
|
|
654
|
+
expect(within(group).getAllByRole("button")).toEqual([
|
|
655
|
+
screen.getByRole("button", { name: "Acknowledge" }),
|
|
656
|
+
screen.getByRole("button", { name: "Resolve" }),
|
|
657
|
+
screen.getByRole("button", { name: "More actions" }),
|
|
658
|
+
]);
|
|
659
|
+
});
|
|
660
|
+
|
|
615
661
|
test("uses the compact inline metadata layout when no title is supplied", () => {
|
|
616
662
|
renderPanel();
|
|
617
663
|
|
|
@@ -628,6 +674,25 @@ describe("EventStatusPanel header layouts", () => {
|
|
|
628
674
|
expect(compactRow).toContainElement(screen.getByTestId("pill"));
|
|
629
675
|
});
|
|
630
676
|
|
|
677
|
+
// Only the titled header moved to xl; the compact layout is as it was.
|
|
678
|
+
test("keeps the compact layout side by side from md, with its action group full width until md", () => {
|
|
679
|
+
renderPanel();
|
|
680
|
+
|
|
681
|
+
const group: HTMLElement = getActionGroup();
|
|
682
|
+
const compactRow: HTMLElement = group.parentElement as HTMLElement;
|
|
683
|
+
|
|
684
|
+
expect(compactRow).toHaveClass(
|
|
685
|
+
"flex flex-col gap-3 px-4 py-4 sm:px-5 md:flex-row md:items-center md:justify-between",
|
|
686
|
+
{ exact: true },
|
|
687
|
+
);
|
|
688
|
+
expect(compactRow).not.toHaveClass("xl:flex-row");
|
|
689
|
+
expect(group).toHaveClass(
|
|
690
|
+
"flex w-full flex-wrap items-center justify-end gap-2 md:w-auto",
|
|
691
|
+
{ exact: true },
|
|
692
|
+
);
|
|
693
|
+
expect(group).not.toHaveClass("xl:w-auto");
|
|
694
|
+
});
|
|
695
|
+
|
|
631
696
|
test("falls back to black when hydrated current-state color is absent", () => {
|
|
632
697
|
renderPanel({
|
|
633
698
|
states: [
|
|
@@ -992,3 +1057,734 @@ describe("EventStatusPanel header facts", () => {
|
|
|
992
1057
|
expect(getActionGroup()).not.toContainElement(getFacts());
|
|
993
1058
|
});
|
|
994
1059
|
});
|
|
1060
|
+
|
|
1061
|
+
/*
|
|
1062
|
+
* Secondary actions are the header's non-state buttons - "Declare Incident"
|
|
1063
|
+
* on an alert is the first. They share the state buttons' row and size but
|
|
1064
|
+
* never their primary look, never reach onActionClick (which would open a
|
|
1065
|
+
* state-change modal for a state that does not exist), never change what the
|
|
1066
|
+
* "More actions" menu offers, and stay on screen when no state action is
|
|
1067
|
+
* left. A locked one explains itself through a hoverable, focusable wrapper,
|
|
1068
|
+
* because a disabled <button> cannot trigger a tooltip.
|
|
1069
|
+
*/
|
|
1070
|
+
describe("EventStatusPanel secondary actions", () => {
|
|
1071
|
+
const DECLARE_ID: string = "alert-declare-incident-btn";
|
|
1072
|
+
const DECLARE_LABEL: string = "Declare Incident";
|
|
1073
|
+
const LOCKED_REASON: string =
|
|
1074
|
+
"You do not have permission to create this Incident.";
|
|
1075
|
+
|
|
1076
|
+
type MakeSecondaryActionFunction = (
|
|
1077
|
+
overrides?: Partial<EventPanelAction> | undefined,
|
|
1078
|
+
) => EventPanelAction;
|
|
1079
|
+
|
|
1080
|
+
const makeSecondaryAction: MakeSecondaryActionFunction = (
|
|
1081
|
+
overrides?: Partial<EventPanelAction> | undefined,
|
|
1082
|
+
): EventPanelAction => {
|
|
1083
|
+
return {
|
|
1084
|
+
id: DECLARE_ID,
|
|
1085
|
+
label: DECLARE_LABEL,
|
|
1086
|
+
icon: IconProp.Alert,
|
|
1087
|
+
onClick: (): void => {},
|
|
1088
|
+
...overrides,
|
|
1089
|
+
};
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
type GetButtonFunction = (name: string) => HTMLElement;
|
|
1093
|
+
|
|
1094
|
+
const getButton: GetButtonFunction = (name: string): HTMLElement => {
|
|
1095
|
+
return screen.getByRole("button", { name: name });
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
type IsBeforeFunction = (first: Node, second: Node) => boolean;
|
|
1099
|
+
|
|
1100
|
+
const isBefore: IsBeforeFunction = (first: Node, second: Node): boolean => {
|
|
1101
|
+
return Boolean(
|
|
1102
|
+
first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
1103
|
+
);
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
afterEach(() => {
|
|
1107
|
+
jest.restoreAllMocks();
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
/*
|
|
1111
|
+
* First in this block on purpose: React reports a missing list key only
|
|
1112
|
+
* once per owner component, so the first render with secondary actions is
|
|
1113
|
+
* the one that would warn.
|
|
1114
|
+
*/
|
|
1115
|
+
test("keys each action by its id, so reordering moves rather than relabels them", () => {
|
|
1116
|
+
const consoleErrorSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
1117
|
+
console,
|
|
1118
|
+
"error",
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
const first: EventPanelAction = makeSecondaryAction({
|
|
1122
|
+
id: "first-secondary-btn",
|
|
1123
|
+
label: "First",
|
|
1124
|
+
});
|
|
1125
|
+
const second: EventPanelAction = makeSecondaryAction({
|
|
1126
|
+
id: "second-secondary-btn",
|
|
1127
|
+
label: "Second",
|
|
1128
|
+
isDisabled: true,
|
|
1129
|
+
tooltip: LOCKED_REASON,
|
|
1130
|
+
});
|
|
1131
|
+
const third: EventPanelAction = makeSecondaryAction({
|
|
1132
|
+
id: "third-secondary-btn",
|
|
1133
|
+
label: "Third",
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
const baseProps: ComponentProps = {
|
|
1137
|
+
states: defaultStates(),
|
|
1138
|
+
identifier: "ALR-12",
|
|
1139
|
+
currentStateId: "created",
|
|
1140
|
+
actions: defaultActions(),
|
|
1141
|
+
onActionClick: (): void => {},
|
|
1142
|
+
onStateSelect: (): void => {},
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
const { rerender } = render(
|
|
1146
|
+
<EventStatusPanel
|
|
1147
|
+
{...baseProps}
|
|
1148
|
+
secondaryActions={[first, second, third]}
|
|
1149
|
+
/>,
|
|
1150
|
+
);
|
|
1151
|
+
|
|
1152
|
+
const firstNode: HTMLElement = getButton("First");
|
|
1153
|
+
const secondWrapper: HTMLElement = screen.getByTestId(
|
|
1154
|
+
"second-secondary-btn-disabled-wrapper",
|
|
1155
|
+
);
|
|
1156
|
+
const thirdNode: HTMLElement = getButton("Third");
|
|
1157
|
+
|
|
1158
|
+
rerender(
|
|
1159
|
+
<EventStatusPanel
|
|
1160
|
+
{...baseProps}
|
|
1161
|
+
secondaryActions={[third, second, first]}
|
|
1162
|
+
/>,
|
|
1163
|
+
);
|
|
1164
|
+
|
|
1165
|
+
// Same DOM nodes, now in the new order.
|
|
1166
|
+
expect(getButton("First")).toBe(firstNode);
|
|
1167
|
+
expect(getButton("Third")).toBe(thirdNode);
|
|
1168
|
+
expect(screen.getByTestId("second-secondary-btn-disabled-wrapper")).toBe(
|
|
1169
|
+
secondWrapper,
|
|
1170
|
+
);
|
|
1171
|
+
expect(firstNode).toHaveAttribute("id", "first-secondary-btn");
|
|
1172
|
+
expect(thirdNode).toHaveAttribute("id", "third-secondary-btn");
|
|
1173
|
+
expect(isBefore(thirdNode, secondWrapper)).toBe(true);
|
|
1174
|
+
expect(isBefore(secondWrapper, firstNode)).toBe(true);
|
|
1175
|
+
|
|
1176
|
+
const keyWarnings: Array<unknown> = consoleErrorSpy.mock.calls.filter(
|
|
1177
|
+
(call: Array<unknown>): boolean => {
|
|
1178
|
+
return KEY_WARNING_PATTERN.test(String(call[0]));
|
|
1179
|
+
},
|
|
1180
|
+
);
|
|
1181
|
+
|
|
1182
|
+
expect(keyWarnings).toEqual([]);
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
test("renders after the state buttons and before the More actions trigger", () => {
|
|
1186
|
+
renderPanel({ secondaryActions: [makeSecondaryAction()] });
|
|
1187
|
+
|
|
1188
|
+
const group: HTMLElement = getActionGroup();
|
|
1189
|
+
const acknowledge: HTMLElement = within(group).getByRole("button", {
|
|
1190
|
+
name: "Acknowledge",
|
|
1191
|
+
});
|
|
1192
|
+
const resolve: HTMLElement = within(group).getByRole("button", {
|
|
1193
|
+
name: "Resolve",
|
|
1194
|
+
});
|
|
1195
|
+
const declare: HTMLElement = within(group).getByRole("button", {
|
|
1196
|
+
name: DECLARE_LABEL,
|
|
1197
|
+
});
|
|
1198
|
+
const more: HTMLElement = within(group).getByRole("button", {
|
|
1199
|
+
name: "More actions",
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
expect(within(group).getAllByRole("button")).toEqual([
|
|
1203
|
+
acknowledge,
|
|
1204
|
+
resolve,
|
|
1205
|
+
declare,
|
|
1206
|
+
more,
|
|
1207
|
+
]);
|
|
1208
|
+
// A direct child of the group, like the state buttons - not nested.
|
|
1209
|
+
expect(declare.parentElement).toBe(group);
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
test("is a native button with its id and label as title", () => {
|
|
1213
|
+
renderPanel({ secondaryActions: [makeSecondaryAction()] });
|
|
1214
|
+
|
|
1215
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1216
|
+
|
|
1217
|
+
expect(declare.tagName).toBe("BUTTON");
|
|
1218
|
+
expect(declare).toHaveAttribute("type", "button");
|
|
1219
|
+
expect(declare).toHaveAttribute("id", DECLARE_ID);
|
|
1220
|
+
expect(declare).toHaveAttribute("title", DECLARE_LABEL);
|
|
1221
|
+
expect(declare).toBeEnabled();
|
|
1222
|
+
expect(declare).toHaveAttribute("aria-disabled", "false");
|
|
1223
|
+
expect(declare.querySelector("span")).toHaveClass("truncate");
|
|
1224
|
+
expect(declare.querySelector("span")).toHaveTextContent(DECLARE_LABEL);
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
test("uses the state buttons' base classes with the neutral, never the indigo, look", () => {
|
|
1228
|
+
renderPanel({ secondaryActions: [makeSecondaryAction()] });
|
|
1229
|
+
|
|
1230
|
+
const acknowledge: HTMLElement = getButton("Acknowledge");
|
|
1231
|
+
// DANGER renders neutral, so Resolve is the neutral reference.
|
|
1232
|
+
const resolve: HTMLElement = getButton("Resolve");
|
|
1233
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1234
|
+
|
|
1235
|
+
const primaryVariantClasses: Array<string> = [
|
|
1236
|
+
"border-indigo-600",
|
|
1237
|
+
"bg-indigo-600",
|
|
1238
|
+
"text-white",
|
|
1239
|
+
"hover:border-indigo-700",
|
|
1240
|
+
"hover:bg-indigo-700",
|
|
1241
|
+
];
|
|
1242
|
+
const baseClasses: Array<string> = acknowledge.className
|
|
1243
|
+
.split(/\s+/)
|
|
1244
|
+
.filter((className: string): boolean => {
|
|
1245
|
+
return (
|
|
1246
|
+
className.length > 0 && !primaryVariantClasses.includes(className)
|
|
1247
|
+
);
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
expect(baseClasses).toEqual(
|
|
1251
|
+
expect.arrayContaining(["inline-flex", "h-9", "min-w-[7rem]"]),
|
|
1252
|
+
);
|
|
1253
|
+
expect(declare).toHaveClass(...baseClasses);
|
|
1254
|
+
expect(declare.className).toBe(resolve.className);
|
|
1255
|
+
expect(declare).toHaveClass(
|
|
1256
|
+
"border-gray-300",
|
|
1257
|
+
"bg-white",
|
|
1258
|
+
"text-gray-700",
|
|
1259
|
+
"hover:bg-gray-50",
|
|
1260
|
+
);
|
|
1261
|
+
expect(declare).not.toHaveClass(
|
|
1262
|
+
"bg-indigo-600",
|
|
1263
|
+
"border-indigo-600",
|
|
1264
|
+
"text-white",
|
|
1265
|
+
"pointer-events-none",
|
|
1266
|
+
"w-full",
|
|
1267
|
+
);
|
|
1268
|
+
expect(declare).not.toHaveAttribute("style");
|
|
1269
|
+
|
|
1270
|
+
// The state action stays the one primary button in the row.
|
|
1271
|
+
const primaryButtons: Array<HTMLElement> = within(getActionGroup())
|
|
1272
|
+
.getAllByRole("button")
|
|
1273
|
+
.filter((button: HTMLElement): boolean => {
|
|
1274
|
+
return button.classList.contains("bg-indigo-600");
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
expect(primaryButtons).toEqual([acknowledge]);
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
test("translates its label like the state labels", () => {
|
|
1281
|
+
renderPanel({
|
|
1282
|
+
secondaryActions: [
|
|
1283
|
+
makeSecondaryAction({ label: "Resolve translation key" }),
|
|
1284
|
+
],
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
const translated: HTMLElement = getButton("Résoudre");
|
|
1288
|
+
|
|
1289
|
+
expect(translated).toHaveAttribute("id", DECLARE_ID);
|
|
1290
|
+
expect(translated).toHaveAttribute("title", "Résoudre");
|
|
1291
|
+
expect(
|
|
1292
|
+
screen.queryByRole("button", { name: "Resolve translation key" }),
|
|
1293
|
+
).not.toBeInTheDocument();
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
test("renders its icon at the state buttons' icon size, and none when it has no icon", () => {
|
|
1297
|
+
renderPanel({
|
|
1298
|
+
secondaryActions: [
|
|
1299
|
+
makeSecondaryAction(),
|
|
1300
|
+
makeSecondaryAction({
|
|
1301
|
+
id: "iconless-secondary-btn",
|
|
1302
|
+
label: "Iconless",
|
|
1303
|
+
icon: undefined,
|
|
1304
|
+
}),
|
|
1305
|
+
],
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
const declareIcon: SVGElement | null =
|
|
1309
|
+
getButton(DECLARE_LABEL).querySelector("svg");
|
|
1310
|
+
const acknowledgeIcon: SVGElement | null =
|
|
1311
|
+
getButton("Acknowledge").querySelector("svg");
|
|
1312
|
+
|
|
1313
|
+
expect(declareIcon).not.toBeNull();
|
|
1314
|
+
expect(declareIcon).toHaveClass("h-4", "w-4", "shrink-0");
|
|
1315
|
+
expect(declareIcon?.innerHTML).not.toEqual(acknowledgeIcon?.innerHTML);
|
|
1316
|
+
expect(getButton("Iconless").querySelector("svg")).toBeNull();
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
test("calls its own onClick exactly once and never the state callbacks", () => {
|
|
1320
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1321
|
+
const rendered: RenderedPanel = renderPanel({
|
|
1322
|
+
secondaryActions: [makeSecondaryAction({ onClick: onDeclare })],
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
fireEvent.click(getButton(DECLARE_LABEL));
|
|
1326
|
+
|
|
1327
|
+
expect(onDeclare).toHaveBeenCalledTimes(1);
|
|
1328
|
+
expect(onDeclare).toHaveBeenCalledWith();
|
|
1329
|
+
expect(rendered.actionClicks).toEqual([]);
|
|
1330
|
+
expect(rendered.stateSelections).toEqual([]);
|
|
1331
|
+
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
|
|
1332
|
+
|
|
1333
|
+
// The state buttons beside it still report their own states only.
|
|
1334
|
+
fireEvent.click(getButton("Acknowledge"));
|
|
1335
|
+
|
|
1336
|
+
expect(rendered.actionClicks).toEqual(["acknowledged"]);
|
|
1337
|
+
expect(onDeclare).toHaveBeenCalledTimes(1);
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
test("never reaches onActionClick even when its id matches a state id", () => {
|
|
1341
|
+
const onClick: MockFunction = getJestMockFunction();
|
|
1342
|
+
const rendered: RenderedPanel = renderPanel({
|
|
1343
|
+
secondaryActions: [
|
|
1344
|
+
makeSecondaryAction({
|
|
1345
|
+
id: "resolved",
|
|
1346
|
+
label: "Look-alike",
|
|
1347
|
+
onClick: onClick,
|
|
1348
|
+
}),
|
|
1349
|
+
],
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
fireEvent.click(getButton("Look-alike"));
|
|
1353
|
+
|
|
1354
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
1355
|
+
expect(rendered.actionClicks).toEqual([]);
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
test("leaves the More actions menu exactly as it was", () => {
|
|
1359
|
+
renderPanel();
|
|
1360
|
+
|
|
1361
|
+
const withoutSecondary: Array<string> = menuChoiceNames(openMenu());
|
|
1362
|
+
|
|
1363
|
+
cleanup();
|
|
1364
|
+
|
|
1365
|
+
renderPanel({ secondaryActions: [makeSecondaryAction()] });
|
|
1366
|
+
|
|
1367
|
+
const menu: HTMLElement = openMenu();
|
|
1368
|
+
|
|
1369
|
+
expect(withoutSecondary).toEqual(["Investigating"]);
|
|
1370
|
+
expect(menuChoiceNames(menu)).toEqual(withoutSecondary);
|
|
1371
|
+
expect(within(menu).queryByText(DECLARE_LABEL)).not.toBeInTheDocument();
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
test.each(["investigating", "acknowledged", "resolved", "created"])(
|
|
1375
|
+
"does not hide the %s state from the menu when an action shares its id",
|
|
1376
|
+
(sharedId: string) => {
|
|
1377
|
+
renderPanel({
|
|
1378
|
+
actions: [],
|
|
1379
|
+
secondaryActions: [
|
|
1380
|
+
makeSecondaryAction({ id: sharedId, label: "Look-alike" }),
|
|
1381
|
+
],
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
expect(menuChoiceNames(openMenu())).toEqual([
|
|
1385
|
+
"Acknowledged",
|
|
1386
|
+
"Investigating",
|
|
1387
|
+
"Resolved",
|
|
1388
|
+
]);
|
|
1389
|
+
},
|
|
1390
|
+
);
|
|
1391
|
+
|
|
1392
|
+
test("reports a menu selection as a state change, not a secondary click", () => {
|
|
1393
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1394
|
+
const rendered: RenderedPanel = renderPanel({
|
|
1395
|
+
secondaryActions: [makeSecondaryAction({ onClick: onDeclare })],
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
fireEvent.click(getMenuChoice(openMenu(), "Investigating"));
|
|
1399
|
+
|
|
1400
|
+
expect(rendered.stateSelections).toEqual(["investigating"]);
|
|
1401
|
+
expect(onDeclare).not.toHaveBeenCalled();
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1404
|
+
test("does not conjure a More actions menu when every other state already has a button", () => {
|
|
1405
|
+
renderPanel({
|
|
1406
|
+
states: [
|
|
1407
|
+
makeState("created", "Created"),
|
|
1408
|
+
makeState("acknowledged", "Acknowledged"),
|
|
1409
|
+
makeState("resolved", "Resolved"),
|
|
1410
|
+
],
|
|
1411
|
+
secondaryActions: [makeSecondaryAction()],
|
|
1412
|
+
});
|
|
1413
|
+
|
|
1414
|
+
expect(
|
|
1415
|
+
screen.queryByRole("button", { name: "More actions" }),
|
|
1416
|
+
).not.toBeInTheDocument();
|
|
1417
|
+
expect(within(getActionGroup()).getAllByRole("button")).toEqual([
|
|
1418
|
+
getButton("Acknowledge"),
|
|
1419
|
+
getButton("Resolve"),
|
|
1420
|
+
getButton(DECLARE_LABEL),
|
|
1421
|
+
]);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
test("stays on a resolved event, where it is the only button", () => {
|
|
1425
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1426
|
+
|
|
1427
|
+
renderPanel({
|
|
1428
|
+
currentStateId: "resolved",
|
|
1429
|
+
actions: [],
|
|
1430
|
+
secondaryActions: [makeSecondaryAction({ onClick: onDeclare })],
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
const group: HTMLElement = getActionGroup();
|
|
1434
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1435
|
+
|
|
1436
|
+
expect(within(group).getAllByRole("button")).toEqual([declare]);
|
|
1437
|
+
expect(declare).toBeEnabled();
|
|
1438
|
+
|
|
1439
|
+
fireEvent.click(declare);
|
|
1440
|
+
|
|
1441
|
+
expect(onDeclare).toHaveBeenCalledTimes(1);
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
test("renders even when the panel has no states at all", () => {
|
|
1445
|
+
render(
|
|
1446
|
+
<EventStatusPanel
|
|
1447
|
+
title="Database latency"
|
|
1448
|
+
states={[]}
|
|
1449
|
+
actions={[]}
|
|
1450
|
+
onActionClick={() => {}}
|
|
1451
|
+
secondaryActions={[makeSecondaryAction()]}
|
|
1452
|
+
/>,
|
|
1453
|
+
);
|
|
1454
|
+
|
|
1455
|
+
expect(within(getActionGroup()).getAllByRole("button")).toEqual([
|
|
1456
|
+
getButton(DECLARE_LABEL),
|
|
1457
|
+
]);
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
test.each([
|
|
1461
|
+
["undefined", undefined],
|
|
1462
|
+
["an empty list", []],
|
|
1463
|
+
])(
|
|
1464
|
+
"renders nothing extra when secondaryActions is %s",
|
|
1465
|
+
(_label: string, secondaryActions: Array<EventPanelAction> | undefined) => {
|
|
1466
|
+
renderPanel({ secondaryActions: secondaryActions });
|
|
1467
|
+
|
|
1468
|
+
const group: HTMLElement = getActionGroup();
|
|
1469
|
+
|
|
1470
|
+
expect(within(group).getAllByRole("button")).toEqual([
|
|
1471
|
+
getButton("Acknowledge"),
|
|
1472
|
+
getButton("Resolve"),
|
|
1473
|
+
getMoreActionsTrigger(),
|
|
1474
|
+
]);
|
|
1475
|
+
expect(document.getElementById(DECLARE_ID)).toBeNull();
|
|
1476
|
+
expect(
|
|
1477
|
+
document.querySelector('[data-testid$="-disabled-wrapper"]'),
|
|
1478
|
+
).toBeNull();
|
|
1479
|
+
},
|
|
1480
|
+
);
|
|
1481
|
+
|
|
1482
|
+
test("is disabled with the rest of the panel, and a click does nothing", () => {
|
|
1483
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1484
|
+
|
|
1485
|
+
renderPanel({
|
|
1486
|
+
isDisabled: true,
|
|
1487
|
+
secondaryActions: [makeSecondaryAction({ onClick: onDeclare })],
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1491
|
+
|
|
1492
|
+
expect(declare).toBeDisabled();
|
|
1493
|
+
expect(declare).toHaveAttribute("aria-disabled", "true");
|
|
1494
|
+
// No reason to show, so no wrapper and the label stays as the title.
|
|
1495
|
+
expect(screen.queryByTestId(`${DECLARE_ID}-disabled-wrapper`)).toBeNull();
|
|
1496
|
+
expect(declare).toHaveAttribute("title", DECLARE_LABEL);
|
|
1497
|
+
|
|
1498
|
+
fireEvent.click(declare);
|
|
1499
|
+
|
|
1500
|
+
expect(onDeclare).not.toHaveBeenCalled();
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1503
|
+
test("gets the tooltip wrapper when the panel is disabled and the action carries a reason", () => {
|
|
1504
|
+
renderPanel({
|
|
1505
|
+
isDisabled: true,
|
|
1506
|
+
secondaryActions: [makeSecondaryAction({ tooltip: LOCKED_REASON })],
|
|
1507
|
+
});
|
|
1508
|
+
|
|
1509
|
+
const wrapper: HTMLElement = screen.getByTestId(
|
|
1510
|
+
`${DECLARE_ID}-disabled-wrapper`,
|
|
1511
|
+
);
|
|
1512
|
+
|
|
1513
|
+
expect(wrapper).toContainElement(getButton(DECLARE_LABEL));
|
|
1514
|
+
expect(getButton(DECLARE_LABEL)).toBeDisabled();
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
test.each([
|
|
1518
|
+
["no tooltip", undefined],
|
|
1519
|
+
["an empty tooltip", ""],
|
|
1520
|
+
])(
|
|
1521
|
+
"a disabled action with %s is disabled in place, with no wrapper",
|
|
1522
|
+
(_label: string, tooltip: string | undefined) => {
|
|
1523
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1524
|
+
const rendered: RenderedPanel = renderPanel({
|
|
1525
|
+
secondaryActions: [
|
|
1526
|
+
makeSecondaryAction({
|
|
1527
|
+
isDisabled: true,
|
|
1528
|
+
tooltip: tooltip,
|
|
1529
|
+
onClick: onDeclare,
|
|
1530
|
+
}),
|
|
1531
|
+
],
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1535
|
+
|
|
1536
|
+
expect(declare).toBeDisabled();
|
|
1537
|
+
expect(declare).toHaveAttribute("aria-disabled", "true");
|
|
1538
|
+
expect(declare).toHaveAttribute("title", DECLARE_LABEL);
|
|
1539
|
+
expect(declare).not.toHaveClass("pointer-events-none", "w-full");
|
|
1540
|
+
expect(declare.parentElement).toBe(getActionGroup());
|
|
1541
|
+
expect(screen.queryByTestId(`${DECLARE_ID}-disabled-wrapper`)).toBeNull();
|
|
1542
|
+
|
|
1543
|
+
fireEvent.click(declare);
|
|
1544
|
+
|
|
1545
|
+
expect(onDeclare).not.toHaveBeenCalled();
|
|
1546
|
+
// Only this action is locked; the state controls still work.
|
|
1547
|
+
expect(getButton("Acknowledge")).toBeEnabled();
|
|
1548
|
+
expect(getButton("Resolve")).toBeEnabled();
|
|
1549
|
+
expect(getMoreActionsTrigger()).toBeEnabled();
|
|
1550
|
+
|
|
1551
|
+
fireEvent.click(getButton("Resolve"));
|
|
1552
|
+
|
|
1553
|
+
expect(rendered.actionClicks).toEqual(["resolved"]);
|
|
1554
|
+
},
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1557
|
+
test("a disabled action with a reason hands the pointer and focus to a wrapper", () => {
|
|
1558
|
+
renderPanel({
|
|
1559
|
+
secondaryActions: [
|
|
1560
|
+
makeSecondaryAction({ isDisabled: true, tooltip: LOCKED_REASON }),
|
|
1561
|
+
],
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
const group: HTMLElement = getActionGroup();
|
|
1565
|
+
const wrapper: HTMLElement = screen.getByTestId(
|
|
1566
|
+
`${DECLARE_ID}-disabled-wrapper`,
|
|
1567
|
+
);
|
|
1568
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1569
|
+
|
|
1570
|
+
expect(wrapper.tagName).toBe("SPAN");
|
|
1571
|
+
expect(wrapper).toHaveAttribute("tabindex", "0");
|
|
1572
|
+
expect(wrapper.tabIndex).toBe(0);
|
|
1573
|
+
expect(wrapper).toHaveClass(
|
|
1574
|
+
"inline-flex",
|
|
1575
|
+
"min-w-[7rem]",
|
|
1576
|
+
"max-w-full",
|
|
1577
|
+
"flex-auto",
|
|
1578
|
+
"rounded-md",
|
|
1579
|
+
"sm:max-w-64",
|
|
1580
|
+
"sm:flex-none",
|
|
1581
|
+
);
|
|
1582
|
+
expect(declare.parentElement).toBe(wrapper);
|
|
1583
|
+
expect(wrapper.parentElement).toBe(group);
|
|
1584
|
+
|
|
1585
|
+
// Taken out of hit-testing so the wrapper receives the hover.
|
|
1586
|
+
expect(declare).toHaveClass("pointer-events-none", "w-full");
|
|
1587
|
+
// The tooltip carries the message; a native title would compete with it.
|
|
1588
|
+
expect(declare).not.toHaveAttribute("title");
|
|
1589
|
+
expect(declare).toBeDisabled();
|
|
1590
|
+
expect(declare).toHaveAttribute("aria-disabled", "true");
|
|
1591
|
+
// Still the neutral look.
|
|
1592
|
+
expect(declare).toHaveClass("border-gray-300", "bg-white", "text-gray-700");
|
|
1593
|
+
expect(declare).not.toHaveClass("bg-indigo-600");
|
|
1594
|
+
|
|
1595
|
+
// Still between the state buttons and the menu.
|
|
1596
|
+
expect(isBefore(getButton("Resolve"), wrapper)).toBe(true);
|
|
1597
|
+
expect(isBefore(wrapper, getMoreActionsTrigger())).toBe(true);
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1600
|
+
test("shows the reason when the wrapper is hovered", () => {
|
|
1601
|
+
renderPanel({
|
|
1602
|
+
secondaryActions: [
|
|
1603
|
+
makeSecondaryAction({ isDisabled: true, tooltip: LOCKED_REASON }),
|
|
1604
|
+
],
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
|
|
1608
|
+
|
|
1609
|
+
fireEvent.mouseEnter(screen.getByTestId(`${DECLARE_ID}-disabled-wrapper`));
|
|
1610
|
+
|
|
1611
|
+
expect(screen.getByRole("tooltip")).toHaveTextContent(LOCKED_REASON);
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
test("shows the reason when the wrapper is focused from the keyboard", () => {
|
|
1615
|
+
renderPanel({
|
|
1616
|
+
secondaryActions: [
|
|
1617
|
+
makeSecondaryAction({ isDisabled: true, tooltip: LOCKED_REASON }),
|
|
1618
|
+
],
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
const wrapper: HTMLElement = screen.getByTestId(
|
|
1622
|
+
`${DECLARE_ID}-disabled-wrapper`,
|
|
1623
|
+
);
|
|
1624
|
+
|
|
1625
|
+
wrapper.focus();
|
|
1626
|
+
|
|
1627
|
+
expect(document.activeElement).toBe(wrapper);
|
|
1628
|
+
|
|
1629
|
+
fireEvent.focus(wrapper);
|
|
1630
|
+
|
|
1631
|
+
expect(screen.getByRole("tooltip")).toHaveTextContent(LOCKED_REASON);
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
test("translates the reason like the label", () => {
|
|
1635
|
+
renderPanel({
|
|
1636
|
+
secondaryActions: [
|
|
1637
|
+
makeSecondaryAction({
|
|
1638
|
+
isDisabled: true,
|
|
1639
|
+
tooltip: "Resolve translation key",
|
|
1640
|
+
}),
|
|
1641
|
+
],
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
fireEvent.mouseEnter(screen.getByTestId(`${DECLARE_ID}-disabled-wrapper`));
|
|
1645
|
+
|
|
1646
|
+
expect(screen.getByRole("tooltip")).toHaveTextContent("Résoudre");
|
|
1647
|
+
expect(screen.getByRole("tooltip")).not.toHaveTextContent(
|
|
1648
|
+
"Resolve translation key",
|
|
1649
|
+
);
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
test("a click on a locked action with a reason does nothing", () => {
|
|
1653
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1654
|
+
const rendered: RenderedPanel = renderPanel({
|
|
1655
|
+
secondaryActions: [
|
|
1656
|
+
makeSecondaryAction({
|
|
1657
|
+
isDisabled: true,
|
|
1658
|
+
tooltip: LOCKED_REASON,
|
|
1659
|
+
onClick: onDeclare,
|
|
1660
|
+
}),
|
|
1661
|
+
],
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
fireEvent.click(screen.getByTestId(`${DECLARE_ID}-disabled-wrapper`));
|
|
1665
|
+
fireEvent.click(getButton(DECLARE_LABEL));
|
|
1666
|
+
|
|
1667
|
+
expect(onDeclare).not.toHaveBeenCalled();
|
|
1668
|
+
expect(rendered.actionClicks).toEqual([]);
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
test("an enabled action ignores its tooltip and gets no wrapper", () => {
|
|
1672
|
+
renderPanel({
|
|
1673
|
+
secondaryActions: [makeSecondaryAction({ tooltip: LOCKED_REASON })],
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1677
|
+
|
|
1678
|
+
expect(screen.queryByTestId(`${DECLARE_ID}-disabled-wrapper`)).toBeNull();
|
|
1679
|
+
expect(declare.parentElement).toBe(getActionGroup());
|
|
1680
|
+
expect(declare).toBeEnabled();
|
|
1681
|
+
expect(declare).toHaveAttribute("title", DECLARE_LABEL);
|
|
1682
|
+
expect(declare).not.toHaveClass("pointer-events-none");
|
|
1683
|
+
});
|
|
1684
|
+
|
|
1685
|
+
test.each([
|
|
1686
|
+
["titled", "Database latency"],
|
|
1687
|
+
["compact", undefined],
|
|
1688
|
+
])("works in the %s layout", (_layout: string, title: string | undefined) => {
|
|
1689
|
+
const onDeclare: MockFunction = getJestMockFunction();
|
|
1690
|
+
|
|
1691
|
+
renderPanel({
|
|
1692
|
+
title: title,
|
|
1693
|
+
secondaryActions: [
|
|
1694
|
+
makeSecondaryAction({ onClick: onDeclare }),
|
|
1695
|
+
makeSecondaryAction({
|
|
1696
|
+
id: "locked-secondary-btn",
|
|
1697
|
+
label: "Locked",
|
|
1698
|
+
isDisabled: true,
|
|
1699
|
+
tooltip: LOCKED_REASON,
|
|
1700
|
+
}),
|
|
1701
|
+
],
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
if (title) {
|
|
1705
|
+
expect(
|
|
1706
|
+
screen.getByRole("heading", { level: 2, name: title }),
|
|
1707
|
+
).toBeInTheDocument();
|
|
1708
|
+
} else {
|
|
1709
|
+
expect(
|
|
1710
|
+
screen.queryByRole("heading", { level: 2 }),
|
|
1711
|
+
).not.toBeInTheDocument();
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
const group: HTMLElement = getActionGroup();
|
|
1715
|
+
const declare: HTMLElement = getButton(DECLARE_LABEL);
|
|
1716
|
+
const locked: HTMLElement = getButton("Locked");
|
|
1717
|
+
|
|
1718
|
+
expect(within(group).getAllByRole("button")).toEqual([
|
|
1719
|
+
getButton("Acknowledge"),
|
|
1720
|
+
getButton("Resolve"),
|
|
1721
|
+
declare,
|
|
1722
|
+
locked,
|
|
1723
|
+
getMoreActionsTrigger(),
|
|
1724
|
+
]);
|
|
1725
|
+
expect(
|
|
1726
|
+
within(group).getByTestId("locked-secondary-btn-disabled-wrapper"),
|
|
1727
|
+
).toContainElement(locked);
|
|
1728
|
+
|
|
1729
|
+
fireEvent.click(declare);
|
|
1730
|
+
|
|
1731
|
+
expect(onDeclare).toHaveBeenCalledTimes(1);
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
test("keeps several secondary actions in caller order, each with its own handler", () => {
|
|
1735
|
+
const onFirst: MockFunction = getJestMockFunction();
|
|
1736
|
+
const onSecond: MockFunction = getJestMockFunction();
|
|
1737
|
+
const onThird: MockFunction = getJestMockFunction();
|
|
1738
|
+
|
|
1739
|
+
const rendered: RenderedPanel = renderPanel({
|
|
1740
|
+
secondaryActions: [
|
|
1741
|
+
makeSecondaryAction({
|
|
1742
|
+
id: "first-secondary-btn",
|
|
1743
|
+
label: "First",
|
|
1744
|
+
onClick: onFirst,
|
|
1745
|
+
}),
|
|
1746
|
+
makeSecondaryAction({
|
|
1747
|
+
id: "second-secondary-btn",
|
|
1748
|
+
label: "Second",
|
|
1749
|
+
onClick: onSecond,
|
|
1750
|
+
isDisabled: true,
|
|
1751
|
+
tooltip: LOCKED_REASON,
|
|
1752
|
+
}),
|
|
1753
|
+
makeSecondaryAction({
|
|
1754
|
+
id: "third-secondary-btn",
|
|
1755
|
+
label: "Third",
|
|
1756
|
+
onClick: onThird,
|
|
1757
|
+
}),
|
|
1758
|
+
],
|
|
1759
|
+
});
|
|
1760
|
+
|
|
1761
|
+
const group: HTMLElement = getActionGroup();
|
|
1762
|
+
const first: HTMLElement = getButton("First");
|
|
1763
|
+
const second: HTMLElement = getButton("Second");
|
|
1764
|
+
const third: HTMLElement = getButton("Third");
|
|
1765
|
+
|
|
1766
|
+
expect(within(group).getAllByRole("button")).toEqual([
|
|
1767
|
+
getButton("Acknowledge"),
|
|
1768
|
+
getButton("Resolve"),
|
|
1769
|
+
first,
|
|
1770
|
+
second,
|
|
1771
|
+
third,
|
|
1772
|
+
getMoreActionsTrigger(),
|
|
1773
|
+
]);
|
|
1774
|
+
expect(first).toHaveAttribute("id", "first-secondary-btn");
|
|
1775
|
+
expect(second).toHaveAttribute("id", "second-secondary-btn");
|
|
1776
|
+
expect(third).toHaveAttribute("id", "third-secondary-btn");
|
|
1777
|
+
|
|
1778
|
+
fireEvent.click(third);
|
|
1779
|
+
fireEvent.click(second);
|
|
1780
|
+
fireEvent.click(first);
|
|
1781
|
+
|
|
1782
|
+
expect(onFirst).toHaveBeenCalledTimes(1);
|
|
1783
|
+
expect(onSecond).not.toHaveBeenCalled();
|
|
1784
|
+
expect(onThird).toHaveBeenCalledTimes(1);
|
|
1785
|
+
expect(onThird.mock.invocationCallOrder[0]!).toBeLessThan(
|
|
1786
|
+
onFirst.mock.invocationCallOrder[0]!,
|
|
1787
|
+
);
|
|
1788
|
+
expect(rendered.actionClicks).toEqual([]);
|
|
1789
|
+
});
|
|
1790
|
+
});
|