@oneuptime/common 14.0.0 → 14.0.1
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/Index.ts +4 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/DatabaseService.ts +10 -0
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorService.ts +3 -2
- package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
- package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
- package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
- package/Server/Utils/Monitor/RootCauseList.ts +139 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
- package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
- package/Tests/Server/Types/Markdown.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
- package/Tests/Types/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
- package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- package/Tests/UI/Components/Icon.test.tsx +26 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
- package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
- package/Types/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/UI/Components/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/Utils/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.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/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +3 -2
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
- package/build/dist/Types/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,613 @@
|
|
|
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
|
+
RenderResult,
|
|
16
|
+
screen,
|
|
17
|
+
waitFor,
|
|
18
|
+
within,
|
|
19
|
+
} from "@testing-library/react";
|
|
20
|
+
import React from "react";
|
|
21
|
+
import { MemoryRouter } from "react-router-dom";
|
|
22
|
+
import SloView from "../../../../App/FeatureSet/Dashboard/src/Pages/Slo/View/Index";
|
|
23
|
+
import { UseSloOverviewDataResult } from "../../../../App/FeatureSet/Dashboard/src/Components/Slo/useSloOverviewData";
|
|
24
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
25
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
26
|
+
import ServiceLevelObjective from "../../../Models/DatabaseModels/ServiceLevelObjective";
|
|
27
|
+
import Route from "../../../Types/API/Route";
|
|
28
|
+
import { Red } from "../../../Types/BrandColors";
|
|
29
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
30
|
+
import Permission from "../../../Types/Permission";
|
|
31
|
+
import SloStatus from "../../../Types/ServiceLevelObjective/SloStatus";
|
|
32
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
33
|
+
import PermissionGate from "../../../UI/Utils/PermissionGate";
|
|
34
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
* Render the overview and its real details card, field renderer and edit
|
|
38
|
+
* form. Only unrelated overview sections and the polling hook are replaced:
|
|
39
|
+
* this catches a card left below the grid, duplicate cards, broken sidebar
|
|
40
|
+
* headers, and edits that stop reaching the SLO or refreshing its summary.
|
|
41
|
+
*/
|
|
42
|
+
const SLO_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
|
|
43
|
+
const PROJECT_ID: string = "11111111-1111-4111-8111-111111111111";
|
|
44
|
+
const EDIT_LABEL: string = "Edit Service Level Objective";
|
|
45
|
+
|
|
46
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
47
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
48
|
+
const createOrUpdateMock: MockFunction = getJestMockFunction();
|
|
49
|
+
const refreshMock: MockFunction = getJestMockFunction();
|
|
50
|
+
|
|
51
|
+
let overviewData: UseSloOverviewDataResult;
|
|
52
|
+
let storedSlo: ServiceLevelObjective;
|
|
53
|
+
let permissionsForTest: Array<Permission>;
|
|
54
|
+
|
|
55
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
56
|
+
return {
|
|
57
|
+
__esModule: true,
|
|
58
|
+
default: {
|
|
59
|
+
getItem: (...args: Array<unknown>): unknown => {
|
|
60
|
+
return getItemMock(...args);
|
|
61
|
+
},
|
|
62
|
+
getList: (...args: Array<unknown>): unknown => {
|
|
63
|
+
return getListMock(...args);
|
|
64
|
+
},
|
|
65
|
+
createOrUpdate: (...args: Array<unknown>): unknown => {
|
|
66
|
+
return createOrUpdateMock(...args);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
jest.mock("../../../UI/Utils/Permission", () => {
|
|
73
|
+
return {
|
|
74
|
+
__esModule: true,
|
|
75
|
+
default: {
|
|
76
|
+
getAllPermissions: (): Array<Permission> => {
|
|
77
|
+
return permissionsForTest;
|
|
78
|
+
},
|
|
79
|
+
getProjectPermissions: (): null => {
|
|
80
|
+
return null;
|
|
81
|
+
},
|
|
82
|
+
getGlobalPermissions: (): { globalPermissions: Array<Permission> } => {
|
|
83
|
+
return { globalPermissions: permissionsForTest };
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
jest.mock("../../../UI/Utils/User", () => {
|
|
90
|
+
return {
|
|
91
|
+
__esModule: true,
|
|
92
|
+
default: {
|
|
93
|
+
isMasterAdmin: (): boolean => {
|
|
94
|
+
return false;
|
|
95
|
+
},
|
|
96
|
+
getUserId: (): null => {
|
|
97
|
+
return null;
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
jest.mock("../../../UI/Utils/Translation", () => {
|
|
104
|
+
return {
|
|
105
|
+
__esModule: true,
|
|
106
|
+
default: () => {
|
|
107
|
+
return {
|
|
108
|
+
translateString: (value: string | undefined): string | undefined => {
|
|
109
|
+
return value;
|
|
110
|
+
},
|
|
111
|
+
translateValue: (value: unknown): unknown => {
|
|
112
|
+
return value;
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
jest.mock(
|
|
120
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/useSloOverviewData",
|
|
121
|
+
() => {
|
|
122
|
+
return {
|
|
123
|
+
__esModule: true,
|
|
124
|
+
default: (): UseSloOverviewDataResult => {
|
|
125
|
+
return overviewData;
|
|
126
|
+
},
|
|
127
|
+
getSloNoticeFingerprint: (): string => {
|
|
128
|
+
return "unchanged-notice";
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
jest.mock(
|
|
135
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloOverviewHero",
|
|
136
|
+
() => {
|
|
137
|
+
return {
|
|
138
|
+
__esModule: true,
|
|
139
|
+
default: (props: { slo: ServiceLevelObjective }): React.ReactElement => {
|
|
140
|
+
return <h1>{props.slo.name}</h1>;
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
jest.mock(
|
|
147
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloNoticeBanner",
|
|
148
|
+
() => {
|
|
149
|
+
return {
|
|
150
|
+
__esModule: true,
|
|
151
|
+
default: (): null => {
|
|
152
|
+
return null;
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
jest.mock(
|
|
159
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloKpiStrip",
|
|
160
|
+
() => {
|
|
161
|
+
return {
|
|
162
|
+
__esModule: true,
|
|
163
|
+
default: (): React.ReactElement => {
|
|
164
|
+
return <div data-testid="kpi-strip" />;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
jest.mock(
|
|
171
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloBudgetBurnDownCard",
|
|
172
|
+
() => {
|
|
173
|
+
return {
|
|
174
|
+
__esModule: true,
|
|
175
|
+
default: (): React.ReactElement => {
|
|
176
|
+
return <section aria-label="Budget burn-down" />;
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
jest.mock(
|
|
183
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloFeed",
|
|
184
|
+
() => {
|
|
185
|
+
return {
|
|
186
|
+
__esModule: true,
|
|
187
|
+
default: (): React.ReactElement => {
|
|
188
|
+
return <section aria-label="Recent activity" />;
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
jest.mock(
|
|
195
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloOverviewGettingStartedCard",
|
|
196
|
+
() => {
|
|
197
|
+
return {
|
|
198
|
+
__esModule: true,
|
|
199
|
+
default: (): React.ReactElement => {
|
|
200
|
+
return <section aria-label="Choose what this SLO measures" />;
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
jest.mock(
|
|
207
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloActiveBurnEventsCard",
|
|
208
|
+
() => {
|
|
209
|
+
return {
|
|
210
|
+
__esModule: true,
|
|
211
|
+
default: (): React.ReactElement => {
|
|
212
|
+
return <section aria-label="Open alerts and incidents" />;
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
jest.mock(
|
|
219
|
+
"../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloBurnRateRulesSummaryCard",
|
|
220
|
+
() => {
|
|
221
|
+
return {
|
|
222
|
+
__esModule: true,
|
|
223
|
+
default: (): React.ReactElement => {
|
|
224
|
+
return <section aria-label="Burn rate rules" />;
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
type RenderPageFunction = () => RenderResult;
|
|
231
|
+
|
|
232
|
+
const renderPage: RenderPageFunction = (): RenderResult => {
|
|
233
|
+
return render(
|
|
234
|
+
<MemoryRouter>
|
|
235
|
+
<SloView
|
|
236
|
+
pageRoute={new Route("/slos")}
|
|
237
|
+
currentProject={null}
|
|
238
|
+
hasPaymentMethod={true}
|
|
239
|
+
/>
|
|
240
|
+
</MemoryRouter>,
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
type GetDetailsCardFunction = () => HTMLElement;
|
|
245
|
+
|
|
246
|
+
const getDetailsCard: GetDetailsCardFunction = (): HTMLElement => {
|
|
247
|
+
return screen
|
|
248
|
+
.getByRole("heading", { name: "SLO Details" })
|
|
249
|
+
.closest<HTMLElement>('[data-testid="card"]')!;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
type WaitForDetailsFunction = () => Promise<void>;
|
|
253
|
+
|
|
254
|
+
const waitForDetails: WaitForDetailsFunction = async (): Promise<void> => {
|
|
255
|
+
await within(getDetailsCard()).findByText(storedSlo.name!);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
type OpenEditorFunction = () => Promise<HTMLElement>;
|
|
259
|
+
|
|
260
|
+
const openEditor: OpenEditorFunction = async (): Promise<HTMLElement> => {
|
|
261
|
+
const editButton: HTMLElement = await within(getDetailsCard()).findByRole(
|
|
262
|
+
"button",
|
|
263
|
+
{ name: EDIT_LABEL },
|
|
264
|
+
);
|
|
265
|
+
await act(async () => {
|
|
266
|
+
fireEvent.click(editButton);
|
|
267
|
+
});
|
|
268
|
+
const dialog: HTMLElement = await screen.findByRole("dialog", {
|
|
269
|
+
name: EDIT_LABEL,
|
|
270
|
+
});
|
|
271
|
+
await within(dialog).findByDisplayValue(storedSlo.name!);
|
|
272
|
+
return dialog;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
beforeEach(() => {
|
|
276
|
+
window.history.pushState(
|
|
277
|
+
{},
|
|
278
|
+
"",
|
|
279
|
+
`/dashboard/${PROJECT_ID}/slos/${SLO_ID.toString()}`,
|
|
280
|
+
);
|
|
281
|
+
window.localStorage.clear();
|
|
282
|
+
permissionsForTest = [Permission.ProjectOwner];
|
|
283
|
+
PermissionGate.clearPermissionPropsCache();
|
|
284
|
+
getItemMock.mockReset();
|
|
285
|
+
getListMock.mockReset();
|
|
286
|
+
createOrUpdateMock.mockReset();
|
|
287
|
+
refreshMock.mockReset();
|
|
288
|
+
|
|
289
|
+
const monitor: Monitor = new Monitor();
|
|
290
|
+
monitor.id = new ObjectID("33333333-3333-4333-8333-333333333333");
|
|
291
|
+
storedSlo = new ServiceLevelObjective();
|
|
292
|
+
storedSlo.id = SLO_ID;
|
|
293
|
+
storedSlo.name = "Checkout availability";
|
|
294
|
+
storedSlo.description = "Availability of the checkout API.";
|
|
295
|
+
storedSlo.isEnabled = true;
|
|
296
|
+
storedSlo.sloStatus = SloStatus.Healthy;
|
|
297
|
+
storedSlo.monitors = [monitor];
|
|
298
|
+
storedSlo.labels = [];
|
|
299
|
+
|
|
300
|
+
overviewData = {
|
|
301
|
+
slo: storedSlo,
|
|
302
|
+
burnRateRules: [],
|
|
303
|
+
burnRateRulesError: "",
|
|
304
|
+
monitorRuleCount: 0,
|
|
305
|
+
enabledMonitorRuleCount: 0,
|
|
306
|
+
owners: [],
|
|
307
|
+
isLoadingOwners: false,
|
|
308
|
+
hasLoaded: true,
|
|
309
|
+
error: "",
|
|
310
|
+
refreshError: "",
|
|
311
|
+
isRefreshing: false,
|
|
312
|
+
refreshCount: 1,
|
|
313
|
+
refresh: (): void => {
|
|
314
|
+
refreshMock();
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
getItemMock.mockImplementation((): Promise<ServiceLevelObjective> => {
|
|
319
|
+
return Promise.resolve(storedSlo);
|
|
320
|
+
});
|
|
321
|
+
getListMock.mockResolvedValue({ data: [], count: 0, skip: 0, limit: 10 });
|
|
322
|
+
createOrUpdateMock.mockResolvedValue({ data: {} });
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
afterEach(() => {
|
|
326
|
+
cleanup();
|
|
327
|
+
jest.restoreAllMocks();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe("SLO overview details sidebar", () => {
|
|
331
|
+
test.each([
|
|
332
|
+
["an SLO with monitors", true, 0, false],
|
|
333
|
+
["a new SLO", false, 0, true],
|
|
334
|
+
["an SLO with monitor rules", false, 1, false],
|
|
335
|
+
["an SLO whose rule count is unavailable", false, null, false],
|
|
336
|
+
])(
|
|
337
|
+
"%s has one details card first in the sidebar",
|
|
338
|
+
async (
|
|
339
|
+
_name: string,
|
|
340
|
+
hasMonitors: boolean,
|
|
341
|
+
monitorRuleCount: number | null,
|
|
342
|
+
isGettingStarted: boolean,
|
|
343
|
+
) => {
|
|
344
|
+
if (!hasMonitors) {
|
|
345
|
+
storedSlo.monitors = [];
|
|
346
|
+
}
|
|
347
|
+
overviewData.monitorRuleCount = monitorRuleCount;
|
|
348
|
+
|
|
349
|
+
renderPage();
|
|
350
|
+
await waitForDetails();
|
|
351
|
+
|
|
352
|
+
expect(
|
|
353
|
+
screen.getAllByRole("heading", { name: "SLO Details" }),
|
|
354
|
+
).toHaveLength(1);
|
|
355
|
+
const details: HTMLElement = getDetailsCard();
|
|
356
|
+
const events: HTMLElement = screen.getByRole("region", {
|
|
357
|
+
name: "Open alerts and incidents",
|
|
358
|
+
});
|
|
359
|
+
const rules: HTMLElement = screen.getByRole("region", {
|
|
360
|
+
name: "Burn rate rules",
|
|
361
|
+
});
|
|
362
|
+
const sidebar: HTMLElement = events.parentElement!;
|
|
363
|
+
expect(details.parentElement).toBe(sidebar);
|
|
364
|
+
expect(Array.from(sidebar.children)).toEqual([details, events, rules]);
|
|
365
|
+
|
|
366
|
+
const activity: HTMLElement = screen.getByRole("region", {
|
|
367
|
+
name: "Recent activity",
|
|
368
|
+
});
|
|
369
|
+
const main: HTMLElement = activity.parentElement!;
|
|
370
|
+
expect(main).not.toBe(sidebar);
|
|
371
|
+
expect(main).toHaveClass("xl:col-span-2");
|
|
372
|
+
expect(main).not.toContainElement(details);
|
|
373
|
+
expect(main.nextElementSibling).toBe(sidebar);
|
|
374
|
+
expect(main.parentElement).toHaveClass(
|
|
375
|
+
"grid",
|
|
376
|
+
"grid-cols-1",
|
|
377
|
+
"xl:grid-cols-3",
|
|
378
|
+
);
|
|
379
|
+
expect(
|
|
380
|
+
within(main).getByRole("region", {
|
|
381
|
+
name: isGettingStarted
|
|
382
|
+
? "Choose what this SLO measures"
|
|
383
|
+
: "Budget burn-down",
|
|
384
|
+
}),
|
|
385
|
+
).toBeInTheDocument();
|
|
386
|
+
expect(screen.queryByTestId("kpi-strip") !== null).toBe(
|
|
387
|
+
!isGettingStarted,
|
|
388
|
+
);
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
test("stacks the title above the documentation and edit actions for the narrow column", async () => {
|
|
393
|
+
renderPage();
|
|
394
|
+
await waitForDetails();
|
|
395
|
+
|
|
396
|
+
const details: HTMLElement = getDetailsCard();
|
|
397
|
+
const header: HTMLElement = within(details).getByTestId("card-header");
|
|
398
|
+
const actions: HTMLElement = within(header).getByTestId(
|
|
399
|
+
"card-header-actions",
|
|
400
|
+
);
|
|
401
|
+
expect(header).toHaveAttribute("data-header-layout", "stacked");
|
|
402
|
+
expect(actions).toHaveClass("flex-wrap");
|
|
403
|
+
expect(
|
|
404
|
+
within(actions).getByRole("button", { name: EDIT_LABEL }),
|
|
405
|
+
).toBeEnabled();
|
|
406
|
+
expect(
|
|
407
|
+
within(actions).getByRole("button", { name: "View Documentation" }),
|
|
408
|
+
).toBeInTheDocument();
|
|
409
|
+
expect(
|
|
410
|
+
within(actions).queryByRole("heading", { name: "SLO Details" }),
|
|
411
|
+
).toBeNull();
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
test("keeps the documentation action pointed at the SLO guide", async () => {
|
|
415
|
+
const navigate: ReturnType<typeof jest.spyOn> = jest
|
|
416
|
+
.spyOn(Navigation, "navigate")
|
|
417
|
+
.mockImplementation((): void => {});
|
|
418
|
+
|
|
419
|
+
renderPage();
|
|
420
|
+
await waitForDetails();
|
|
421
|
+
fireEvent.click(
|
|
422
|
+
within(getDetailsCard()).getByRole("button", {
|
|
423
|
+
name: "View Documentation",
|
|
424
|
+
}),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
expect(navigate).toHaveBeenCalledTimes(1);
|
|
428
|
+
expect(navigate.mock.calls[0]![0]!.toString()).toBe(
|
|
429
|
+
"/docs/slo/error-budget",
|
|
430
|
+
);
|
|
431
|
+
expect(navigate.mock.calls[0]![1]).toEqual({ openInNewTab: true });
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("renders the name, description and label together in a single column", async () => {
|
|
435
|
+
const label: Label = new Label();
|
|
436
|
+
label.id = new ObjectID("44444444-4444-4444-8444-444444444444");
|
|
437
|
+
label.name = "Customer facing";
|
|
438
|
+
label.color = Red;
|
|
439
|
+
storedSlo.labels = [label];
|
|
440
|
+
|
|
441
|
+
renderPage();
|
|
442
|
+
await waitForDetails();
|
|
443
|
+
|
|
444
|
+
const details: HTMLElement = getDetailsCard();
|
|
445
|
+
expect(
|
|
446
|
+
within(details).getByText(storedSlo.description!),
|
|
447
|
+
).toBeInTheDocument();
|
|
448
|
+
expect(within(details).getByText("Customer facing")).toBeInTheDocument();
|
|
449
|
+
const fields: HTMLElement =
|
|
450
|
+
details.querySelector<HTMLElement>("#slo-details")!;
|
|
451
|
+
expect(fields).toHaveClass("grid-cols-1", "sm:grid-cols-1");
|
|
452
|
+
expect(
|
|
453
|
+
Array.from(fields.querySelectorAll("label")).map(
|
|
454
|
+
(labelElement: HTMLLabelElement): string | null => {
|
|
455
|
+
return labelElement.textContent;
|
|
456
|
+
},
|
|
457
|
+
),
|
|
458
|
+
).toEqual(["Name", "Description", "Labels"]);
|
|
459
|
+
expect(getItemMock).toHaveBeenCalledWith(
|
|
460
|
+
expect.objectContaining({
|
|
461
|
+
modelType: ServiceLevelObjective,
|
|
462
|
+
id: SLO_ID,
|
|
463
|
+
select: {
|
|
464
|
+
_id: true,
|
|
465
|
+
name: true,
|
|
466
|
+
description: true,
|
|
467
|
+
labels: { name: true, color: true },
|
|
468
|
+
},
|
|
469
|
+
}),
|
|
470
|
+
);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test.each(["absent", "empty"])(
|
|
474
|
+
"retains the empty states when description and labels are %s",
|
|
475
|
+
async (emptyState: string) => {
|
|
476
|
+
if (emptyState === "absent") {
|
|
477
|
+
delete storedSlo.description;
|
|
478
|
+
delete storedSlo.labels;
|
|
479
|
+
} else {
|
|
480
|
+
storedSlo.description = "";
|
|
481
|
+
storedSlo.labels = [];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
renderPage();
|
|
485
|
+
await waitForDetails();
|
|
486
|
+
|
|
487
|
+
const details: HTMLElement = getDetailsCard();
|
|
488
|
+
expect(within(details).getByText("No description")).toBeInTheDocument();
|
|
489
|
+
expect(within(details).getByText("No labels")).toBeInTheDocument();
|
|
490
|
+
},
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
test("opens the same SLO's editor and refreshes details and overview after a successful save", async () => {
|
|
494
|
+
createOrUpdateMock.mockImplementation(
|
|
495
|
+
(request: {
|
|
496
|
+
model: ServiceLevelObjective;
|
|
497
|
+
}): Promise<{ data: Record<string, unknown> }> => {
|
|
498
|
+
storedSlo = Object.assign(
|
|
499
|
+
new ServiceLevelObjective(),
|
|
500
|
+
storedSlo,
|
|
501
|
+
request.model,
|
|
502
|
+
);
|
|
503
|
+
return Promise.resolve({ data: {} });
|
|
504
|
+
},
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
renderPage();
|
|
508
|
+
await waitForDetails();
|
|
509
|
+
const sidebar: HTMLElement = getDetailsCard().parentElement!;
|
|
510
|
+
const dialog: HTMLElement = await openEditor();
|
|
511
|
+
expect(
|
|
512
|
+
within(dialog).getByDisplayValue("Availability of the checkout API."),
|
|
513
|
+
).toBeInTheDocument();
|
|
514
|
+
expect(within(dialog).getByText("Labels")).toBeInTheDocument();
|
|
515
|
+
expect(within(dialog).queryByText("Target (%)")).toBeNull();
|
|
516
|
+
|
|
517
|
+
fireEvent.change(
|
|
518
|
+
within(dialog).getByDisplayValue("Checkout availability"),
|
|
519
|
+
{
|
|
520
|
+
target: { value: "Payments availability" },
|
|
521
|
+
},
|
|
522
|
+
);
|
|
523
|
+
fireEvent.change(
|
|
524
|
+
within(dialog).getByDisplayValue("Availability of the checkout API."),
|
|
525
|
+
{ target: { value: "Availability of the payments API." } },
|
|
526
|
+
);
|
|
527
|
+
fireEvent.click(
|
|
528
|
+
within(dialog).getByRole("button", { name: "Save Changes" }),
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
await waitFor(() => {
|
|
532
|
+
expect(screen.queryByRole("dialog")).toBeNull();
|
|
533
|
+
expect(refreshMock).toHaveBeenCalledTimes(1);
|
|
534
|
+
});
|
|
535
|
+
expect(createOrUpdateMock).toHaveBeenCalledTimes(1);
|
|
536
|
+
const request: { model: ServiceLevelObjective; modelType: unknown } =
|
|
537
|
+
createOrUpdateMock.mock.calls[0]![0];
|
|
538
|
+
expect(request.modelType).toBe(ServiceLevelObjective);
|
|
539
|
+
expect(request.model.id?.toString()).toBe(SLO_ID.toString());
|
|
540
|
+
expect(request.model.name).toBe("Payments availability");
|
|
541
|
+
expect(request.model.description).toBe("Availability of the payments API.");
|
|
542
|
+
expect(request.model.targetPercentage).toBeUndefined();
|
|
543
|
+
await within(getDetailsCard()).findByText("Payments availability");
|
|
544
|
+
expect(getDetailsCard().parentElement).toBe(sidebar);
|
|
545
|
+
expect(
|
|
546
|
+
within(getDetailsCard()).getByText("Availability of the payments API."),
|
|
547
|
+
).toBeInTheDocument();
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("canceling an edit keeps the sidebar card and does not save or refresh", async () => {
|
|
551
|
+
renderPage();
|
|
552
|
+
await waitForDetails();
|
|
553
|
+
const details: HTMLElement = getDetailsCard();
|
|
554
|
+
const dialog: HTMLElement = await openEditor();
|
|
555
|
+
fireEvent.click(within(dialog).getByRole("button", { name: "Cancel" }));
|
|
556
|
+
|
|
557
|
+
await waitFor(() => {
|
|
558
|
+
expect(screen.queryByRole("dialog")).toBeNull();
|
|
559
|
+
});
|
|
560
|
+
expect(getDetailsCard()).toBe(details);
|
|
561
|
+
expect(createOrUpdateMock).not.toHaveBeenCalled();
|
|
562
|
+
expect(refreshMock).not.toHaveBeenCalled();
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test("a failed edit stays open without refreshing the overview", async () => {
|
|
566
|
+
createOrUpdateMock.mockRejectedValue(
|
|
567
|
+
new Error("Changes could not be saved."),
|
|
568
|
+
);
|
|
569
|
+
renderPage();
|
|
570
|
+
await waitForDetails();
|
|
571
|
+
const dialog: HTMLElement = await openEditor();
|
|
572
|
+
fireEvent.click(
|
|
573
|
+
within(dialog).getByRole("button", { name: "Save Changes" }),
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
await within(dialog).findByText("Changes could not be saved.");
|
|
577
|
+
expect(screen.getByRole("dialog")).toBe(dialog);
|
|
578
|
+
expect(refreshMock).not.toHaveBeenCalled();
|
|
579
|
+
expect(
|
|
580
|
+
within(getDetailsCard()).getByText("Checkout availability"),
|
|
581
|
+
).toBeInTheDocument();
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
test("moving the card preserves the permission gate on editing", async () => {
|
|
585
|
+
permissionsForTest = [Permission.Viewer];
|
|
586
|
+
renderPage();
|
|
587
|
+
await waitForDetails();
|
|
588
|
+
|
|
589
|
+
const edit: HTMLElement = within(getDetailsCard()).getByRole("button", {
|
|
590
|
+
name: EDIT_LABEL,
|
|
591
|
+
});
|
|
592
|
+
expect(edit).toBeDisabled();
|
|
593
|
+
fireEvent.click(edit);
|
|
594
|
+
expect(screen.queryByRole("dialog")).toBeNull();
|
|
595
|
+
expect(createOrUpdateMock).not.toHaveBeenCalled();
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test.each(["loading", "failed"])(
|
|
599
|
+
"does not mount an editable details card while the overview is %s",
|
|
600
|
+
(state: string) => {
|
|
601
|
+
overviewData.slo = null;
|
|
602
|
+
overviewData.hasLoaded = state !== "loading";
|
|
603
|
+
overviewData.error =
|
|
604
|
+
state === "failed" ? "The SLO could not be loaded." : "";
|
|
605
|
+
|
|
606
|
+
renderPage();
|
|
607
|
+
|
|
608
|
+
expect(screen.queryByRole("heading", { name: "SLO Details" })).toBeNull();
|
|
609
|
+
expect(screen.queryByRole("button", { name: EDIT_LABEL })).toBeNull();
|
|
610
|
+
expect(getItemMock).not.toHaveBeenCalled();
|
|
611
|
+
},
|
|
612
|
+
);
|
|
613
|
+
});
|
|
@@ -258,14 +258,6 @@ const FEED_MODELS: Array<FeedModelSpec> = [
|
|
|
258
258
|
"ServiceLevelObjectiveArchived",
|
|
259
259
|
"ServiceLevelObjectiveRestored",
|
|
260
260
|
],
|
|
261
|
-
/*
|
|
262
|
-
* SLOs have no owner rules and no label rules - the rules they do have
|
|
263
|
-
* (burn rate and monitor rules) post their own events - so these two would
|
|
264
|
-
* be enum members nothing can ever write. Enum values are stored verbatim
|
|
265
|
-
* and can never be removed once rows exist, so they are exempted here
|
|
266
|
-
* rather than added as dead values. The four owner events still apply.
|
|
267
|
-
*/
|
|
268
|
-
exemptSharedEventTypes: ["OwnerRuleExecuted", "LabelRuleExecuted"],
|
|
269
261
|
},
|
|
270
262
|
];
|
|
271
263
|
|