@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.
Files changed (196) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -0
  2. package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
  3. package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
  4. package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
  5. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  9. package/Server/Services/DatabaseService.ts +10 -0
  10. package/Server/Services/Index.ts +4 -0
  11. package/Server/Services/MonitorService.ts +3 -2
  12. package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
  13. package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
  14. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
  15. package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
  16. package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
  17. package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
  18. package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
  19. package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
  20. package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
  21. package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
  22. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  23. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
  24. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
  25. package/Server/Utils/Monitor/RootCauseList.ts +139 -0
  26. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
  27. package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
  28. package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
  29. package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
  30. package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
  31. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
  32. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
  33. package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
  34. package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
  35. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
  36. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
  37. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
  38. package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
  39. package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
  40. package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
  41. package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
  42. package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
  43. package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
  44. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
  45. package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
  46. package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
  47. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  48. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  49. package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
  50. package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
  51. package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
  52. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
  53. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
  54. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
  55. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
  56. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
  57. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
  58. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
  59. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
  60. package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
  61. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
  62. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
  63. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
  64. package/Tests/Server/Types/Markdown.test.ts +2 -1
  65. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
  66. package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
  67. package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
  68. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
  69. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
  70. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
  71. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
  72. package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
  73. package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
  74. package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
  75. package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
  76. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
  77. package/Tests/Types/API/Route.test.ts +56 -0
  78. package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
  79. package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
  80. package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
  81. package/Tests/UI/Components/Icon.test.tsx +26 -0
  82. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  83. package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
  84. package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
  85. package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
  86. package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
  87. package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
  88. package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
  89. package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
  90. package/Types/API/Route.ts +19 -2
  91. package/Types/API/URL.ts +26 -2
  92. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
  93. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
  94. package/Types/Permission.ts +90 -0
  95. package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
  96. package/Types/Rules/RuleRun.ts +4 -0
  97. package/UI/Components/Feed/FeedOptions.ts +427 -0
  98. package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
  99. package/UI/Components/Feed/useFeedItems.ts +28 -9
  100. package/UI/Components/Feed/useFeedOptions.ts +225 -0
  101. package/UI/Components/Icon/Icon.tsx +1 -1
  102. package/Utils/LabelRuleImportExport.ts +27 -9
  103. package/Utils/Monitor/MetricValueFormatter.ts +2 -2
  104. package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
  105. package/Utils/Slo/SloDuration.ts +22 -6
  106. package/Utils/Slo/SloHealth.ts +8 -7
  107. package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
  108. package/Utils/Slo/SloOverviewText.ts +33 -0
  109. package/Utils/Slo/SloProjection.ts +4 -3
  110. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  111. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  112. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
  113. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
  114. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
  115. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
  116. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
  117. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
  118. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
  119. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
  123. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  125. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  126. package/build/dist/Server/Services/DatabaseService.js +7 -0
  127. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  128. package/build/dist/Server/Services/Index.js +4 -0
  129. package/build/dist/Server/Services/Index.js.map +1 -1
  130. package/build/dist/Server/Services/MonitorService.js +3 -2
  131. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  132. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
  133. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
  134. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
  135. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
  136. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
  137. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
  138. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
  139. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
  140. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
  141. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
  142. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
  143. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
  144. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
  145. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  146. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
  147. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
  148. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
  149. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  150. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
  151. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
  152. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  153. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  154. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
  155. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  156. package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
  157. package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
  158. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
  159. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  160. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
  161. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
  162. package/build/dist/Types/API/Route.js +18 -2
  163. package/build/dist/Types/API/Route.js.map +1 -1
  164. package/build/dist/Types/API/URL.js +23 -2
  165. package/build/dist/Types/API/URL.js.map +1 -1
  166. package/build/dist/Types/Permission.js +84 -0
  167. package/build/dist/Types/Permission.js.map +1 -1
  168. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
  169. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  170. package/build/dist/Types/Rules/RuleRun.js +4 -0
  171. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  172. package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
  173. package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
  174. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
  175. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
  176. package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
  177. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
  178. package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
  179. package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
  180. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  181. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  182. package/build/dist/Utils/LabelRuleImportExport.js +22 -6
  183. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  184. package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
  185. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
  186. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
  187. package/build/dist/Utils/Slo/SloDuration.js +13 -5
  188. package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
  189. package/build/dist/Utils/Slo/SloHealth.js +8 -7
  190. package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
  191. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
  192. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
  193. package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
  194. package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
  195. package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
  196. package/package.json +1 -1
