@oneuptime/common 14.0.7 → 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/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/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 +14 -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/DatabaseService.ts +93 -10
- 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 +1535 -65
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +68 -18
- 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/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -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/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/CustomFieldsSettingsIncidentOptions.test.tsx +357 -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 +242 -5
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- 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/PublicNotePagesNotifyDefault.test.tsx +113 -1
- 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 +14 -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/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.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 +1052 -0
- 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/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
- 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/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/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/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/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/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/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/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/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
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import {
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeEach,
|
|
5
|
+
describe,
|
|
6
|
+
expect,
|
|
7
|
+
jest,
|
|
8
|
+
test,
|
|
9
|
+
} from "@jest/globals";
|
|
10
|
+
import {
|
|
11
|
+
act,
|
|
12
|
+
cleanup,
|
|
13
|
+
fireEvent,
|
|
14
|
+
render,
|
|
15
|
+
screen,
|
|
16
|
+
waitFor,
|
|
17
|
+
} from "@testing-library/react";
|
|
18
|
+
import userEvent from "@testing-library/user-event";
|
|
19
|
+
import { SpyInstance } from "jest-mock";
|
|
20
|
+
import React from "react";
|
|
21
|
+
import { MemoryRouter } from "react-router-dom";
|
|
22
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
23
|
+
import { RevenueEventName } from "../../../Types/Analytics/RevenueEvent";
|
|
24
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
25
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
26
|
+
import * as UIConfig from "../../../UI/Config";
|
|
27
|
+
import API from "../../../UI/Utils/API/API";
|
|
28
|
+
import UiAnalytics from "../../../UI/Utils/Analytics";
|
|
29
|
+
import LoginUtil from "../../../UI/Utils/Login";
|
|
30
|
+
import ModelAPI, {
|
|
31
|
+
ModelAPIHttpResponse,
|
|
32
|
+
} from "../../../UI/Utils/ModelAPI/ModelAPI";
|
|
33
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
34
|
+
import UserUtil from "../../../UI/Utils/User";
|
|
35
|
+
import "../../../../App/FeatureSet/Accounts/src/Utils/i18n";
|
|
36
|
+
import RegisterPage from "../../../../App/FeatureSet/Accounts/src/Pages/Register";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* A hosted signup that ends on "verify your email", driven through the real
|
|
40
|
+
* Register -> ModelForm -> BasicForm stack with only the network, analytics
|
|
41
|
+
* and navigation mocked.
|
|
42
|
+
*
|
|
43
|
+
* On the hosted service /signup creates the account but starts no session:
|
|
44
|
+
* it answers `emailVerificationRequired` and, when it could mint one, a
|
|
45
|
+
* resend token plus how long until the first resend is allowed. The page has
|
|
46
|
+
* to (a) not log anybody in, (b) show the address the link went to, and
|
|
47
|
+
* (c) wire the resend button to that token with that wait. And when the
|
|
48
|
+
* address was mistyped, "Sign up with a different email" has to bring the
|
|
49
|
+
* form back filled in -- minus the secrets -- without counting the visitor
|
|
50
|
+
* twice in the signup funnel.
|
|
51
|
+
*
|
|
52
|
+
* TYPING AN EMAIL. RegisterPassword.test.tsx notes that the shared Email
|
|
53
|
+
* input loses synthetic paste events in jsdom (user-event cannot place a
|
|
54
|
+
* selection in an <input type="email">), which is why it prefills the
|
|
55
|
+
* address through ?email=. A change event carries the value just fine, so
|
|
56
|
+
* the flows below that need an editable address set it with
|
|
57
|
+
* fireEvent.change; everything else is typed or pasted as a person would.
|
|
58
|
+
*
|
|
59
|
+
* THE CLOCK. The form is filled on the real clock; the fake one is switched
|
|
60
|
+
* on just before submitting, so the resend countdown that starts with the
|
|
61
|
+
* verification screen only moves when a test advances it -- and never ticks
|
|
62
|
+
* outside act() while a test is still looking at the page.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
jest.mock("../../../UI/Config", () => {
|
|
66
|
+
return {
|
|
67
|
+
...(jest.requireActual(
|
|
68
|
+
"../../../UI/Config",
|
|
69
|
+
) as typeof import("../../../UI/Config")),
|
|
70
|
+
__esModule: true,
|
|
71
|
+
BILLING_ENABLED: true,
|
|
72
|
+
CAPTCHA_ENABLED: false,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const EMAIL: string = "ada@example.com";
|
|
77
|
+
const TYPO_EMAIL: string = "ada@exmaple.com";
|
|
78
|
+
const NAME: string = "Ada Lovelace";
|
|
79
|
+
const COMPANY: string = "Analytical Engines";
|
|
80
|
+
const PHONE: string = "+14155552671";
|
|
81
|
+
const PASSWORD: string = "lantern river meadow";
|
|
82
|
+
const RESEND_TOKEN: string =
|
|
83
|
+
"v1.eyJ2IjoxLCJ1IjoiMzMzMyJ9.c2lnbmF0dXJlLXNpZ25hdHVyZS1zaWduYXR1cmUtc2ln";
|
|
84
|
+
|
|
85
|
+
// Straight out of en.json.
|
|
86
|
+
const TITLE: string = "Verify your email to finish signing up";
|
|
87
|
+
const SPAM_HINT: string =
|
|
88
|
+
"Can't find it? Check your spam or junk folder. The link expires in 24 hours.";
|
|
89
|
+
const SIGN_IN_FALLBACK_HINT: string =
|
|
90
|
+
"Can't find it? Check your spam folder. The link expires in 24 hours, and signing in with your email and password sends you a new one.";
|
|
91
|
+
const SENT: string =
|
|
92
|
+
"We've sent a new verification link. It can take a minute or two to arrive.";
|
|
93
|
+
const SENT_TO: (email: string) => string = (email: string): string => {
|
|
94
|
+
return `We've sent a verification link to ${email}.`;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
type CountdownFunction = (time: string) => string;
|
|
98
|
+
|
|
99
|
+
const countdown: CountdownFunction = (time: string): string => {
|
|
100
|
+
return `You can request another email in ${time}.`;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/*
|
|
104
|
+
* Printed by React for BasicForm's own validation (see the other Register
|
|
105
|
+
* tests); this suite only insists that nothing it added shows up here.
|
|
106
|
+
*/
|
|
107
|
+
const OWN_COMPONENTS: Array<string> = [
|
|
108
|
+
"ResendVerificationEmail",
|
|
109
|
+
"VerifyEmailPending",
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
* What /signup answers with. Set per test; the account itself is never in
|
|
114
|
+
* the answer, which deliberately describes no account.
|
|
115
|
+
*/
|
|
116
|
+
let signupMiscData: JSONObject = {};
|
|
117
|
+
|
|
118
|
+
/* What ?email= holds -- "" when the visitor typed the address themselves. */
|
|
119
|
+
let linkedEmail: string = "";
|
|
120
|
+
|
|
121
|
+
let resendPosts: Array<{ url: string; data: unknown }> = [];
|
|
122
|
+
let consoleErrors: SpyInstance<typeof console.error>;
|
|
123
|
+
|
|
124
|
+
type FakeClockFunction = () => void;
|
|
125
|
+
|
|
126
|
+
const useFakeClock: FakeClockFunction = (): void => {
|
|
127
|
+
jest.useFakeTimers();
|
|
128
|
+
jest.setSystemTime(new Date("2026-03-02T10:00:00.000Z"));
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
type AdvanceFunction = (milliseconds: number) => void;
|
|
132
|
+
|
|
133
|
+
const advance: AdvanceFunction = (milliseconds: number): void => {
|
|
134
|
+
act(() => {
|
|
135
|
+
jest.advanceTimersByTime(milliseconds);
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
type SettleFunction = () => Promise<void>;
|
|
140
|
+
|
|
141
|
+
const settle: SettleFunction = async (): Promise<void> => {
|
|
142
|
+
await act(async () => {
|
|
143
|
+
for (let tick: number = 0; tick < 50; tick++) {
|
|
144
|
+
await Promise.resolve();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
type RenderPageFunction = () => Promise<void>;
|
|
150
|
+
|
|
151
|
+
const renderPage: RenderPageFunction = async (): Promise<void> => {
|
|
152
|
+
render(
|
|
153
|
+
<MemoryRouter>
|
|
154
|
+
<RegisterPage />
|
|
155
|
+
</MemoryRouter>,
|
|
156
|
+
);
|
|
157
|
+
await screen.findByTestId("password");
|
|
158
|
+
|
|
159
|
+
if (linkedEmail) {
|
|
160
|
+
await waitFor(() => {
|
|
161
|
+
expect(screen.getByTestId("email")).toHaveValue(linkedEmail);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
type SetFieldFunction = (name: string, value: string) => Promise<void>;
|
|
167
|
+
|
|
168
|
+
const setField: SetFieldFunction = async (
|
|
169
|
+
name: string,
|
|
170
|
+
value: string,
|
|
171
|
+
): Promise<void> => {
|
|
172
|
+
const user: ReturnType<typeof userEvent.setup> = userEvent.setup({
|
|
173
|
+
delay: null,
|
|
174
|
+
});
|
|
175
|
+
const input: HTMLElement = screen.getByTestId(name);
|
|
176
|
+
await user.clear(input);
|
|
177
|
+
if (value) {
|
|
178
|
+
await user.paste(value);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
type TypeEmailFunction = (value: string) => Promise<void>;
|
|
183
|
+
|
|
184
|
+
// See "TYPING AN EMAIL" above.
|
|
185
|
+
const typeEmail: TypeEmailFunction = async (value: string): Promise<void> => {
|
|
186
|
+
const input: HTMLElement = screen.getByTestId("email");
|
|
187
|
+
await act(async () => {
|
|
188
|
+
fireEvent.change(input, { target: { value: value } });
|
|
189
|
+
});
|
|
190
|
+
expect(input).toHaveValue(value);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
type FillFormFunction = (email?: string) => Promise<void>;
|
|
194
|
+
|
|
195
|
+
const fillForm: FillFormFunction = async (email?: string): Promise<void> => {
|
|
196
|
+
if (email !== undefined) {
|
|
197
|
+
await typeEmail(email);
|
|
198
|
+
}
|
|
199
|
+
await setField("name", NAME);
|
|
200
|
+
await setField("companyName", COMPANY);
|
|
201
|
+
await setField("companyPhoneNumber", PHONE);
|
|
202
|
+
await setField("password", PASSWORD);
|
|
203
|
+
await setField("confirmPassword", PASSWORD);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
type SubmitFunction = () => Promise<void>;
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
* Switches to the fake clock (see THE CLOCK above), then submits and lets
|
|
210
|
+
* the mocked /signup settle.
|
|
211
|
+
*/
|
|
212
|
+
const submit: SubmitFunction = async (): Promise<void> => {
|
|
213
|
+
useFakeClock();
|
|
214
|
+
await act(async () => {
|
|
215
|
+
fireEvent.click(screen.getByRole("button", { name: "Sign Up" }));
|
|
216
|
+
});
|
|
217
|
+
await settle();
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
type ElementFunction = () => HTMLElement;
|
|
221
|
+
|
|
222
|
+
const verifyScreen: ElementFunction = (): HTMLElement => {
|
|
223
|
+
return screen.getByTestId("verify-email-required");
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
type CountRevenueFunction = (name: RevenueEventName) => number;
|
|
227
|
+
|
|
228
|
+
const revenueEvents: CountRevenueFunction = (
|
|
229
|
+
name: RevenueEventName,
|
|
230
|
+
): number => {
|
|
231
|
+
return jest
|
|
232
|
+
.mocked(UiAnalytics.captureRevenueEvent)
|
|
233
|
+
.mock.calls.filter((call: Array<unknown>) => {
|
|
234
|
+
return call[0] === name;
|
|
235
|
+
}).length;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
type CountCaptureFunction = (name: string) => number;
|
|
239
|
+
|
|
240
|
+
const captures: CountCaptureFunction = (name: string): number => {
|
|
241
|
+
return jest
|
|
242
|
+
.mocked(UiAnalytics.capture)
|
|
243
|
+
.mock.calls.filter((call: Array<unknown>) => {
|
|
244
|
+
return call[0] === name;
|
|
245
|
+
}).length;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
type IdentifiedFunction = () => Array<string>;
|
|
249
|
+
|
|
250
|
+
const identifiedAs: IdentifiedFunction = (): Array<string> => {
|
|
251
|
+
return jest
|
|
252
|
+
.mocked(UiAnalytics.userAuth)
|
|
253
|
+
.mock.calls.map((call: Array<unknown>) => {
|
|
254
|
+
return String(call[0]);
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
type SubmittedEmailsFunction = () => Array<string>;
|
|
259
|
+
|
|
260
|
+
const submittedEmails: SubmittedEmailsFunction = (): Array<string> => {
|
|
261
|
+
return jest
|
|
262
|
+
.mocked(ModelAPI.createOrUpdate)
|
|
263
|
+
.mock.calls.map((call: Parameters<typeof ModelAPI.createOrUpdate>) => {
|
|
264
|
+
return (call[0].model as User).email?.toString() || "";
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
describe("Hosted signup that needs email verification", () => {
|
|
269
|
+
beforeEach(() => {
|
|
270
|
+
Object.defineProperty(UIConfig, "BILLING_ENABLED", { value: true });
|
|
271
|
+
|
|
272
|
+
linkedEmail = "";
|
|
273
|
+
resendPosts = [];
|
|
274
|
+
signupMiscData = {
|
|
275
|
+
emailVerificationRequired: true,
|
|
276
|
+
verificationEmailResendToken: RESEND_TOKEN,
|
|
277
|
+
verificationEmailResendAvailableInSeconds: 60,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
consoleErrors = jest.spyOn(console, "error");
|
|
281
|
+
|
|
282
|
+
jest.spyOn(UserUtil, "isLoggedIn").mockReturnValue(false);
|
|
283
|
+
jest.spyOn(UserUtil, "getUtmParams").mockReturnValue({});
|
|
284
|
+
jest.spyOn(UserUtil, "getAttributionClickIds").mockReturnValue(null);
|
|
285
|
+
jest.spyOn(UserUtil, "getFirstTouchAttribution").mockReturnValue(null);
|
|
286
|
+
jest.spyOn(Navigation, "navigate").mockImplementation(() => {});
|
|
287
|
+
jest
|
|
288
|
+
.spyOn(Navigation, "getQueryStringByName")
|
|
289
|
+
.mockImplementation((key: string): string => {
|
|
290
|
+
return key === "email" ? linkedEmail : "";
|
|
291
|
+
});
|
|
292
|
+
jest.spyOn(UiAnalytics, "userAuth").mockImplementation(() => {});
|
|
293
|
+
jest.spyOn(UiAnalytics, "capture").mockImplementation(() => {});
|
|
294
|
+
jest.spyOn(UiAnalytics, "captureRevenueEvent").mockImplementation(() => {});
|
|
295
|
+
jest.spyOn(LoginUtil, "login").mockImplementation(() => {});
|
|
296
|
+
jest
|
|
297
|
+
.spyOn(ModelAPI, "createOrUpdate")
|
|
298
|
+
.mockImplementation(async (): Promise<ModelAPIHttpResponse<User>> => {
|
|
299
|
+
const response: ModelAPIHttpResponse<User> =
|
|
300
|
+
new ModelAPIHttpResponse<User>(200, {}, {});
|
|
301
|
+
response.miscData = { ...signupMiscData };
|
|
302
|
+
return response;
|
|
303
|
+
});
|
|
304
|
+
jest
|
|
305
|
+
.spyOn(API, "post")
|
|
306
|
+
.mockImplementation(
|
|
307
|
+
async (
|
|
308
|
+
options: Parameters<typeof API.post>[0],
|
|
309
|
+
): Promise<HTTPResponse<JSONObject>> => {
|
|
310
|
+
const url: string = options.url.toString();
|
|
311
|
+
|
|
312
|
+
if (!url.endsWith("/resend-verification-email")) {
|
|
313
|
+
throw new Error(`Unexpected request to ${url}`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
resendPosts.push({ url: url, data: options.data });
|
|
317
|
+
return new HTTPResponse<JSONObject>(
|
|
318
|
+
200,
|
|
319
|
+
{ emailSent: true, alreadyVerified: false, retryAfterSeconds: 60 },
|
|
320
|
+
{},
|
|
321
|
+
);
|
|
322
|
+
},
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
afterEach(() => {
|
|
327
|
+
cleanup();
|
|
328
|
+
|
|
329
|
+
const ownComponentErrors: Array<string> = consoleErrors.mock.calls
|
|
330
|
+
.map((call: Array<unknown>) => {
|
|
331
|
+
return call.map(String).join(" ");
|
|
332
|
+
})
|
|
333
|
+
.filter((message: string) => {
|
|
334
|
+
return OWN_COMPONENTS.some((name: string) => {
|
|
335
|
+
return message.includes(name);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
jest.useRealTimers();
|
|
340
|
+
jest.restoreAllMocks();
|
|
341
|
+
|
|
342
|
+
expect(ownComponentErrors).toEqual([]);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("with the address from the link (?email=)", () => {
|
|
346
|
+
beforeEach(() => {
|
|
347
|
+
linkedEmail = EMAIL;
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("shows the verification screen for that address and logs nobody in", async () => {
|
|
351
|
+
await renderPage();
|
|
352
|
+
await fillForm();
|
|
353
|
+
await submit();
|
|
354
|
+
|
|
355
|
+
expect(ModelAPI.createOrUpdate).toHaveBeenCalledTimes(1);
|
|
356
|
+
expect(submittedEmails()).toEqual([EMAIL]);
|
|
357
|
+
|
|
358
|
+
const card: HTMLElement = verifyScreen();
|
|
359
|
+
expect(
|
|
360
|
+
screen.getByRole("heading", { level: 1, name: TITLE }),
|
|
361
|
+
).toBeInTheDocument();
|
|
362
|
+
expect(screen.getByTestId("verify-email-address")).toHaveTextContent(
|
|
363
|
+
EMAIL,
|
|
364
|
+
);
|
|
365
|
+
expect(card).toHaveTextContent(SENT_TO(EMAIL));
|
|
366
|
+
expect(document.querySelector("form")).toBeNull();
|
|
367
|
+
expect(screen.queryByTestId("password")).not.toBeInTheDocument();
|
|
368
|
+
expect(
|
|
369
|
+
screen.queryByRole("button", { name: "Sign Up" }),
|
|
370
|
+
).not.toBeInTheDocument();
|
|
371
|
+
|
|
372
|
+
// No session exists yet, so nothing may pretend otherwise.
|
|
373
|
+
expect(LoginUtil.login).not.toHaveBeenCalled();
|
|
374
|
+
expect(Navigation.navigate).not.toHaveBeenCalled();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("wires the resend button to the token, disabled for the wait /signup gave", async () => {
|
|
378
|
+
await renderPage();
|
|
379
|
+
await fillForm();
|
|
380
|
+
await submit();
|
|
381
|
+
|
|
382
|
+
expect(screen.getByText(SPAM_HINT)).toBeInTheDocument();
|
|
383
|
+
expect(screen.queryByText(SIGN_IN_FALLBACK_HINT)).not.toBeInTheDocument();
|
|
384
|
+
|
|
385
|
+
const resend: HTMLElement = screen.getByTestId(
|
|
386
|
+
"resend-verification-email",
|
|
387
|
+
);
|
|
388
|
+
expect(resend).toBeDisabled();
|
|
389
|
+
expect(resend).toHaveAccessibleName("Resend verification email");
|
|
390
|
+
expect(
|
|
391
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
392
|
+
).toHaveTextContent(countdown("1:00"));
|
|
393
|
+
|
|
394
|
+
advance(30_000);
|
|
395
|
+
expect(
|
|
396
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
397
|
+
).toHaveTextContent(countdown("0:30"));
|
|
398
|
+
expect(resend).toBeDisabled();
|
|
399
|
+
expect(resendPosts).toHaveLength(0);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("after the wait, resend posts the token from /signup and nothing else", async () => {
|
|
403
|
+
await renderPage();
|
|
404
|
+
await fillForm();
|
|
405
|
+
await submit();
|
|
406
|
+
|
|
407
|
+
advance(60_000);
|
|
408
|
+
|
|
409
|
+
const resend: HTMLElement = screen.getByTestId(
|
|
410
|
+
"resend-verification-email",
|
|
411
|
+
);
|
|
412
|
+
expect(resend).toBeEnabled();
|
|
413
|
+
expect(
|
|
414
|
+
screen.queryByTestId("resend-verification-email-countdown"),
|
|
415
|
+
).not.toBeInTheDocument();
|
|
416
|
+
|
|
417
|
+
fireEvent.click(resend);
|
|
418
|
+
await settle();
|
|
419
|
+
|
|
420
|
+
expect(resendPosts).toEqual([
|
|
421
|
+
{
|
|
422
|
+
url: expect.stringMatching(/\/resend-verification-email$/),
|
|
423
|
+
data: { data: { resendToken: RESEND_TOKEN } },
|
|
424
|
+
},
|
|
425
|
+
]);
|
|
426
|
+
expect(
|
|
427
|
+
screen.getByTestId("resend-verification-email-status"),
|
|
428
|
+
).toHaveTextContent(SENT);
|
|
429
|
+
expect(
|
|
430
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
431
|
+
).toHaveTextContent(countdown("1:01"));
|
|
432
|
+
expect(LoginUtil.login).not.toHaveBeenCalled();
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test("uses whatever wait /signup gave", async () => {
|
|
436
|
+
signupMiscData["verificationEmailResendAvailableInSeconds"] = 5;
|
|
437
|
+
await renderPage();
|
|
438
|
+
await fillForm();
|
|
439
|
+
await submit();
|
|
440
|
+
|
|
441
|
+
expect(
|
|
442
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
443
|
+
).toHaveTextContent(countdown("0:05"));
|
|
444
|
+
|
|
445
|
+
advance(5000);
|
|
446
|
+
expect(screen.getByTestId("resend-verification-email")).toBeEnabled();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("falls back to the default minute when /signup gives no usable wait", async () => {
|
|
450
|
+
signupMiscData["verificationEmailResendAvailableInSeconds"] = "soon";
|
|
451
|
+
await renderPage();
|
|
452
|
+
await fillForm();
|
|
453
|
+
await submit();
|
|
454
|
+
|
|
455
|
+
expect(
|
|
456
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
457
|
+
).toHaveTextContent(countdown("1:00"));
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test.each([
|
|
461
|
+
["no resend token", undefined],
|
|
462
|
+
["an empty resend token", ""],
|
|
463
|
+
["a resend token that is not a string", 12345],
|
|
464
|
+
])(
|
|
465
|
+
"with %s: the sign-in advice and no resend button",
|
|
466
|
+
async (_label: string, token: unknown) => {
|
|
467
|
+
if (token === undefined) {
|
|
468
|
+
delete signupMiscData["verificationEmailResendToken"];
|
|
469
|
+
delete signupMiscData["verificationEmailResendAvailableInSeconds"];
|
|
470
|
+
} else {
|
|
471
|
+
signupMiscData["verificationEmailResendToken"] = token as string;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
await renderPage();
|
|
475
|
+
await fillForm();
|
|
476
|
+
await submit();
|
|
477
|
+
|
|
478
|
+
expect(verifyScreen()).toBeInTheDocument();
|
|
479
|
+
expect(screen.getByTestId("verify-email-address")).toHaveTextContent(
|
|
480
|
+
EMAIL,
|
|
481
|
+
);
|
|
482
|
+
expect(screen.getByText(SIGN_IN_FALLBACK_HINT)).toBeInTheDocument();
|
|
483
|
+
expect(screen.queryByText(SPAM_HINT)).not.toBeInTheDocument();
|
|
484
|
+
expect(
|
|
485
|
+
screen.queryByTestId("resend-verification-email"),
|
|
486
|
+
).not.toBeInTheDocument();
|
|
487
|
+
expect(LoginUtil.login).not.toHaveBeenCalled();
|
|
488
|
+
},
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
test("does not offer a different email for an address that came with the link", async () => {
|
|
492
|
+
await renderPage();
|
|
493
|
+
|
|
494
|
+
expect(screen.getByTestId("email")).toHaveAttribute("readonly");
|
|
495
|
+
|
|
496
|
+
await fillForm();
|
|
497
|
+
await submit();
|
|
498
|
+
|
|
499
|
+
expect(verifyScreen()).toBeInTheDocument();
|
|
500
|
+
expect(
|
|
501
|
+
screen.queryByTestId("verify-email-use-different-email"),
|
|
502
|
+
).not.toBeInTheDocument();
|
|
503
|
+
expect(
|
|
504
|
+
screen.queryByText("Wrong email address?"),
|
|
505
|
+
).not.toBeInTheDocument();
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
test("counts one completed signup, under that address", async () => {
|
|
509
|
+
await renderPage();
|
|
510
|
+
await fillForm();
|
|
511
|
+
await submit();
|
|
512
|
+
|
|
513
|
+
expect(revenueEvents(RevenueEventName.SignupStarted)).toBe(1);
|
|
514
|
+
expect(revenueEvents(RevenueEventName.SignupCompleted)).toBe(1);
|
|
515
|
+
expect(captures("accounts/register")).toBe(1);
|
|
516
|
+
expect(captures("accounts/register_email_changed")).toBe(0);
|
|
517
|
+
expect(identifiedAs()).toEqual([EMAIL]);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("a signup that starts a session still logs in as before", async () => {
|
|
521
|
+
signupMiscData = { token: "signup-session" };
|
|
522
|
+
jest
|
|
523
|
+
.spyOn(ModelAPI, "createOrUpdate")
|
|
524
|
+
.mockImplementation(async (): Promise<ModelAPIHttpResponse<User>> => {
|
|
525
|
+
const response: ModelAPIHttpResponse<User> =
|
|
526
|
+
new ModelAPIHttpResponse<User>(
|
|
527
|
+
200,
|
|
528
|
+
{
|
|
529
|
+
_id: "33333333-3333-4333-8333-333333333333",
|
|
530
|
+
email: EMAIL,
|
|
531
|
+
name: NAME,
|
|
532
|
+
},
|
|
533
|
+
{},
|
|
534
|
+
);
|
|
535
|
+
response.miscData = { ...signupMiscData };
|
|
536
|
+
return response;
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
await renderPage();
|
|
540
|
+
await fillForm();
|
|
541
|
+
await submit();
|
|
542
|
+
|
|
543
|
+
expect(LoginUtil.login).toHaveBeenCalledTimes(1);
|
|
544
|
+
expect(
|
|
545
|
+
screen.queryByTestId("verify-email-required"),
|
|
546
|
+
).not.toBeInTheDocument();
|
|
547
|
+
expect(revenueEvents(RevenueEventName.SignupCompleted)).toBe(1);
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
describe("with an address the visitor typed", () => {
|
|
552
|
+
test("offers a way back when the address was wrong, and keeps what was typed", async () => {
|
|
553
|
+
await renderPage();
|
|
554
|
+
|
|
555
|
+
expect(screen.getByTestId("email")).not.toHaveAttribute("readonly");
|
|
556
|
+
expect(screen.getByTestId("email")).toBeEnabled();
|
|
557
|
+
|
|
558
|
+
await fillForm(TYPO_EMAIL);
|
|
559
|
+
await submit();
|
|
560
|
+
|
|
561
|
+
expect(screen.getByTestId("verify-email-address")).toHaveTextContent(
|
|
562
|
+
TYPO_EMAIL,
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
const useDifferentEmail: HTMLElement = screen.getByTestId(
|
|
566
|
+
"verify-email-use-different-email",
|
|
567
|
+
);
|
|
568
|
+
expect(useDifferentEmail).toHaveAccessibleName(
|
|
569
|
+
"Sign up with a different email",
|
|
570
|
+
);
|
|
571
|
+
expect(useDifferentEmail.closest("p")).toHaveTextContent(
|
|
572
|
+
"Wrong email address?",
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
await act(async () => {
|
|
576
|
+
fireEvent.click(useDifferentEmail);
|
|
577
|
+
});
|
|
578
|
+
await settle();
|
|
579
|
+
|
|
580
|
+
// Back to the form, as it was left.
|
|
581
|
+
expect(
|
|
582
|
+
screen.queryByTestId("verify-email-required"),
|
|
583
|
+
).not.toBeInTheDocument();
|
|
584
|
+
expect(screen.getByRole("button", { name: "Sign Up" })).toBeEnabled();
|
|
585
|
+
|
|
586
|
+
const email: HTMLElement = screen.getByTestId("email");
|
|
587
|
+
expect(email).toHaveValue(TYPO_EMAIL);
|
|
588
|
+
expect(email).toBeEnabled();
|
|
589
|
+
expect(email).not.toHaveAttribute("readonly");
|
|
590
|
+
expect(screen.getByTestId("name")).toHaveValue(NAME);
|
|
591
|
+
expect(screen.getByTestId("companyName")).toHaveValue(COMPANY);
|
|
592
|
+
expect(screen.getByTestId("companyPhoneNumber")).toHaveValue(PHONE);
|
|
593
|
+
|
|
594
|
+
// Never the secrets.
|
|
595
|
+
expect(screen.getByTestId("password")).toHaveValue("");
|
|
596
|
+
expect(screen.getByTestId("confirmPassword")).toHaveValue("");
|
|
597
|
+
|
|
598
|
+
// The resend countdown went with the screen.
|
|
599
|
+
expect(
|
|
600
|
+
screen.queryByTestId("resend-verification-email"),
|
|
601
|
+
).not.toBeInTheDocument();
|
|
602
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
test("the returned form accepts a corrected address and signs up again under it", async () => {
|
|
606
|
+
await renderPage();
|
|
607
|
+
await fillForm(TYPO_EMAIL);
|
|
608
|
+
await submit();
|
|
609
|
+
|
|
610
|
+
await act(async () => {
|
|
611
|
+
fireEvent.click(screen.getByTestId("verify-email-use-different-email"));
|
|
612
|
+
});
|
|
613
|
+
await settle();
|
|
614
|
+
|
|
615
|
+
// Back on the real clock for typing, as at the start.
|
|
616
|
+
jest.useRealTimers();
|
|
617
|
+
|
|
618
|
+
await typeEmail(EMAIL);
|
|
619
|
+
await setField("password", PASSWORD);
|
|
620
|
+
await setField("confirmPassword", PASSWORD);
|
|
621
|
+
await submit();
|
|
622
|
+
|
|
623
|
+
expect(submittedEmails()).toEqual([TYPO_EMAIL, EMAIL]);
|
|
624
|
+
|
|
625
|
+
const secondRequest: User = jest.mocked(ModelAPI.createOrUpdate).mock
|
|
626
|
+
.calls[1]![0].model as User;
|
|
627
|
+
expect(secondRequest.name?.toString()).toBe(NAME);
|
|
628
|
+
expect(secondRequest.companyName?.toString()).toBe(COMPANY);
|
|
629
|
+
expect(secondRequest.password?.toString()).toBe(PASSWORD);
|
|
630
|
+
|
|
631
|
+
expect(screen.getByTestId("verify-email-address")).toHaveTextContent(
|
|
632
|
+
EMAIL,
|
|
633
|
+
);
|
|
634
|
+
expect(verifyScreen()).not.toHaveTextContent(TYPO_EMAIL);
|
|
635
|
+
expect(
|
|
636
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
637
|
+
).toHaveTextContent(countdown("1:00"));
|
|
638
|
+
expect(LoginUtil.login).not.toHaveBeenCalled();
|
|
639
|
+
|
|
640
|
+
/*
|
|
641
|
+
* One visitor, one conversion: the funnel does not count the second
|
|
642
|
+
* account, but analytics follows the visitor to the address they
|
|
643
|
+
* actually use.
|
|
644
|
+
*/
|
|
645
|
+
expect(revenueEvents(RevenueEventName.SignupStarted)).toBe(1);
|
|
646
|
+
expect(revenueEvents(RevenueEventName.SignupCompleted)).toBe(1);
|
|
647
|
+
expect(captures("accounts/register")).toBe(1);
|
|
648
|
+
expect(captures("accounts/register_email_changed")).toBe(1);
|
|
649
|
+
expect(identifiedAs()).toEqual([TYPO_EMAIL, EMAIL]);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
test("signing up again under the same address is not an address change", async () => {
|
|
653
|
+
await renderPage();
|
|
654
|
+
await fillForm(EMAIL);
|
|
655
|
+
await submit();
|
|
656
|
+
|
|
657
|
+
await act(async () => {
|
|
658
|
+
fireEvent.click(screen.getByTestId("verify-email-use-different-email"));
|
|
659
|
+
});
|
|
660
|
+
await settle();
|
|
661
|
+
jest.useRealTimers();
|
|
662
|
+
|
|
663
|
+
expect(screen.getByTestId("email")).toHaveValue(EMAIL);
|
|
664
|
+
await setField("password", PASSWORD);
|
|
665
|
+
await setField("confirmPassword", PASSWORD);
|
|
666
|
+
await submit();
|
|
667
|
+
|
|
668
|
+
expect(submittedEmails()).toEqual([EMAIL, EMAIL]);
|
|
669
|
+
expect(verifyScreen()).toBeInTheDocument();
|
|
670
|
+
expect(revenueEvents(RevenueEventName.SignupCompleted)).toBe(1);
|
|
671
|
+
expect(captures("accounts/register_email_changed")).toBe(0);
|
|
672
|
+
expect(identifiedAs()).toEqual([EMAIL]);
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
test("the new screen gets the new token and its own wait", async () => {
|
|
676
|
+
await renderPage();
|
|
677
|
+
await fillForm(TYPO_EMAIL);
|
|
678
|
+
await submit();
|
|
679
|
+
|
|
680
|
+
advance(40_000);
|
|
681
|
+
expect(
|
|
682
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
683
|
+
).toHaveTextContent(countdown("0:20"));
|
|
684
|
+
|
|
685
|
+
await act(async () => {
|
|
686
|
+
fireEvent.click(screen.getByTestId("verify-email-use-different-email"));
|
|
687
|
+
});
|
|
688
|
+
await settle();
|
|
689
|
+
jest.useRealTimers();
|
|
690
|
+
|
|
691
|
+
signupMiscData = {
|
|
692
|
+
emailVerificationRequired: true,
|
|
693
|
+
verificationEmailResendToken: `${RESEND_TOKEN}-second`,
|
|
694
|
+
verificationEmailResendAvailableInSeconds: 45,
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
await typeEmail(EMAIL);
|
|
698
|
+
await setField("password", PASSWORD);
|
|
699
|
+
await setField("confirmPassword", PASSWORD);
|
|
700
|
+
await submit();
|
|
701
|
+
|
|
702
|
+
expect(
|
|
703
|
+
screen.getByTestId("resend-verification-email-countdown"),
|
|
704
|
+
).toHaveTextContent(countdown("0:45"));
|
|
705
|
+
|
|
706
|
+
advance(45_000);
|
|
707
|
+
fireEvent.click(screen.getByTestId("resend-verification-email"));
|
|
708
|
+
await settle();
|
|
709
|
+
|
|
710
|
+
expect(
|
|
711
|
+
resendPosts.map((post: { data: unknown }) => {
|
|
712
|
+
return post.data;
|
|
713
|
+
}),
|
|
714
|
+
).toEqual([{ data: { resendToken: `${RESEND_TOKEN}-second` } }]);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
test("a second signup whose server sends no token falls back to the sign-in advice", async () => {
|
|
718
|
+
await renderPage();
|
|
719
|
+
await fillForm(TYPO_EMAIL);
|
|
720
|
+
await submit();
|
|
721
|
+
|
|
722
|
+
expect(
|
|
723
|
+
screen.getByTestId("resend-verification-email"),
|
|
724
|
+
).toBeInTheDocument();
|
|
725
|
+
|
|
726
|
+
await act(async () => {
|
|
727
|
+
fireEvent.click(screen.getByTestId("verify-email-use-different-email"));
|
|
728
|
+
});
|
|
729
|
+
await settle();
|
|
730
|
+
jest.useRealTimers();
|
|
731
|
+
|
|
732
|
+
signupMiscData = { emailVerificationRequired: true };
|
|
733
|
+
|
|
734
|
+
await typeEmail(EMAIL);
|
|
735
|
+
await setField("password", PASSWORD);
|
|
736
|
+
await setField("confirmPassword", PASSWORD);
|
|
737
|
+
await submit();
|
|
738
|
+
|
|
739
|
+
expect(screen.getByTestId("verify-email-address")).toHaveTextContent(
|
|
740
|
+
EMAIL,
|
|
741
|
+
);
|
|
742
|
+
expect(screen.getByText(SIGN_IN_FALLBACK_HINT)).toBeInTheDocument();
|
|
743
|
+
expect(
|
|
744
|
+
screen.queryByTestId("resend-verification-email"),
|
|
745
|
+
).not.toBeInTheDocument();
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
});
|