@oneuptime/common 14.0.2 → 14.0.3
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/AlertFeed.ts +2 -0
- package/Models/DatabaseModels/IncidentAlert.ts +540 -0
- package/Models/DatabaseModels/IncidentFeed.ts +2 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Models/DatabaseModels/RumApplication.ts +52 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/IncidentAlertService.ts +1811 -0
- package/Server/Services/IncidentService.ts +210 -10
- package/Server/Services/IncidentStateTimelineService.ts +24 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
- package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
- package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
- package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
- package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
- package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
- package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
- package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
- package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
- package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
- package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
- package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
- package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
- package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
- package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
- package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
- package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
- package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
- package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
- package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
- package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
- package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
- package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
- package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
- package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
- package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
- package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
- package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
- package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
- package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
- package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
- package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
- package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
- package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
- package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
- package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
- package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
- package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
- package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
- package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
- package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
- package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
- package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
- package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
- package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
- package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
- package/Tests/Types/Rum/WebVitals.test.ts +173 -0
- package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
- package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
- package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
- package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
- package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
- package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
- package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
- package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
- package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
- package/Types/Incident/IncidentAlertLink.ts +38 -0
- package/Types/Permission.ts +48 -0
- package/Types/Rum/SessionReplay.ts +25 -6
- package/Types/Rum/WebVitals.ts +52 -0
- package/UI/Components/Detail/FieldLabel.tsx +8 -0
- package/UI/Components/InfoCard/InfoCard.tsx +47 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +95 -17
- package/UI/Components/ModelTable/Column.ts +5 -0
- package/UI/Components/Table/TableHeader.tsx +47 -2
- package/UI/Components/Table/Types/Column.ts +7 -0
- package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
- package/UI/Utils/PermissionGate.ts +11 -2
- package/Utils/Incident/IncidentFromAlerts.ts +516 -0
- package/Utils/Rum/SessionTraceState.ts +212 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
- package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +164 -12
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +25 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
- package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +66 -14
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +18 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
- package/build/dist/UI/Utils/PermissionGate.js +4 -2
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
- package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
- package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,689 @@
|
|
|
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 { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import { MemoryRouter } from "react-router-dom";
|
|
13
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* Declaring an incident from alerts: the create-incident page reads
|
|
17
|
+
* `?alertIds=`, prefills the form from those alerts, shows which alerts will
|
|
18
|
+
* be linked, and hands their ids to the server as miscDataProps. Without the
|
|
19
|
+
* parameter nothing about the page changes.
|
|
20
|
+
*
|
|
21
|
+
* ModelForm is stubbed to capture the props it is handed (as
|
|
22
|
+
* AffectedResourcesSummaryStep.test.tsx does), and ModelAPI answers per model
|
|
23
|
+
* type. BasicForm latches initial values on its first render, so the page
|
|
24
|
+
* must not render the form until the prefill is in - every captured render is
|
|
25
|
+
* checked, not just the last one.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
jest.mock("react-i18next", () => {
|
|
29
|
+
return {
|
|
30
|
+
useTranslation: () => {
|
|
31
|
+
return {
|
|
32
|
+
t: (key: string, opts?: { defaultValue?: string }): string => {
|
|
33
|
+
return opts?.defaultValue ?? key;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
type CapturedFormProps = {
|
|
41
|
+
initialValues: Record<string, unknown>;
|
|
42
|
+
onBeforeCreate?:
|
|
43
|
+
| ((
|
|
44
|
+
item: unknown,
|
|
45
|
+
miscDataProps: Record<string, unknown>,
|
|
46
|
+
) => Promise<unknown>)
|
|
47
|
+
| undefined;
|
|
48
|
+
fields: Array<Record<string, unknown>>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let capturedForms: Array<CapturedFormProps> = [];
|
|
52
|
+
|
|
53
|
+
jest.mock("../../../UI/Components/Forms/ModelForm", () => {
|
|
54
|
+
// Only the component is stubbed: the page imports FormType from here too.
|
|
55
|
+
const actual: Record<string, unknown> = jest.requireActual(
|
|
56
|
+
"../../../UI/Components/Forms/ModelForm",
|
|
57
|
+
) as Record<string, unknown>;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
__esModule: true,
|
|
61
|
+
...actual,
|
|
62
|
+
default: (props: CapturedFormProps): React.ReactElement => {
|
|
63
|
+
capturedForms.push(props);
|
|
64
|
+
return <div data-testid="model-form" />;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
70
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
71
|
+
|
|
72
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
73
|
+
return {
|
|
74
|
+
__esModule: true,
|
|
75
|
+
default: {
|
|
76
|
+
getList: (...args: Array<any>) => {
|
|
77
|
+
return getListMock(...args);
|
|
78
|
+
},
|
|
79
|
+
getItem: (...args: Array<any>) => {
|
|
80
|
+
return getItemMock(...args);
|
|
81
|
+
},
|
|
82
|
+
create: async (): Promise<null> => {
|
|
83
|
+
return null;
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
import IncidentCreate from "../../../../App/FeatureSet/Dashboard/src/Pages/Incidents/Create";
|
|
90
|
+
import Alert from "../../../Models/DatabaseModels/Alert";
|
|
91
|
+
import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
|
|
92
|
+
import Host from "../../../Models/DatabaseModels/Host";
|
|
93
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
94
|
+
import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
|
|
95
|
+
import IncidentState from "../../../Models/DatabaseModels/IncidentState";
|
|
96
|
+
import IncidentTemplate from "../../../Models/DatabaseModels/IncidentTemplate";
|
|
97
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
98
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
99
|
+
import OnCallDutyPolicy from "../../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
100
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
101
|
+
import Service from "../../../Models/DatabaseModels/Service";
|
|
102
|
+
import Route from "../../../Types/API/Route";
|
|
103
|
+
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
104
|
+
import {
|
|
105
|
+
INCIDENT_ALERT_IDS_TO_LINK_KEY,
|
|
106
|
+
INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM,
|
|
107
|
+
MAX_ALERTS_PER_INCIDENT_LINK_ACTION,
|
|
108
|
+
} from "../../../Types/Incident/IncidentAlertLink";
|
|
109
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
110
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
111
|
+
import ProjectUtil from "../../../UI/Utils/Project";
|
|
112
|
+
|
|
113
|
+
const PROJECT_ID: string = "8f2a1b3c-4d5e-4f60-9a7b-1c2d3e4f5a6b";
|
|
114
|
+
const FIRST_STATE_ID: string = "55555555-5555-4555-8555-000000000001";
|
|
115
|
+
const TEMPLATE_ID: string = "66666666-6666-4666-8666-000000000001";
|
|
116
|
+
|
|
117
|
+
const ALERT_ONE_ID: string = "22222222-2222-4222-8222-000000000001";
|
|
118
|
+
const ALERT_TWO_ID: string = "22222222-2222-4222-8222-000000000002";
|
|
119
|
+
const ALERT_THREE_ID: string = "22222222-2222-4222-8222-000000000003";
|
|
120
|
+
|
|
121
|
+
const ALERT_HIGH_ID: string = "77777777-7777-4777-8777-000000000001";
|
|
122
|
+
const ALERT_LOW_ID: string = "77777777-7777-4777-8777-000000000002";
|
|
123
|
+
const INCIDENT_CRITICAL_ID: string = "88888888-8888-4888-8888-000000000001";
|
|
124
|
+
const INCIDENT_MAJOR_ID: string = "88888888-8888-4888-8888-000000000002";
|
|
125
|
+
|
|
126
|
+
type MakeNamedFunction = <T extends { _id?: string; name?: string }>(
|
|
127
|
+
modelType: { new (): T },
|
|
128
|
+
id: string,
|
|
129
|
+
name: string,
|
|
130
|
+
) => T;
|
|
131
|
+
|
|
132
|
+
const makeNamed: MakeNamedFunction = <
|
|
133
|
+
T extends { _id?: string; name?: string },
|
|
134
|
+
>(
|
|
135
|
+
modelType: { new (): T },
|
|
136
|
+
id: string,
|
|
137
|
+
name: string,
|
|
138
|
+
): T => {
|
|
139
|
+
const model: T = new modelType();
|
|
140
|
+
model._id = id;
|
|
141
|
+
model.name = name;
|
|
142
|
+
return model;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
type MakeLabelFunction = (id: string) => Label;
|
|
146
|
+
|
|
147
|
+
const makeLabel: MakeLabelFunction = (id: string): Label => {
|
|
148
|
+
const label: Label = new Label();
|
|
149
|
+
label._id = id;
|
|
150
|
+
return label;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* Alert one is Low severity on monitor "API"; alert two is High severity,
|
|
155
|
+
* private, on monitor "Database" plus a host and a service. Both share a
|
|
156
|
+
* label, so the prefill has to dedupe.
|
|
157
|
+
*/
|
|
158
|
+
type MakeAlertsFunction = () => Array<Alert>;
|
|
159
|
+
|
|
160
|
+
const makeAlerts: MakeAlertsFunction = (): Array<Alert> => {
|
|
161
|
+
const one: Alert = new Alert();
|
|
162
|
+
one._id = ALERT_ONE_ID;
|
|
163
|
+
one.title = "API latency is high";
|
|
164
|
+
one.description = "p99 above 2s.";
|
|
165
|
+
one.alertNumber = 11;
|
|
166
|
+
one.alertSeverityId = new ObjectID(ALERT_LOW_ID);
|
|
167
|
+
one.monitor = makeNamed(Monitor, "m-api", "API");
|
|
168
|
+
one.hosts = [];
|
|
169
|
+
one.labels = [makeLabel("label-shared"), makeLabel("label-api")];
|
|
170
|
+
one.isPrivate = false;
|
|
171
|
+
|
|
172
|
+
const two: Alert = new Alert();
|
|
173
|
+
two._id = ALERT_TWO_ID;
|
|
174
|
+
two.title = "Database is down";
|
|
175
|
+
two.description = "Primary unreachable.";
|
|
176
|
+
two.alertNumber = 12;
|
|
177
|
+
two.alertNumberWithPrefix = "ALT-12";
|
|
178
|
+
two.alertSeverityId = new ObjectID(ALERT_HIGH_ID);
|
|
179
|
+
two.monitor = makeNamed(Monitor, "m-db", "Database");
|
|
180
|
+
two.hosts = [makeNamed(Host, "h-db", "db-1")];
|
|
181
|
+
two.services = [makeNamed(Service, "s-checkout", "Checkout")];
|
|
182
|
+
two.labels = [makeLabel("label-shared")];
|
|
183
|
+
two.isPrivate = true;
|
|
184
|
+
|
|
185
|
+
return [one, two];
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
type ListResultFunction = (data: Array<unknown>) => {
|
|
189
|
+
data: Array<unknown>;
|
|
190
|
+
count: number;
|
|
191
|
+
skip: number;
|
|
192
|
+
limit: number;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const listResult: ListResultFunction = (data: Array<unknown>) => {
|
|
196
|
+
return { data: data, count: data.length, skip: 0, limit: data.length };
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
interface ApiFixture {
|
|
200
|
+
alerts: Array<Alert>;
|
|
201
|
+
failAlerts?: boolean;
|
|
202
|
+
failSeverities?: boolean;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let fixture: ApiFixture = { alerts: [] };
|
|
206
|
+
let queryParams: Record<string, string> = {};
|
|
207
|
+
|
|
208
|
+
type AnswerListFunction = (request: any) => Promise<unknown>;
|
|
209
|
+
|
|
210
|
+
const answerList: AnswerListFunction = async (
|
|
211
|
+
request: any,
|
|
212
|
+
): Promise<unknown> => {
|
|
213
|
+
if (request.modelType === IncidentState) {
|
|
214
|
+
const state: IncidentState = new IncidentState();
|
|
215
|
+
state._id = FIRST_STATE_ID;
|
|
216
|
+
return listResult([state]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (request.modelType === Alert) {
|
|
220
|
+
if (fixture.failAlerts) {
|
|
221
|
+
throw new Error("Could not read the alerts.");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Answer in reverse so the page has to restore the link's order.
|
|
225
|
+
const ids: Array<string> = (request.query._id as Includes).values.map(
|
|
226
|
+
(value: unknown): string => {
|
|
227
|
+
return String(value);
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
return listResult(
|
|
232
|
+
fixture.alerts
|
|
233
|
+
.filter((alert: Alert): boolean => {
|
|
234
|
+
return ids.includes(alert._id || "");
|
|
235
|
+
})
|
|
236
|
+
.reverse(),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (request.modelType === AlertSeverity) {
|
|
241
|
+
if (fixture.failSeverities) {
|
|
242
|
+
throw new Error("Forbidden");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return listResult(
|
|
246
|
+
[
|
|
247
|
+
makeNamed(AlertSeverity, ALERT_LOW_ID, "Low"),
|
|
248
|
+
makeNamed(AlertSeverity, ALERT_HIGH_ID, "High"),
|
|
249
|
+
].map((severity: AlertSeverity, index: number): AlertSeverity => {
|
|
250
|
+
severity.order = index === 0 ? 2 : 1;
|
|
251
|
+
return severity;
|
|
252
|
+
}),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (request.modelType === IncidentSeverity) {
|
|
257
|
+
if (fixture.failSeverities) {
|
|
258
|
+
throw new Error("Forbidden");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const critical: IncidentSeverity = makeNamed(
|
|
262
|
+
IncidentSeverity,
|
|
263
|
+
INCIDENT_CRITICAL_ID,
|
|
264
|
+
"Critical Incident",
|
|
265
|
+
);
|
|
266
|
+
critical.order = 1;
|
|
267
|
+
const major: IncidentSeverity = makeNamed(
|
|
268
|
+
IncidentSeverity,
|
|
269
|
+
INCIDENT_MAJOR_ID,
|
|
270
|
+
"Major Incident",
|
|
271
|
+
);
|
|
272
|
+
major.order = 2;
|
|
273
|
+
|
|
274
|
+
return listResult([major, critical]);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// The template owner lists.
|
|
278
|
+
return listResult([]);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
type AlertRequestsFunction = () => Array<any>;
|
|
282
|
+
|
|
283
|
+
const alertRequests: AlertRequestsFunction = (): Array<any> => {
|
|
284
|
+
return getListMock.mock.calls
|
|
285
|
+
.map((call: Array<any>): any => {
|
|
286
|
+
return call[0];
|
|
287
|
+
})
|
|
288
|
+
.filter((request: any): boolean => {
|
|
289
|
+
return request.modelType === Alert;
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
type OpenPageFunction = () => Promise<CapturedFormProps>;
|
|
294
|
+
|
|
295
|
+
const openPage: OpenPageFunction = async (): Promise<CapturedFormProps> => {
|
|
296
|
+
render(
|
|
297
|
+
<MemoryRouter>
|
|
298
|
+
<IncidentCreate
|
|
299
|
+
pageRoute={new Route("/dashboard/incidents/create")}
|
|
300
|
+
currentProject={new Project()}
|
|
301
|
+
hasPaymentMethod={true}
|
|
302
|
+
/>
|
|
303
|
+
</MemoryRouter>,
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
await waitFor(() => {
|
|
307
|
+
expect(capturedForms.length).toBeGreaterThan(0);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return capturedForms[capturedForms.length - 1]!;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
type MiscDataFunction = (
|
|
314
|
+
form: CapturedFormProps,
|
|
315
|
+
) => Promise<Record<string, unknown>>;
|
|
316
|
+
|
|
317
|
+
// What the form would send as miscDataProps, after the page's onBeforeCreate.
|
|
318
|
+
const miscDataSentBy: MiscDataFunction = async (
|
|
319
|
+
form: CapturedFormProps,
|
|
320
|
+
): Promise<Record<string, unknown>> => {
|
|
321
|
+
const miscDataProps: Record<string, unknown> = {};
|
|
322
|
+
const item: Incident = new Incident();
|
|
323
|
+
|
|
324
|
+
expect(form.onBeforeCreate).toBeDefined();
|
|
325
|
+
|
|
326
|
+
const returned: unknown = await form.onBeforeCreate!(item, miscDataProps);
|
|
327
|
+
|
|
328
|
+
expect(returned).toBe(item);
|
|
329
|
+
|
|
330
|
+
return miscDataProps;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
type AlertIdsFunction = (count: number) => Array<string>;
|
|
334
|
+
|
|
335
|
+
const manyAlertIds: AlertIdsFunction = (count: number): Array<string> => {
|
|
336
|
+
return Array.from({ length: count }, (_value: unknown, index: number) => {
|
|
337
|
+
return `22222222-2222-4222-8222-${(index + 100).toString().padStart(12, "0")}`;
|
|
338
|
+
});
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
describe("declaring an incident from alerts", () => {
|
|
342
|
+
beforeEach(() => {
|
|
343
|
+
capturedForms = [];
|
|
344
|
+
fixture = { alerts: makeAlerts() };
|
|
345
|
+
queryParams = {};
|
|
346
|
+
getListMock.mockReset();
|
|
347
|
+
getItemMock.mockReset();
|
|
348
|
+
getListMock.mockImplementation(answerList);
|
|
349
|
+
|
|
350
|
+
jest
|
|
351
|
+
.spyOn(ProjectUtil, "getCurrentProjectId")
|
|
352
|
+
.mockReturnValue(new ObjectID(PROJECT_ID));
|
|
353
|
+
jest
|
|
354
|
+
.spyOn(Navigation, "getQueryStringByName")
|
|
355
|
+
.mockImplementation((name: string): string | null => {
|
|
356
|
+
return queryParams[name] ?? null;
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
afterEach(() => {
|
|
361
|
+
cleanup();
|
|
362
|
+
jest.restoreAllMocks();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("reads the shared query parameter name", () => {
|
|
366
|
+
expect(INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM).toBe("alertIds");
|
|
367
|
+
expect(INCIDENT_ALERT_IDS_TO_LINK_KEY).toBe("alertIdsToLink");
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe("without alert ids", () => {
|
|
371
|
+
test("renders the form straight away, reads no alerts and shows no banner", async () => {
|
|
372
|
+
const form: CapturedFormProps = await openPage();
|
|
373
|
+
|
|
374
|
+
expect(capturedForms[0]!.initialValues).toEqual({});
|
|
375
|
+
expect(alertRequests()).toHaveLength(0);
|
|
376
|
+
expect(
|
|
377
|
+
screen.queryByTestId("incident-create-alerts-to-link"),
|
|
378
|
+
).not.toBeInTheDocument();
|
|
379
|
+
expect(
|
|
380
|
+
screen.queryByTestId("incident-create-alerts-not-found"),
|
|
381
|
+
).not.toBeInTheDocument();
|
|
382
|
+
|
|
383
|
+
// The first state still arrives the way it always did.
|
|
384
|
+
await waitFor(() => {
|
|
385
|
+
expect(capturedForms[capturedForms.length - 1]!.initialValues).toEqual({
|
|
386
|
+
currentIncidentState: FIRST_STATE_ID,
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
expect(await miscDataSentBy(form)).toEqual({});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("ignores a parameter holding no valid ids", async () => {
|
|
394
|
+
queryParams = { [INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: "nope,,<b>" };
|
|
395
|
+
|
|
396
|
+
const form: CapturedFormProps = await openPage();
|
|
397
|
+
|
|
398
|
+
expect(alertRequests()).toHaveLength(0);
|
|
399
|
+
expect(
|
|
400
|
+
screen.queryByTestId("incident-create-alerts-to-link"),
|
|
401
|
+
).not.toBeInTheDocument();
|
|
402
|
+
expect(await miscDataSentBy(form)).toEqual({});
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe("with alert ids", () => {
|
|
407
|
+
beforeEach(() => {
|
|
408
|
+
queryParams = {
|
|
409
|
+
[INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: `${ALERT_ONE_ID},${ALERT_TWO_ID}`,
|
|
410
|
+
};
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("reads exactly those alerts, with what the prefill needs", async () => {
|
|
414
|
+
await openPage();
|
|
415
|
+
|
|
416
|
+
const requests: Array<any> = alertRequests();
|
|
417
|
+
|
|
418
|
+
expect(requests).toHaveLength(1);
|
|
419
|
+
expect((requests[0].query._id as Includes).values).toEqual([
|
|
420
|
+
ALERT_ONE_ID,
|
|
421
|
+
ALERT_TWO_ID,
|
|
422
|
+
]);
|
|
423
|
+
expect(requests[0].limit).toBe(MAX_ALERTS_PER_INCIDENT_LINK_ACTION);
|
|
424
|
+
expect(requests[0].select).toEqual(
|
|
425
|
+
expect.objectContaining({
|
|
426
|
+
title: true,
|
|
427
|
+
description: true,
|
|
428
|
+
alertSeverityId: true,
|
|
429
|
+
isPrivate: true,
|
|
430
|
+
monitor: { _id: true, name: true },
|
|
431
|
+
hosts: { _id: true, name: true },
|
|
432
|
+
services: { _id: true, name: true },
|
|
433
|
+
labels: { _id: true },
|
|
434
|
+
}),
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("prefills the form from the alerts before it first renders", async () => {
|
|
439
|
+
await openPage();
|
|
440
|
+
|
|
441
|
+
const expected: Record<string, unknown> = {
|
|
442
|
+
currentIncidentState: FIRST_STATE_ID,
|
|
443
|
+
title: "Database is down",
|
|
444
|
+
description: [
|
|
445
|
+
"- Alert #11: API latency is high",
|
|
446
|
+
"- Alert ALT-12: Database is down",
|
|
447
|
+
].join("\n"),
|
|
448
|
+
incidentSeverity: INCIDENT_CRITICAL_ID,
|
|
449
|
+
monitors: [
|
|
450
|
+
{ _id: "m-api", name: "API" },
|
|
451
|
+
{ _id: "m-db", name: "Database" },
|
|
452
|
+
],
|
|
453
|
+
hosts: [{ _id: "h-db", name: "db-1" }],
|
|
454
|
+
services: [{ _id: "s-checkout", name: "Checkout" }],
|
|
455
|
+
labels: ["label-shared", "label-api"],
|
|
456
|
+
isPrivate: true,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// Every render, including the first: the form latches initial values.
|
|
460
|
+
for (const form of capturedForms) {
|
|
461
|
+
expect(form.initialValues).toEqual(expected);
|
|
462
|
+
}
|
|
463
|
+
expect(capturedForms[0]!.initialValues).not.toHaveProperty(
|
|
464
|
+
"onCallDutyPolicies",
|
|
465
|
+
);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("lists the alerts that will be linked, in the link's order", async () => {
|
|
469
|
+
await openPage();
|
|
470
|
+
|
|
471
|
+
const banner: HTMLElement = screen.getByTestId(
|
|
472
|
+
"incident-create-alerts-to-link",
|
|
473
|
+
);
|
|
474
|
+
const items: Array<HTMLLIElement> = Array.from(
|
|
475
|
+
banner.querySelectorAll("li"),
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
expect(banner).toHaveTextContent("Declaring this incident from alerts");
|
|
479
|
+
expect(
|
|
480
|
+
items.map((item: HTMLLIElement): string => {
|
|
481
|
+
return item.textContent || "";
|
|
482
|
+
}),
|
|
483
|
+
).toEqual([
|
|
484
|
+
"Alert #11:API latency is high",
|
|
485
|
+
"Alert ALT-12:Database is down",
|
|
486
|
+
]);
|
|
487
|
+
expect(items[1]!.querySelector("a")?.getAttribute("href")).toBe(
|
|
488
|
+
`/dashboard/${PROJECT_ID}/alerts/${ALERT_TWO_ID}`,
|
|
489
|
+
);
|
|
490
|
+
expect(
|
|
491
|
+
screen.queryByTestId("incident-create-alerts-not-found"),
|
|
492
|
+
).not.toBeInTheDocument();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
/*
|
|
496
|
+
* Alert two is private, so the incident is prefilled private - visible
|
|
497
|
+
* only to its owners and admins. The banner says so, and that the alerts'
|
|
498
|
+
* owners become the incident's owners, before the user declares it. The
|
|
499
|
+
* user can still switch Private Incident off, so it says both only hold
|
|
500
|
+
* while the switch stays on.
|
|
501
|
+
*/
|
|
502
|
+
test("says the incident starts private, and who will own it, when an alert is private", async () => {
|
|
503
|
+
await openPage();
|
|
504
|
+
|
|
505
|
+
const banner: HTMLElement = screen.getByTestId(
|
|
506
|
+
"incident-create-alerts-to-link",
|
|
507
|
+
);
|
|
508
|
+
const note: HTMLElement = screen.getByTestId(
|
|
509
|
+
"incident-create-private-from-alerts",
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
expect(banner).toContainElement(note);
|
|
513
|
+
expect(note).toHaveTextContent(
|
|
514
|
+
"At least one of these alerts is private, so Private Incident starts switched on. While it stays on, only the incident's owners, project owners and project admins can see it, and the owners of these alerts are added as its owners once it is declared.",
|
|
515
|
+
);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("says nothing about privacy when no alert is private", async () => {
|
|
519
|
+
queryParams = { [INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: ALERT_ONE_ID };
|
|
520
|
+
|
|
521
|
+
await openPage();
|
|
522
|
+
|
|
523
|
+
expect(
|
|
524
|
+
screen.getByTestId("incident-create-alerts-to-link"),
|
|
525
|
+
).toBeInTheDocument();
|
|
526
|
+
expect(
|
|
527
|
+
screen.queryByTestId("incident-create-private-from-alerts"),
|
|
528
|
+
).not.toBeInTheDocument();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("sends the alert ids to the server as miscDataProps", async () => {
|
|
532
|
+
const form: CapturedFormProps = await openPage();
|
|
533
|
+
|
|
534
|
+
expect(await miscDataSentBy(form)).toEqual({
|
|
535
|
+
[INCIDENT_ALERT_IDS_TO_LINK_KEY]: [ALERT_ONE_ID, ALERT_TWO_ID],
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test("prefills a single alert's own title and description", async () => {
|
|
540
|
+
queryParams = { [INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: ALERT_ONE_ID };
|
|
541
|
+
|
|
542
|
+
const form: CapturedFormProps = await openPage();
|
|
543
|
+
|
|
544
|
+
expect(form.initialValues["title"]).toBe("API latency is high");
|
|
545
|
+
expect(form.initialValues["description"]).toBe("p99 above 2s.");
|
|
546
|
+
expect(form.initialValues["incidentSeverity"]).toBe(INCIDENT_MAJOR_ID);
|
|
547
|
+
expect(form.initialValues["isPrivate"]).toBeUndefined();
|
|
548
|
+
expect(await miscDataSentBy(form)).toEqual({
|
|
549
|
+
[INCIDENT_ALERT_IDS_TO_LINK_KEY]: [ALERT_ONE_ID],
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("leaves the severity to the user when severities cannot be read", async () => {
|
|
554
|
+
fixture.failSeverities = true;
|
|
555
|
+
|
|
556
|
+
const form: CapturedFormProps = await openPage();
|
|
557
|
+
|
|
558
|
+
// No severity to rank by, so the first alert names the incident.
|
|
559
|
+
expect(form.initialValues["title"]).toBe("API latency is high");
|
|
560
|
+
expect(form.initialValues["incidentSeverity"]).toBeUndefined();
|
|
561
|
+
expect(form.initialValues["monitors"]).toHaveLength(2);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test("links only the alerts it could read, and says some were missing", async () => {
|
|
565
|
+
queryParams = {
|
|
566
|
+
[INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: `${ALERT_ONE_ID},${ALERT_THREE_ID}`,
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const form: CapturedFormProps = await openPage();
|
|
570
|
+
|
|
571
|
+
expect(
|
|
572
|
+
screen.getByTestId("incident-create-alerts-to-link"),
|
|
573
|
+
).toHaveTextContent("Some alerts could not be found");
|
|
574
|
+
expect(await miscDataSentBy(form)).toEqual({
|
|
575
|
+
[INCIDENT_ALERT_IDS_TO_LINK_KEY]: [ALERT_ONE_ID],
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test("warns and links nothing when none of the alerts can be found", async () => {
|
|
580
|
+
queryParams = { [INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: ALERT_THREE_ID };
|
|
581
|
+
|
|
582
|
+
const form: CapturedFormProps = await openPage();
|
|
583
|
+
|
|
584
|
+
expect(
|
|
585
|
+
screen.getByTestId("incident-create-alerts-not-found"),
|
|
586
|
+
).toBeInTheDocument();
|
|
587
|
+
expect(
|
|
588
|
+
screen.queryByTestId("incident-create-alerts-to-link"),
|
|
589
|
+
).not.toBeInTheDocument();
|
|
590
|
+
expect(form.initialValues).toEqual({
|
|
591
|
+
currentIncidentState: FIRST_STATE_ID,
|
|
592
|
+
});
|
|
593
|
+
expect(await miscDataSentBy(form)).toEqual({});
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
test("caps the alerts at the per-action maximum and says so", async () => {
|
|
597
|
+
const ids: Array<string> = manyAlertIds(
|
|
598
|
+
MAX_ALERTS_PER_INCIDENT_LINK_ACTION + 2,
|
|
599
|
+
);
|
|
600
|
+
fixture.alerts = ids.map((id: string, index: number): Alert => {
|
|
601
|
+
const alert: Alert = new Alert();
|
|
602
|
+
alert._id = id;
|
|
603
|
+
alert.title = `Alert ${index}`;
|
|
604
|
+
return alert;
|
|
605
|
+
});
|
|
606
|
+
queryParams = { [INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: ids.join(",") };
|
|
607
|
+
|
|
608
|
+
const form: CapturedFormProps = await openPage();
|
|
609
|
+
|
|
610
|
+
expect((alertRequests()[0].query._id as Includes).values).toEqual(
|
|
611
|
+
ids.slice(0, MAX_ALERTS_PER_INCIDENT_LINK_ACTION),
|
|
612
|
+
);
|
|
613
|
+
expect(
|
|
614
|
+
screen.getByTestId("incident-create-alerts-to-link"),
|
|
615
|
+
).toHaveTextContent(
|
|
616
|
+
`Only the first ${MAX_ALERTS_PER_INCIDENT_LINK_ACTION} alerts are linked`,
|
|
617
|
+
);
|
|
618
|
+
expect(
|
|
619
|
+
(
|
|
620
|
+
(await miscDataSentBy(form))[
|
|
621
|
+
INCIDENT_ALERT_IDS_TO_LINK_KEY
|
|
622
|
+
] as Array<string>
|
|
623
|
+
).length,
|
|
624
|
+
).toBe(MAX_ALERTS_PER_INCIDENT_LINK_ACTION);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
test("shows the error instead of a form when the alerts cannot be read", async () => {
|
|
628
|
+
fixture.failAlerts = true;
|
|
629
|
+
|
|
630
|
+
render(
|
|
631
|
+
<MemoryRouter>
|
|
632
|
+
<IncidentCreate
|
|
633
|
+
pageRoute={new Route("/dashboard/incidents/create")}
|
|
634
|
+
currentProject={new Project()}
|
|
635
|
+
hasPaymentMethod={true}
|
|
636
|
+
/>
|
|
637
|
+
</MemoryRouter>,
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
expect(
|
|
641
|
+
await screen.findByText("Could not read the alerts."),
|
|
642
|
+
).toBeInTheDocument();
|
|
643
|
+
expect(capturedForms).toHaveLength(0);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
test("composes with a template: the template's choices stand, lists are unioned", async () => {
|
|
647
|
+
const template: IncidentTemplate = new IncidentTemplate();
|
|
648
|
+
template._id = TEMPLATE_ID;
|
|
649
|
+
template.title = "Template title";
|
|
650
|
+
template.description = "Template description";
|
|
651
|
+
template.monitors = [
|
|
652
|
+
makeNamed(Monitor, "m-template", "Template monitor"),
|
|
653
|
+
];
|
|
654
|
+
template.labels = [makeLabel("label-template")];
|
|
655
|
+
template.onCallDutyPolicies = [
|
|
656
|
+
makeNamed(OnCallDutyPolicy, "policy-template", "Primary"),
|
|
657
|
+
];
|
|
658
|
+
getItemMock.mockResolvedValue(template);
|
|
659
|
+
queryParams = {
|
|
660
|
+
incidentTemplateId: TEMPLATE_ID,
|
|
661
|
+
[INCIDENT_CREATE_ALERT_IDS_QUERY_PARAM]: `${ALERT_ONE_ID},${ALERT_TWO_ID}`,
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const form: CapturedFormProps = await openPage();
|
|
665
|
+
|
|
666
|
+
expect(getItemMock).toHaveBeenCalledTimes(1);
|
|
667
|
+
expect(form.initialValues["title"]).toBe("Template title");
|
|
668
|
+
expect(form.initialValues["description"]).toBe("Template description");
|
|
669
|
+
expect(form.initialValues["incidentSeverity"]).toBe(INCIDENT_CRITICAL_ID);
|
|
670
|
+
expect(form.initialValues["monitors"]).toEqual([
|
|
671
|
+
{ _id: "m-template", name: "Template monitor" },
|
|
672
|
+
{ _id: "m-api", name: "API" },
|
|
673
|
+
{ _id: "m-db", name: "Database" },
|
|
674
|
+
]);
|
|
675
|
+
expect(form.initialValues["labels"]).toEqual([
|
|
676
|
+
"label-template",
|
|
677
|
+
"label-shared",
|
|
678
|
+
"label-api",
|
|
679
|
+
]);
|
|
680
|
+
// The template's own policies only - none come from the alerts.
|
|
681
|
+
expect(form.initialValues["onCallDutyPolicies"]).toEqual([
|
|
682
|
+
"policy-template",
|
|
683
|
+
]);
|
|
684
|
+
expect(await miscDataSentBy(form)).toEqual({
|
|
685
|
+
[INCIDENT_ALERT_IDS_TO_LINK_KEY]: [ALERT_ONE_ID, ALERT_TWO_ID],
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
});
|