@@ -12,11 +12,13 @@ import React from "react";
12
12
  import SloNoticeBanner from "../../../../App/FeatureSet/Dashboard/src/Components/Slo/SloNoticeBanner";
13
13
  import Monitor from "../../../Models/DatabaseModels/Monitor";
14
14
  import ServiceLevelObjective from "../../../Models/DatabaseModels/ServiceLevelObjective";
15
+ import IconProp from "../../../Types/Icon/IconProp";
15
16
  import ObjectID from "../../../Types/ObjectID";
16
17
  import SliType from "../../../Types/ServiceLevelObjective/SliType";
17
18
  import SloMultiMonitorMode from "../../../Types/ServiceLevelObjective/SloMultiMonitorMode";
18
19
  import SloStatus from "../../../Types/ServiceLevelObjective/SloStatus";
19
20
  import SloWindowType from "../../../Types/ServiceLevelObjective/SloWindowType";
21
+ import Icon from "../../../UI/Components/Icon/Icon";
20
22
  import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
21
23
  import { goTo, PROJECT_ID } from "./SideMenuHarness";
22
24
 
@@ -46,6 +48,9 @@ const SETTINGS_PATH: string = `${OVERVIEW_PATH}/settings`;
46
48
  const MONITORS_PATH: string = `${OVERVIEW_PATH}/monitors`;
47
49
  const BANNER_TEST_ID: string = "slo-notice-banner";
48
50
  const SECONDS_PER_DAY: number = 24 * 60 * 60;
51
+ const WAITING_TITLE: string = "Waiting for SLO evaluation";
52
+ const WAITING_BODY: string =
53
+ "Monitors are attached. OneUptime evaluates this SLO every few minutes. Results will appear once monitor status data is available and the next evaluation completes.";
49
54
 
50
55
  type SloFields = Record<string, unknown>;
51
56
 
@@ -93,6 +98,13 @@ async function findBanner(): Promise<HTMLElement> {
93
98
  return await screen.findByTestId(BANNER_TEST_ID);
94
99
  }
95
100
 
