@oneuptime/common 12.0.9 → 12.0.11
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/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
- package/Models/DatabaseModels/RunnerJob.ts +17 -2
- package/Server/API/LlmProviderAPI.ts +8 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
- package/Server/Services/NetworkDeviceLinkService.ts +338 -2
- package/Server/Services/NetworkDeviceService.ts +265 -12
- package/Server/Services/RunnerJobService.ts +24 -1
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
- package/Server/Types/Database/JSONColumnQuery.ts +42 -4
- package/Server/Utils/LLM/LLMService.ts +108 -19
- package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
- package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
- package/Server/Utils/SSRFProtection.ts +5 -4
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
- package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
- package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
- package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
- package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
- package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
- package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
- package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
- package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
- package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
- package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
- package/Tests/Types/API/URLTelScheme.test.ts +261 -0
- package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
- package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
- package/Tests/UI/Components/Button.test.tsx +279 -0
- package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
- package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
- package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
- package/Tests/UI/Components/Icon.test.tsx +57 -0
- package/Tests/UI/Components/List.test.tsx +20 -3
- package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
- package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
- package/Tests/UI/Components/Pagination.test.tsx +721 -146
- package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
- package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
- package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
- package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
- package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
- package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
- package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
- package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
- package/Types/API/Protocol.ts +9 -0
- package/Types/API/URL.ts +286 -20
- package/Types/Billing/PayAsYouGoPricing.ts +47 -0
- package/Types/CustomField/CustomFieldType.ts +28 -0
- package/Types/Monitor/MonitorType.ts +11 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
- package/Types/Runbook/RunbookStepType.ts +23 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
- package/UI/Components/Button/Button.tsx +14 -22
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
- package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +4 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
- package/UI/Components/Dictionary/Dictionary.tsx +4 -39
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
- package/UI/Components/Pagination/Pagination.tsx +373 -328
- package/UI/Components/Pagination/PaginationUtil.ts +263 -0
- package/UI/Components/Pagination/Screenshots/README.md +15 -0
- package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
- package/UI/Components/TextArea/TextArea.tsx +3 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
- package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
- package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
- package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
- package/Utils/API.ts +243 -36
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
- package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +9 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +20 -2
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +5 -4
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Types/API/Protocol.js +9 -0
- package/build/dist/Types/API/Protocol.js.map +1 -1
- package/build/dist/Types/API/URL.js +241 -18
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +10 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -16
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
- package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
- package/build/dist/Utils/API.js +175 -34
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
- package/package.json +1 -1
|
@@ -519,6 +519,24 @@ type SummaryJsonFunction = (
|
|
|
519
519
|
isFallbackEnabled?: boolean | undefined,
|
|
520
520
|
) => JSONObject;
|
|
521
521
|
|
|
522
|
+
/*
|
|
523
|
+
* The login addresses the fixture responders sign in with, read off the
|
|
524
|
+
* fixtures rather than retyped so a renamed responder cannot quietly drop out
|
|
525
|
+
* of the masking guard's exemption list and start passing for the wrong
|
|
526
|
+
* reason. These are NOT notification-method identifiers: those all live in
|
|
527
|
+
* ALL_RAW_IDENTIFIERS and must never render.
|
|
528
|
+
*/
|
|
529
|
+
const ALL_LOGIN_EMAILS: Array<string> = [
|
|
530
|
+
READY_USER,
|
|
531
|
+
PARTIAL_USER,
|
|
532
|
+
UNREACHABLE_USER,
|
|
533
|
+
TWO_TEAM_USER,
|
|
534
|
+
CHANNELS_OFF_USER,
|
|
535
|
+
DISTINCT_KIND_USER,
|
|
536
|
+
].map((user: JSONObject) => {
|
|
537
|
+
return user["userEmail"] as string;
|
|
538
|
+
});
|
|
539
|
+
|
|
522
540
|
/*
|
|
523
541
|
* `isFallbackEnabled` defaults to true because that is the product default
|
|
524
542
|
* (disableOnCallNotificationFallback is false unless an admin sets it), and every
|
|
@@ -3130,11 +3148,18 @@ describe("On-call readiness page", () => {
|
|
|
3130
3148
|
test("the options are the teams that actually page somebody", async () => {
|
|
3131
3149
|
await openTeamFilter();
|
|
3132
3150
|
|
|
3133
|
-
|
|
3134
|
-
|
|
3151
|
+
/*
|
|
3152
|
+
* Scoped to the open menu rather than the document: the table's
|
|
3153
|
+
* pagination bar carries a page-size select, whose native options are
|
|
3154
|
+
* `option`s too and are there whether a menu is open or not.
|
|
3155
|
+
*/
|
|
3156
|
+
const menu: HTMLElement = await screen.findByRole("listbox");
|
|
3157
|
+
|
|
3158
|
+
const options: Array<string> = within(menu)
|
|
3159
|
+
.getAllByRole("option")
|
|
3160
|
+
.map((option: HTMLElement): string => {
|
|
3135
3161
|
return option.textContent || "";
|
|
3136
|
-
}
|
|
3137
|
-
);
|
|
3162
|
+
});
|
|
3138
3163
|
|
|
3139
3164
|
expect(options).toEqual(["Payments", "Platform"]);
|
|
3140
3165
|
});
|
|
@@ -3527,11 +3552,61 @@ describe("no unmasked identifier reaches the DOM", () => {
|
|
|
3527
3552
|
/*
|
|
3528
3553
|
* The scan above catches the planted values by name; these two catch the
|
|
3529
3554
|
* shape, so an identifier this file never thought of is caught too.
|
|
3555
|
+
*
|
|
3556
|
+
* Two things come out of the text before the shape is scanned.
|
|
3557
|
+
*
|
|
3558
|
+
* The pagination bar, because `textContent` joins siblings with no
|
|
3559
|
+
* separator: its page-size options ("10", "20", "25", "50", "100") fuse
|
|
3560
|
+
* into "10202550100", a digit run long enough to read as a phone number.
|
|
3561
|
+
*
|
|
3562
|
+
* Login addresses, because a responder row shows the person's login
|
|
3563
|
+
* email under their name, and that address is legitimate here for the
|
|
3564
|
+
* same reason the mail draft below may carry it: an admin already reads
|
|
3565
|
+
* it on Settings > Users and on the team page.
|
|
3566
|
+
*
|
|
3567
|
+
* What must never appear is a notification-method identifier, and
|
|
3568
|
+
* neither exclusion hides one: substituting a method identifier for one
|
|
3569
|
+
* of these login addresses still fails the shape scan, and one rendered
|
|
3570
|
+
* inside the pagination bar still fails the by-name scan above, which
|
|
3571
|
+
* reads the whole container.
|
|
3530
3572
|
*/
|
|
3531
|
-
|
|
3532
|
-
|
|
3573
|
+
const withoutPagination: HTMLElement = container.cloneNode(
|
|
3574
|
+
true,
|
|
3575
|
+
) as HTMLElement;
|
|
3576
|
+
|
|
3577
|
+
withoutPagination
|
|
3578
|
+
.querySelectorAll('nav[aria-label^="Pagination"]')
|
|
3579
|
+
.forEach((paginationBar: Element): void => {
|
|
3580
|
+
paginationBar.remove();
|
|
3581
|
+
});
|
|
3582
|
+
|
|
3583
|
+
let text: string = withoutPagination.textContent || "";
|
|
3584
|
+
|
|
3585
|
+
for (const loginEmail of ALL_LOGIN_EMAILS) {
|
|
3586
|
+
text = text.split(loginEmail).join("");
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
expect(text).not.toMatch(UNMASKED_EMAIL_PATTERN);
|
|
3590
|
+
expect(text).not.toMatch(UNMASKED_PHONE_PATTERN);
|
|
3533
3591
|
};
|
|
3534
3592
|
|
|
3593
|
+
/*
|
|
3594
|
+
* The other half of the exemption above: the login email is not merely
|
|
3595
|
+
* tolerated on these surfaces, it is meant to be there, so that two
|
|
3596
|
+
* responders who share a name are still tellable apart. If this stops
|
|
3597
|
+
* holding, the exemption is protecting nothing and should go.
|
|
3598
|
+
*/
|
|
3599
|
+
test("the responder row names the person by their login email", async () => {
|
|
3600
|
+
respondWith(summaryJson(ALL_THREE_USERS));
|
|
3601
|
+
|
|
3602
|
+
const container: HTMLElement = await renderPolicyCard();
|
|
3603
|
+
|
|
3604
|
+
await screen.findByText("Zed Unreachable");
|
|
3605
|
+
|
|
3606
|
+
expect(container.textContent).toContain("zed.unreachable@example.com");
|
|
3607
|
+
expect(container.textContent).toContain("jane.partial@example.com");
|
|
3608
|
+
});
|
|
3609
|
+
|
|
3535
3610
|
test("the policy card renders masked identifiers only", async () => {
|
|
3536
3611
|
respondWith(summaryJson(ALL_THREE_USERS));
|
|
3537
3612
|
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { beforeEach, describe, expect, it } from "@jest/globals";
|
|
6
|
+
import { PlanType } from "../../../Types/Billing/SubscriptionPlan";
|
|
7
|
+
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
8
|
+
import Permission, { UserPermission } from "../../../Types/Permission";
|
|
9
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
10
|
+
import TelemetryIngestionKey from "../../../Models/DatabaseModels/TelemetryIngestionKey";
|
|
11
|
+
import FormFieldSchemaType from "../../../UI/Components/Forms/Types/FormFieldSchemaType";
|
|
12
|
+
import ProjectUtil from "../../../UI/Utils/Project";
|
|
13
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
14
|
+
import { getJestSpyOn } from "../../Spy";
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* The consent checkbox is the whole point of the change: on the Free plan a
|
|
18
|
+
* user must not be able to start a metered charge without saying they
|
|
19
|
+
* understand it. This suite drives the real ModelForm rather than the field
|
|
20
|
+
* definitions, because the mechanism is subtle - the form's own `required`
|
|
21
|
+
* check stringifies values, so an unticked box reads as the non-empty string
|
|
22
|
+
* "false" and sails straight through it. The gate is a customValidation, and
|
|
23
|
+
* it only runs at all because getDefaultValue seeds the key.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const createOrUpdateMock: MockFunction = getJestMockFunction();
|
|
27
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
28
|
+
|
|
29
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
30
|
+
return {
|
|
31
|
+
__esModule: true,
|
|
32
|
+
default: {
|
|
33
|
+
getItem: (...args: Array<any>) => {
|
|
34
|
+
return getItemMock(...args);
|
|
35
|
+
},
|
|
36
|
+
createOrUpdate: (...args: Array<any>) => {
|
|
37
|
+
return createOrUpdateMock(...args);
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const OWNER_PERMISSIONS: Array<Permission> = [
|
|
44
|
+
Permission.Public,
|
|
45
|
+
Permission.User,
|
|
46
|
+
Permission.CurrentUser,
|
|
47
|
+
Permission.ProjectOwner,
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
jest.mock("../../../UI/Utils/Permission", () => {
|
|
51
|
+
return {
|
|
52
|
+
__esModule: true,
|
|
53
|
+
default: {
|
|
54
|
+
getAllPermissions: () => {
|
|
55
|
+
return OWNER_PERMISSIONS;
|
|
56
|
+
},
|
|
57
|
+
getProjectPermissions: () => {
|
|
58
|
+
return {
|
|
59
|
+
permissions: OWNER_PERMISSIONS.map((permission: Permission) => {
|
|
60
|
+
return {
|
|
61
|
+
permission,
|
|
62
|
+
labelIds: [],
|
|
63
|
+
_type: "UserPermission",
|
|
64
|
+
} as UserPermission;
|
|
65
|
+
}),
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
getGlobalPermissions: () => {
|
|
69
|
+
return { globalPermissions: OWNER_PERMISSIONS };
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
jest.mock("../../../UI/Utils/User", () => {
|
|
76
|
+
return {
|
|
77
|
+
__esModule: true,
|
|
78
|
+
default: {
|
|
79
|
+
isMasterAdmin: () => {
|
|
80
|
+
return false;
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
interface MutableConfig {
|
|
87
|
+
billingEnabled: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const config: MutableConfig = { billingEnabled: true };
|
|
91
|
+
|
|
92
|
+
(
|
|
93
|
+
globalThis as unknown as { __payAsYouGoGateConfig: MutableConfig }
|
|
94
|
+
).__payAsYouGoGateConfig = config;
|
|
95
|
+
|
|
96
|
+
jest.mock("../../../UI/Config", () => {
|
|
97
|
+
const mocked: Record<string, unknown> = {
|
|
98
|
+
...jest.requireActual("../../../UI/Config"),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
Object.defineProperty(mocked, "BILLING_ENABLED", {
|
|
102
|
+
get: (): boolean => {
|
|
103
|
+
return Boolean(
|
|
104
|
+
(
|
|
105
|
+
globalThis as unknown as {
|
|
106
|
+
__payAsYouGoGateConfig: MutableConfig | undefined;
|
|
107
|
+
}
|
|
108
|
+
).__payAsYouGoGateConfig?.billingEnabled,
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return mocked;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Imported after the mocks above.
|
|
117
|
+
import ModelForm, { FormType } from "../../../UI/Components/Forms/ModelForm";
|
|
118
|
+
import {
|
|
119
|
+
MONITOR_CONSENT_ERROR,
|
|
120
|
+
MONITOR_CONSENT_FIELD_KEY,
|
|
121
|
+
TELEMETRY_CONSENT_ERROR,
|
|
122
|
+
TELEMETRY_CONSENT_FIELD_KEY,
|
|
123
|
+
getMonitorPayAsYouGoFormFields,
|
|
124
|
+
getTelemetryPayAsYouGoFormFields,
|
|
125
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/Billing/PayAsYouGo";
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* These render real forms that validate on every keystroke, so give the waits
|
|
129
|
+
* room to survive a loaded CI box.
|
|
130
|
+
*/
|
|
131
|
+
const WAIT_TIMEOUT: number = 20000;
|
|
132
|
+
|
|
133
|
+
type RenderIngestionKeyFormFunction = () => void;
|
|
134
|
+
|
|
135
|
+
const renderIngestionKeyForm: RenderIngestionKeyFormFunction = (): void => {
|
|
136
|
+
render(
|
|
137
|
+
<ModelForm<TelemetryIngestionKey>
|
|
138
|
+
modelType={TelemetryIngestionKey}
|
|
139
|
+
id="create-ingestion-key-form"
|
|
140
|
+
name="Create Ingestion Key"
|
|
141
|
+
formType={FormType.Create}
|
|
142
|
+
submitButtonText="Create Ingestion Key"
|
|
143
|
+
fields={[
|
|
144
|
+
...getTelemetryPayAsYouGoFormFields(),
|
|
145
|
+
{
|
|
146
|
+
field: { name: true },
|
|
147
|
+
title: "Name",
|
|
148
|
+
fieldType: FormFieldSchemaType.Text,
|
|
149
|
+
required: true,
|
|
150
|
+
dataTestId: "ingestion-key-name",
|
|
151
|
+
},
|
|
152
|
+
]}
|
|
153
|
+
/>,
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
type RenderMonitorFormFunction = (monitorType: MonitorType) => void;
|
|
158
|
+
|
|
159
|
+
const renderMonitorForm: RenderMonitorFormFunction = (
|
|
160
|
+
monitorType: MonitorType,
|
|
161
|
+
): void => {
|
|
162
|
+
render(
|
|
163
|
+
<ModelForm<Monitor>
|
|
164
|
+
modelType={Monitor}
|
|
165
|
+
id="create-monitor-form"
|
|
166
|
+
name="Create New Monitor"
|
|
167
|
+
formType={FormType.Create}
|
|
168
|
+
submitButtonText="Create Monitor"
|
|
169
|
+
initialValues={{ monitorType: monitorType } as any}
|
|
170
|
+
steps={[{ title: "Monitor Info", id: "monitor-info" }]}
|
|
171
|
+
fields={[
|
|
172
|
+
{
|
|
173
|
+
field: { name: true },
|
|
174
|
+
stepId: "monitor-info",
|
|
175
|
+
title: "Name",
|
|
176
|
+
fieldType: FormFieldSchemaType.Text,
|
|
177
|
+
required: true,
|
|
178
|
+
dataTestId: "monitor-name",
|
|
179
|
+
},
|
|
180
|
+
...getMonitorPayAsYouGoFormFields({ stepId: "monitor-info" }),
|
|
181
|
+
]}
|
|
182
|
+
/>,
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
describe("Pay as you go consent gate", () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
jest.restoreAllMocks();
|
|
189
|
+
config.billingEnabled = true;
|
|
190
|
+
createOrUpdateMock.mockReset();
|
|
191
|
+
getItemMock.mockReset();
|
|
192
|
+
createOrUpdateMock.mockResolvedValue({ data: {} });
|
|
193
|
+
getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(PlanType.Free);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("creating a telemetry ingestion key on the Free plan", () => {
|
|
197
|
+
it("shows the pay as you go notice and the consent box in the form", async () => {
|
|
198
|
+
renderIngestionKeyForm();
|
|
199
|
+
|
|
200
|
+
expect(
|
|
201
|
+
await screen.findByText(
|
|
202
|
+
"Telemetry is a pay as you go feature",
|
|
203
|
+
{},
|
|
204
|
+
{ timeout: WAIT_TIMEOUT },
|
|
205
|
+
),
|
|
206
|
+
).toBeInTheDocument();
|
|
207
|
+
expect(
|
|
208
|
+
screen.getByTestId("telemetry-pay-as-you-go-consent"),
|
|
209
|
+
).toBeInTheDocument();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("refuses to create the key until the charge is acknowledged", async () => {
|
|
213
|
+
renderIngestionKeyForm();
|
|
214
|
+
|
|
215
|
+
await userEvent.type(
|
|
216
|
+
await screen.findByTestId(
|
|
217
|
+
"ingestion-key-name",
|
|
218
|
+
{},
|
|
219
|
+
{ timeout: WAIT_TIMEOUT },
|
|
220
|
+
),
|
|
221
|
+
"Production",
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
await userEvent.click(screen.getByText("Create Ingestion Key"));
|
|
225
|
+
|
|
226
|
+
await waitFor(
|
|
227
|
+
() => {
|
|
228
|
+
expect(screen.getByText(TELEMETRY_CONSENT_ERROR)).toBeInTheDocument();
|
|
229
|
+
},
|
|
230
|
+
{ timeout: WAIT_TIMEOUT },
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
expect(createOrUpdateMock).not.toHaveBeenCalled();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("creates the key once the box is ticked", async () => {
|
|
237
|
+
renderIngestionKeyForm();
|
|
238
|
+
|
|
239
|
+
await userEvent.type(
|
|
240
|
+
await screen.findByTestId(
|
|
241
|
+
"ingestion-key-name",
|
|
242
|
+
{},
|
|
243
|
+
{ timeout: WAIT_TIMEOUT },
|
|
244
|
+
),
|
|
245
|
+
"Production",
|
|
246
|
+
);
|
|
247
|
+
await userEvent.click(
|
|
248
|
+
screen.getByTestId("telemetry-pay-as-you-go-consent"),
|
|
249
|
+
);
|
|
250
|
+
await userEvent.click(screen.getByText("Create Ingestion Key"));
|
|
251
|
+
|
|
252
|
+
await waitFor(
|
|
253
|
+
() => {
|
|
254
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
255
|
+
},
|
|
256
|
+
{ timeout: WAIT_TIMEOUT },
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("blocks again if the box is ticked and then unticked", async () => {
|
|
261
|
+
/*
|
|
262
|
+
* The regression this guards: an unticked box that has been touched
|
|
263
|
+
* holds boolean false, which the form's `required` check would accept.
|
|
264
|
+
*/
|
|
265
|
+
renderIngestionKeyForm();
|
|
266
|
+
|
|
267
|
+
await userEvent.type(
|
|
268
|
+
await screen.findByTestId(
|
|
269
|
+
"ingestion-key-name",
|
|
270
|
+
{},
|
|
271
|
+
{ timeout: WAIT_TIMEOUT },
|
|
272
|
+
),
|
|
273
|
+
"Production",
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const consent: HTMLElement = screen.getByTestId(
|
|
277
|
+
"telemetry-pay-as-you-go-consent",
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
await userEvent.click(consent);
|
|
281
|
+
await userEvent.click(consent);
|
|
282
|
+
await userEvent.click(screen.getByText("Create Ingestion Key"));
|
|
283
|
+
|
|
284
|
+
await waitFor(
|
|
285
|
+
() => {
|
|
286
|
+
expect(screen.getByText(TELEMETRY_CONSENT_ERROR)).toBeInTheDocument();
|
|
287
|
+
},
|
|
288
|
+
{ timeout: WAIT_TIMEOUT },
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
expect(createOrUpdateMock).not.toHaveBeenCalled();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("does not send the acknowledgement to the API", async () => {
|
|
295
|
+
renderIngestionKeyForm();
|
|
296
|
+
|
|
297
|
+
await userEvent.type(
|
|
298
|
+
await screen.findByTestId(
|
|
299
|
+
"ingestion-key-name",
|
|
300
|
+
{},
|
|
301
|
+
{ timeout: WAIT_TIMEOUT },
|
|
302
|
+
),
|
|
303
|
+
"Production",
|
|
304
|
+
);
|
|
305
|
+
await userEvent.click(
|
|
306
|
+
screen.getByTestId("telemetry-pay-as-you-go-consent"),
|
|
307
|
+
);
|
|
308
|
+
await userEvent.click(screen.getByText("Create Ingestion Key"));
|
|
309
|
+
|
|
310
|
+
await waitFor(
|
|
311
|
+
() => {
|
|
312
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
313
|
+
},
|
|
314
|
+
{ timeout: WAIT_TIMEOUT },
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const call: any = createOrUpdateMock.mock.calls[0]?.[0];
|
|
318
|
+
|
|
319
|
+
/*
|
|
320
|
+
* The consent is a UI gate, not data. It has no column, so sending it
|
|
321
|
+
* would be an unknown property on the create payload.
|
|
322
|
+
*/
|
|
323
|
+
expect(call?.miscDataProps ?? {}).not.toHaveProperty(
|
|
324
|
+
TELEMETRY_CONSENT_FIELD_KEY,
|
|
325
|
+
);
|
|
326
|
+
expect(JSON.stringify(call?.model ?? {})).not.toContain(
|
|
327
|
+
TELEMETRY_CONSENT_FIELD_KEY,
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe("creating a telemetry ingestion key off the Free plan", () => {
|
|
333
|
+
it("asks for nothing extra on a paid plan", async () => {
|
|
334
|
+
getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(
|
|
335
|
+
PlanType.Growth,
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
renderIngestionKeyForm();
|
|
339
|
+
|
|
340
|
+
await userEvent.type(
|
|
341
|
+
await screen.findByTestId(
|
|
342
|
+
"ingestion-key-name",
|
|
343
|
+
{},
|
|
344
|
+
{ timeout: WAIT_TIMEOUT },
|
|
345
|
+
),
|
|
346
|
+
"Production",
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
expect(
|
|
350
|
+
screen.queryByTestId("telemetry-pay-as-you-go-consent"),
|
|
351
|
+
).not.toBeInTheDocument();
|
|
352
|
+
expect(
|
|
353
|
+
screen.queryByText("Telemetry is a pay as you go feature"),
|
|
354
|
+
).not.toBeInTheDocument();
|
|
355
|
+
|
|
356
|
+
await userEvent.click(screen.getByText("Create Ingestion Key"));
|
|
357
|
+
|
|
358
|
+
await waitFor(
|
|
359
|
+
() => {
|
|
360
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
361
|
+
},
|
|
362
|
+
{ timeout: WAIT_TIMEOUT },
|
|
363
|
+
);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("asks for nothing extra on a self-hosted install", async () => {
|
|
367
|
+
config.billingEnabled = false;
|
|
368
|
+
|
|
369
|
+
renderIngestionKeyForm();
|
|
370
|
+
|
|
371
|
+
await userEvent.type(
|
|
372
|
+
await screen.findByTestId(
|
|
373
|
+
"ingestion-key-name",
|
|
374
|
+
{},
|
|
375
|
+
{ timeout: WAIT_TIMEOUT },
|
|
376
|
+
),
|
|
377
|
+
"Production",
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
expect(
|
|
381
|
+
screen.queryByTestId("telemetry-pay-as-you-go-consent"),
|
|
382
|
+
).not.toBeInTheDocument();
|
|
383
|
+
|
|
384
|
+
await userEvent.click(screen.getByText("Create Ingestion Key"));
|
|
385
|
+
|
|
386
|
+
await waitFor(
|
|
387
|
+
() => {
|
|
388
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
389
|
+
},
|
|
390
|
+
{ timeout: WAIT_TIMEOUT },
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe("creating a monitor on the Free plan", () => {
|
|
396
|
+
it("refuses to create a billed monitor until the charge is acknowledged", async () => {
|
|
397
|
+
renderMonitorForm(MonitorType.Website);
|
|
398
|
+
|
|
399
|
+
await userEvent.type(
|
|
400
|
+
await screen.findByTestId(
|
|
401
|
+
"monitor-name",
|
|
402
|
+
{},
|
|
403
|
+
{ timeout: WAIT_TIMEOUT },
|
|
404
|
+
),
|
|
405
|
+
"Marketing site",
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
expect(
|
|
409
|
+
screen.getByTestId("monitor-pay-as-you-go-consent"),
|
|
410
|
+
).toBeInTheDocument();
|
|
411
|
+
|
|
412
|
+
await userEvent.click(screen.getByText("Create Monitor"));
|
|
413
|
+
|
|
414
|
+
await waitFor(
|
|
415
|
+
() => {
|
|
416
|
+
expect(screen.getByText(MONITOR_CONSENT_ERROR)).toBeInTheDocument();
|
|
417
|
+
},
|
|
418
|
+
{ timeout: WAIT_TIMEOUT },
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
expect(createOrUpdateMock).not.toHaveBeenCalled();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("creates the billed monitor once the box is ticked", async () => {
|
|
425
|
+
renderMonitorForm(MonitorType.Website);
|
|
426
|
+
|
|
427
|
+
await userEvent.type(
|
|
428
|
+
await screen.findByTestId(
|
|
429
|
+
"monitor-name",
|
|
430
|
+
{},
|
|
431
|
+
{ timeout: WAIT_TIMEOUT },
|
|
432
|
+
),
|
|
433
|
+
"Marketing site",
|
|
434
|
+
);
|
|
435
|
+
await userEvent.click(
|
|
436
|
+
screen.getByTestId("monitor-pay-as-you-go-consent"),
|
|
437
|
+
);
|
|
438
|
+
await userEvent.click(screen.getByText("Create Monitor"));
|
|
439
|
+
|
|
440
|
+
await waitFor(
|
|
441
|
+
() => {
|
|
442
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
443
|
+
},
|
|
444
|
+
{ timeout: WAIT_TIMEOUT },
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
const call: any = createOrUpdateMock.mock.calls[0]?.[0];
|
|
448
|
+
|
|
449
|
+
expect(call?.miscDataProps ?? {}).not.toHaveProperty(
|
|
450
|
+
MONITOR_CONSENT_FIELD_KEY,
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("asks nothing of a Manual monitor - those are free", async () => {
|
|
455
|
+
renderMonitorForm(MonitorType.Manual);
|
|
456
|
+
|
|
457
|
+
await userEvent.type(
|
|
458
|
+
await screen.findByTestId(
|
|
459
|
+
"monitor-name",
|
|
460
|
+
{},
|
|
461
|
+
{ timeout: WAIT_TIMEOUT },
|
|
462
|
+
),
|
|
463
|
+
"Runbook step",
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
expect(
|
|
467
|
+
screen.queryByTestId("monitor-pay-as-you-go-consent"),
|
|
468
|
+
).not.toBeInTheDocument();
|
|
469
|
+
|
|
470
|
+
await userEvent.click(screen.getByText("Create Monitor"));
|
|
471
|
+
|
|
472
|
+
await waitFor(
|
|
473
|
+
() => {
|
|
474
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
475
|
+
},
|
|
476
|
+
{ timeout: WAIT_TIMEOUT },
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it.each([
|
|
481
|
+
MonitorType.API,
|
|
482
|
+
MonitorType.Logs,
|
|
483
|
+
MonitorType.IncomingRequest,
|
|
484
|
+
MonitorType.SSLCertificate,
|
|
485
|
+
])(
|
|
486
|
+
"blocks %s too - every non-Manual type is billed",
|
|
487
|
+
async (monitorType: MonitorType) => {
|
|
488
|
+
renderMonitorForm(monitorType);
|
|
489
|
+
|
|
490
|
+
await userEvent.type(
|
|
491
|
+
await screen.findByTestId(
|
|
492
|
+
"monitor-name",
|
|
493
|
+
{},
|
|
494
|
+
{ timeout: WAIT_TIMEOUT },
|
|
495
|
+
),
|
|
496
|
+
"Something",
|
|
497
|
+
);
|
|
498
|
+
await userEvent.click(screen.getByText("Create Monitor"));
|
|
499
|
+
|
|
500
|
+
await waitFor(
|
|
501
|
+
() => {
|
|
502
|
+
expect(screen.getByText(MONITOR_CONSENT_ERROR)).toBeInTheDocument();
|
|
503
|
+
},
|
|
504
|
+
{ timeout: WAIT_TIMEOUT },
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
expect(createOrUpdateMock).not.toHaveBeenCalled();
|
|
508
|
+
},
|
|
509
|
+
);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
describe("creating a monitor off the Free plan", () => {
|
|
513
|
+
it("asks for nothing extra on a paid plan", async () => {
|
|
514
|
+
getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(
|
|
515
|
+
PlanType.Growth,
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
renderMonitorForm(MonitorType.Website);
|
|
519
|
+
|
|
520
|
+
await userEvent.type(
|
|
521
|
+
await screen.findByTestId(
|
|
522
|
+
"monitor-name",
|
|
523
|
+
{},
|
|
524
|
+
{ timeout: WAIT_TIMEOUT },
|
|
525
|
+
),
|
|
526
|
+
"Marketing site",
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
expect(
|
|
530
|
+
screen.queryByTestId("monitor-pay-as-you-go-consent"),
|
|
531
|
+
).not.toBeInTheDocument();
|
|
532
|
+
|
|
533
|
+
await userEvent.click(screen.getByText("Create Monitor"));
|
|
534
|
+
|
|
535
|
+
await waitFor(
|
|
536
|
+
() => {
|
|
537
|
+
expect(createOrUpdateMock).toHaveBeenCalled();
|
|
538
|
+
},
|
|
539
|
+
{ timeout: WAIT_TIMEOUT },
|
|
540
|
+
);
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
});
|