101
+ function iconMarkup(icon: IconProp): string {
102
+ const { container, unmount } = render(<Icon icon={icon} />);
103
+ const markup: string = container.querySelector("svg")?.innerHTML || "";
104
+ unmount();
105
+ return markup;
106
+ }
107
+
96
108
  async function expectNoBanner(
97
109
  getItem: ReturnType<typeof jest.spyOn>,
98
110
  ): Promise<void> {
@@ -229,12 +241,142 @@ describe("SloNoticeBanner", () => {
229
241
  const banner: HTMLElement = await findBanner();
230
242
 
231
243
  expect(banner).toHaveAttribute("role", "alert");
244
+ expect(banner).toHaveClass("bg-amber-50", "border-amber-200");
232
245
  expect(screen.getByText("No monitors attached")).toBeInTheDocument();
233
246
  expect(
234
247
  screen.getByRole("link", { name: "Attach monitors" }),
235
248
  ).toHaveAttribute("href", MONITORS_PATH);
236
249
  });
237
250
 
251
+ test("shows information when 16 monitors are attached but the stored evaluation status is still misconfigured", async () => {
252
+ const infoIcon: string = iconMarkup(IconProp.Info);
253
+ const warningIcon: string = iconMarkup(IconProp.Alert);
254
+ serveSlo({
255
+ sloStatus: SloStatus.Misconfigured,
256
+ lastEvaluatedAt: new Date("2026-09-21T10:00:00Z"),
257
+ monitors: Array.from({ length: 16 }, (_: unknown, index: number) => {
258
+ return makeMonitor(
259
+ `44444444-0000-4000-8000-${String(index + 1).padStart(12, "0")}`,
260
+ );
261
+ }),
262
+ });
263
+
264
+ render(<SloNoticeBanner sloId={SLO_ID} />);
265
+
266
+ const banner: HTMLElement = await findBanner();
267
+ const icon: SVGElement | null = banner.querySelector("svg");
268
+
269
+ expect(banner).toHaveAttribute("role", "status");
270
+ expect(banner).toHaveClass("bg-white", "border-gray-200");
271
+ expect(banner).not.toHaveClass("bg-amber-50", "border-amber-200");
272
+ expect(screen.queryByRole("alert")).toBeNull();
273
+ expect(screen.getByText(WAITING_TITLE)).toHaveClass("text-gray-900");
274
+ expect(screen.getByText(WAITING_BODY)).toBeInTheDocument();
275
+ expect(icon).toHaveClass("text-blue-600");
276
+ expect(icon?.innerHTML).toBe(infoIcon);
277
+ expect(icon?.innerHTML).not.toBe(warningIcon);
278
+ expect(screen.getByRole("link", { name: "View monitors" })).toHaveAttribute(
279
+ "href",
280
+ MONITORS_PATH,
281
+ );
282
+ expect(screen.queryByText("This SLO cannot be evaluated")).toBeNull();
283
+ expect(screen.queryByText("Review monitors")).toBeNull();
284
+ });
285
+
286
+ test.each([null, undefined])(
287
+ "explains pending monitor data without a previous evaluation timestamp (%s)",
288
+ async (lastEvaluatedAt: null | undefined) => {
289
+ serveSlo({
290
+ sloStatus: SloStatus.Misconfigured,
291
+ lastEvaluatedAt: lastEvaluatedAt,
292
+ });
293
+
294
+ render(<SloNoticeBanner sloId={SLO_ID} />);
295
+
296
+ const banner: HTMLElement = await findBanner();
297
+
298
+ expect(banner).toHaveAttribute("role", "status");
299
+ expect(screen.getByText(WAITING_TITLE)).toBeInTheDocument();
300
+ expect(screen.getByText(WAITING_BODY)).toBeInTheDocument();
301
+ expect(screen.queryByText("Not evaluated yet")).toBeNull();
302
+ expect(screen.queryByText("This SLO cannot be evaluated")).toBeNull();
303
+ },
304
+ );
305
+
306
+ test.each([null, undefined])(
307
+ "uses the same informational state when the SLI type uses its default (%s)",
308
+ async (sliType: null | undefined) => {
309
+ serveSlo({ sloStatus: SloStatus.Misconfigured, sliType: sliType });
310
+
311
+ render(<SloNoticeBanner sloId={SLO_ID} />);
312
+
313
+ const banner: HTMLElement = await findBanner();
314
+
315
+ expect(banner).toHaveAttribute("role", "status");
316
+ expect(screen.getByText(WAITING_TITLE)).toBeInTheDocument();
317
+ expect(
318
+ screen.getByRole("link", { name: "View monitors" }),
319
+ ).toHaveAttribute("href", MONITORS_PATH);
320
+ },
321
+ );
322
+
323
+ test("keeps the evaluation information on the Monitors page without linking back to that page", async () => {
324
+ goTo(MONITORS_PATH);
325
+ serveSlo({ sloStatus: SloStatus.Misconfigured });
326
+
327
+ render(<SloNoticeBanner sloId={SLO_ID} />);
328
+
329
+ const banner: HTMLElement = await findBanner();
330
+
331
+ expect(banner).toHaveAttribute("role", "status");
332
+ expect(screen.getByText(WAITING_TITLE)).toBeInTheDocument();
333
+ expect(screen.getByText(WAITING_BODY)).toBeInTheDocument();
334
+ expect(screen.queryByRole("link")).toBeNull();
335
+ });
336
+
337
+ test("still warns when attached monitors cannot make an unsupported SLI type evaluable", async () => {
338
+ serveSlo({ sloStatus: SloStatus.Misconfigured, sliType: SliType.Metric });
339
+
340
+ render(<SloNoticeBanner sloId={SLO_ID} />);
341
+
342
+ const banner: HTMLElement = await findBanner();
343
+
344
+ expect(banner).toHaveAttribute("role", "alert");
345
+ expect(banner).toHaveClass("bg-amber-50", "border-amber-200");
346
+ expect(
347
+ screen.getByText("This SLO cannot be evaluated"),
348
+ ).toBeInTheDocument();
349
+ expect(
350
+ screen.getByText(/Only "Monitor Uptime" SLIs are supported/),
351
+ ).toBeInTheDocument();
352
+ expect(screen.queryByText(WAITING_TITLE)).toBeNull();
353
+ expect(screen.queryByRole("link")).toBeNull();
354
+ });
355
+
356
+ test.each([0, 100, null])(
357
+ "still warns about an invalid target (%s) even with monitors attached",
358
+ async (targetPercentage: number | null) => {
359
+ serveSlo({
360
+ sloStatus: SloStatus.Misconfigured,
361
+ targetPercentage: targetPercentage,
362
+ });
363
+
364
+ render(<SloNoticeBanner sloId={SLO_ID} />);
365
+
366
+ const banner: HTMLElement = await findBanner();
367
+
368
+ expect(banner).toHaveAttribute("role", "alert");
369
+ expect(banner).toHaveClass("bg-amber-50", "border-amber-200");
370
+ expect(
371
+ screen.getByText("The target is out of range"),
372
+ ).toBeInTheDocument();
373
+ expect(
374
+ screen.getByRole("link", { name: "Edit objective" }),
375
+ ).toHaveAttribute("href", SETTINGS_PATH);
376
+ expect(screen.queryByText(WAITING_TITLE)).toBeNull();
377
+ },
378
+ );
379
+
238
380
  test("asks a brand-new SLO for monitors before its first evaluation", async () => {
239
381
  serveSlo({ sloStatus: null, lastEvaluatedAt: null, monitors: [] });
240
382
 
@@ -302,4 +444,48 @@ describe("SloNoticeBanner", () => {
302
444
  });
303
445
  expect(getItem).toHaveBeenCalledTimes(2);
304
446
  });
447
+
448
+ test("changes from a missing-monitor warning to evaluation information after attaching monitors, then disappears after a healthy evaluation", async () => {
449
+ const getItem: ReturnType<typeof jest.spyOn> = jest
450
+ .spyOn(ModelAPI, "getItem")
451
+ .mockResolvedValueOnce(
452
+ makeSlo({
453
+ ...MEASURING,
454
+ sloStatus: SloStatus.Misconfigured,
455
+ monitors: [],
456
+ }),
457
+ )
458
+ .mockResolvedValueOnce(
459
+ makeSlo({ ...MEASURING, sloStatus: SloStatus.Misconfigured }),
460
+ )
461
+ .mockResolvedValueOnce(makeSlo(MEASURING));
462
+
463
+ const { rerender } = render(
464
+ <SloNoticeBanner sloId={SLO_ID} refreshToggle="0" />,
465
+ );
466
+
467
+ expect(await findBanner()).toHaveAttribute("role", "alert");
468
+ expect(screen.getByText("No monitors attached")).toBeInTheDocument();
469
+
470
+ rerender(<SloNoticeBanner sloId={SLO_ID} refreshToggle="1" />);
471
+
472
+ expect(await screen.findByText(WAITING_TITLE)).toBeInTheDocument();
473
+ expect(screen.getByTestId(BANNER_TEST_ID)).toHaveAttribute(
474
+ "role",
475
+ "status",
476
+ );
477
+ expect(screen.queryByText("No monitors attached")).toBeNull();
478
+ expect(screen.queryByRole("alert")).toBeNull();
479
+ expect(screen.getByRole("link", { name: "View monitors" })).toHaveAttribute(
480
+ "href",
481
+ MONITORS_PATH,
482
+ );
483
+
484
+ rerender(<SloNoticeBanner sloId={SLO_ID} refreshToggle="2" />);
485
+
486
+ await waitFor(() => {
487
+ expect(screen.queryByTestId(BANNER_TEST_ID)).toBeNull();
488
+ });
489
+ expect(getItem).toHaveBeenCalledTimes(3);
490
+ });
305
491
  });