@oneuptime/common 12.0.34 → 13.0.0

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 (259) hide show
  1. package/Models/DatabaseModels/GlobalConfig.ts +12 -12
  2. package/Models/DatabaseModels/InventoryItem.ts +44 -1
  3. package/Models/DatabaseModels/NetworkDevice.ts +126 -0
  4. package/Server/EnvironmentConfig.ts +47 -16
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  7. package/Server/Infrastructure/Redis.ts +8 -8
  8. package/Server/Infrastructure/Status.ts +10 -4
  9. package/Server/Services/DatabaseService.ts +65 -13
  10. package/Server/Services/InventoryItemService.ts +31 -1
  11. package/Server/Services/MonitorTemplateService.ts +140 -19
  12. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +349 -41
  13. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
  14. package/Server/Services/NetworkDeviceService.ts +69 -0
  15. package/Server/Services/UserNotificationRuleService.ts +76 -25
  16. package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
  17. package/Server/Types/Database/QueryUtil.ts +56 -0
  18. package/Server/Types/Markdown.ts +93 -1
  19. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
  20. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +10 -1
  21. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
  22. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +82 -35
  23. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
  24. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +202 -37
  25. package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
  26. package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
  27. package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
  28. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
  29. package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
  30. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
  31. package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
  32. package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
  33. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
  34. package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
  35. package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
  36. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
  37. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
  38. package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
  39. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
  40. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
  41. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
  42. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
  43. package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
  44. package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
  45. package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
  46. package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
  47. package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
  48. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
  49. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
  50. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
  51. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
  52. package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
  53. package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
  54. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
  55. package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
  56. package/Tests/Models/InventoryItemStatus.test.ts +147 -0
  57. package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
  58. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
  59. package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
  60. package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
  61. package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +62 -23
  62. package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
  63. package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
  64. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
  65. package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
  66. package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
  67. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
  68. package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
  69. package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
  70. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1133 -81
  71. package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
  72. package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
  73. package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
  74. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
  75. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
  76. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
  77. package/Tests/Server/Types/Markdown.test.ts +194 -1
  78. package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
  79. package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
  80. package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
  81. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
  82. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1 -1
  83. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
  84. package/Tests/Server/Utils/EventLoop.test.ts +359 -0
  85. package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
  86. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
  87. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +13 -7
  88. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
  90. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
  91. package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
  92. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +224 -10
  93. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
  94. package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
  95. package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
  96. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
  97. package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
  98. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
  99. package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
  100. package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
  101. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
  102. package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
  103. package/Tests/Types/JSONFunctions.test.ts +40 -0
  104. package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
  105. package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
  106. package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
  107. package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
  108. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
  109. package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
  110. package/Tests/UI/Rum/ReplayHeader.test.tsx +10 -2
  111. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
  112. package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
  113. package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
  114. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  115. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
  116. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
  117. package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
  118. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
  119. package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
  120. package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
  121. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
  122. package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
  123. package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
  124. package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
  125. package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
  126. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  127. package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
  128. package/Types/Dashboard/DashboardVariable.ts +8 -0
  129. package/Types/JSON.ts +4 -0
  130. package/Types/JSONFunctions.ts +16 -0
  131. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
  132. package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
  133. package/Types/SerializableObjectDictionary.ts +4 -0
  134. package/Types/Telemetry/InventoryLiveness.ts +56 -0
  135. package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
  136. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +46 -21
  137. package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
  138. package/UI/esbuild-config.js +6 -0
  139. package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
  140. package/Utils/Dashboard/LabelVariable.ts +152 -0
  141. package/Utils/Dashboard/VariableUrlState.ts +27 -5
  142. package/Utils/MetricUnitUtil.ts +34 -0
  143. package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
  144. package/Utils/Monitor/MetricValueFormatter.ts +342 -0
  145. package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
  146. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
  147. package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
  148. package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
  149. package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
  150. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
  152. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
  154. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  155. package/build/dist/Server/EnvironmentConfig.js +44 -12
  156. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  157. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
  158. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
  159. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  160. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  161. package/build/dist/Server/Infrastructure/Redis.js +8 -8
  162. package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
  163. package/build/dist/Server/Infrastructure/Status.js +10 -4
  164. package/build/dist/Server/Infrastructure/Status.js.map +1 -1
  165. package/build/dist/Server/Services/DatabaseService.js +58 -13
  166. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  167. package/build/dist/Server/Services/InventoryItemService.js +28 -1
  168. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  169. package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
  170. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  171. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +270 -37
  172. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  173. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
  174. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  175. package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
  176. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  177. package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
  178. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  179. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
  180. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  181. package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
  182. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  183. package/build/dist/Server/Types/Markdown.js +75 -0
  184. package/build/dist/Server/Types/Markdown.js.map +1 -1
  185. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
  186. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  187. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +10 -1
  188. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  189. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
  190. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +58 -35
  192. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  193. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
  194. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  195. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +166 -35
  196. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  197. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
  198. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
  199. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
  200. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
  201. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
  202. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
  203. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
  204. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  205. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
  206. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
  207. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
  208. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  209. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
  210. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  211. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
  212. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  213. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
  214. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
  215. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  216. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  217. package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
  218. package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
  219. package/build/dist/Types/JSON.js +1 -0
  220. package/build/dist/Types/JSON.js.map +1 -1
  221. package/build/dist/Types/JSONFunctions.js +13 -0
  222. package/build/dist/Types/JSONFunctions.js.map +1 -1
  223. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
  224. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  225. package/build/dist/Types/SerializableObjectDictionary.js +4 -0
  226. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  227. package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
  228. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
  229. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
  230. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
  231. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +9 -2
  232. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  233. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
  234. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
  235. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
  236. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
  237. package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
  238. package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
  239. package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
  240. package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
  241. package/build/dist/Utils/MetricUnitUtil.js +26 -0
  242. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  243. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
  244. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
  245. package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
  246. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
  247. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
  248. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
  249. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
  250. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  251. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
  252. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  253. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
  254. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
  255. package/jest.config.json +2 -0
  256. package/package.json +1 -1
  257. package/test-setup.sh +9 -9
  258. package/tsconfig.json +51 -11
  259. package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +0 -399
@@ -0,0 +1,401 @@
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
+ cleanup,
12
+ fireEvent,
13
+ render,
14
+ screen,
15
+ waitFor,
16
+ within,
17
+ } from "@testing-library/react";
18
+ import React from "react";
19
+ import { MemoryRouter } from "react-router-dom";
20
+ import InventoryItem from "../../../Models/DatabaseModels/InventoryItem";
21
+ import InventoryItemRelationship from "../../../Models/DatabaseModels/InventoryItemRelationship";
22
+ import EntityType from "../../../Types/Telemetry/EntityType";
23
+ import EntityRelationshipType from "../../../Types/Telemetry/EntityRelationshipType";
24
+ import ObjectID from "../../../Types/ObjectID";
25
+ import InfrastructureExplorer from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/InfrastructureExplorer";
26
+
27
+ jest.mock("react-i18next", () => {
28
+ return {
29
+ useTranslation: () => {
30
+ return {
31
+ t: (value: string): string => {
32
+ return value;
33
+ },
34
+ };
35
+ },
36
+ };
37
+ });
38
+ jest.mock("../../../UI/Utils/Project", () => {
39
+ return {
40
+ __esModule: true,
41
+ default: {
42
+ getCurrentProjectId: () => {
43
+ return new ObjectID("dde060c6-fe0d-49ce-b44c-4035a13bc1db");
44
+ },
45
+ },
46
+ };
47
+ });
48
+ jest.mock(
49
+ "../../../../App/FeatureSet/Dashboard/src/Components/Topology/InfrastructureGraph",
50
+ () => {
51
+ return {
52
+ __esModule: true,
53
+ default: (props: {
54
+ entities: Array<InventoryItem>;
55
+ onSelectResource?: (key: string) => void;
56
+ }): React.ReactElement => {
57
+ return (
58
+ <div data-testid="infrastructure-map">
59
+ {props.entities.map((item: InventoryItem): React.ReactElement => {
60
+ return (
61
+ <button
62
+ type="button"
63
+ key={item.entityKey}
64
+ onClick={() => {
65
+ props.onSelectResource?.(item.entityKey!);
66
+ }}
67
+ >
68
+ {item.displayName}
69
+ </button>
70
+ );
71
+ })}
72
+ </div>
73
+ );
74
+ },
75
+ };
76
+ },
77
+ );
78
+ jest.mock(
79
+ "../../../../App/FeatureSet/Dashboard/src/Components/Topology/EntityDetailPanel",
80
+ () => {
81
+ return {
82
+ __esModule: true,
83
+ default: (props: {
84
+ entity: InventoryItem;
85
+ onClose: () => void;
86
+ onFocus: (key: string) => void;
87
+ }): React.ReactElement => {
88
+ return (
89
+ <div role="dialog" aria-label={props.entity.displayName}>
90
+ <button type="button" onClick={props.onClose}>
91
+ Close resource details
92
+ </button>
93
+ <button
94
+ type="button"
95
+ onClick={() => {
96
+ props.onFocus(props.entity.entityKey!);
97
+ }}
98
+ >
99
+ Explore this resource
100
+ </button>
101
+ </div>
102
+ );
103
+ },
104
+ };
105
+ },
106
+ );
107
+
108
+ function entity(
109
+ key: string,
110
+ type: EntityType = EntityType.Host,
111
+ name: string = key,
112
+ ): InventoryItem {
113
+ const item: InventoryItem = new InventoryItem();
114
+ item.entityKey = key;
115
+ item.entityType = type;
116
+ item.displayName = name;
117
+ return item;
118
+ }
119
+ function edge(from: string, to: string): InventoryItemRelationship {
120
+ const item: InventoryItemRelationship = new InventoryItemRelationship();
121
+ item.fromEntityKey = from;
122
+ item.toEntityKey = to;
123
+ item.relationshipType = EntityRelationshipType.HostedOn;
124
+ return item;
125
+ }
126
+ function fixtures(): {
127
+ entities: Array<InventoryItem>;
128
+ relationships: Array<InventoryItemRelationship>;
129
+ } {
130
+ return {
131
+ entities: [
132
+ entity("api", EntityType.Service, "Checkout API"),
133
+ entity("web", EntityType.Service, "Web frontend"),
134
+ entity("api-host", EntityType.Host, "API worker"),
135
+ entity("shared", EntityType.Host, "Shared worker"),
136
+ entity("web-host", EntityType.Host, "Web worker"),
137
+ entity("quiet", EntityType.Host, "Quiet host"),
138
+ ],
139
+ relationships: [
140
+ edge("api", "api-host"),
141
+ edge("api", "shared"),
142
+ edge("web", "shared"),
143
+ edge("web", "web-host"),
144
+ ],
145
+ };
146
+ }
147
+ function renderExplorer(
148
+ data: {
149
+ entities: Array<InventoryItem>;
150
+ relationships: Array<InventoryItemRelationship>;
151
+ } = fixtures(),
152
+ ): ReturnType<typeof render> {
153
+ return render(
154
+ <MemoryRouter>
155
+ <InfrastructureExplorer {...data} metricsWindowSeconds={900} />
156
+ </MemoryRouter>,
157
+ );
158
+ }
159
+ function search(value: string): void {
160
+ fireEvent.change(
161
+ screen.getByRole("searchbox", { name: "Search infrastructure" }),
162
+ { target: { value } },
163
+ );
164
+ }
165
+ function types(): ReturnType<typeof within> {
166
+ return within(screen.getByRole("complementary", { name: "Resource types" }));
167
+ }
168
+
169
+ beforeEach(() => {
170
+ window.history.replaceState(
171
+ {},
172
+ "",
173
+ "/dashboard/project/topology/overview?tab=Infrastructure",
174
+ );
175
+ });
176
+ afterEach(() => {
177
+ cleanup();
178
+ jest.restoreAllMocks();
179
+ });
180
+
181
+ describe("Infrastructure explorer", () => {
182
+ test("starts with meaningful groups and resource categories instead of a dense canvas", () => {
183
+ renderExplorer();
184
+ expect(
185
+ screen.getByRole("button", { name: "Explore Checkout API" }),
186
+ ).toBeVisible();
187
+ expect(
188
+ screen.getByRole("button", { name: "Explore Web frontend" }),
189
+ ).toBeVisible();
190
+ expect(screen.getByText("Browse resources")).toBeVisible();
191
+ expect(screen.getByText("Unlinked resources")).toBeVisible();
192
+ expect(screen.queryByTestId("infrastructure-map")).not.toBeInTheDocument();
193
+ });
194
+ test("drills into one group's resources and returns through a breadcrumb", () => {
195
+ renderExplorer();
196
+ fireEvent.click(
197
+ screen.getByRole("button", { name: "Explore Checkout API" }),
198
+ );
199
+ expect(screen.getByText("API worker")).toBeVisible();
200
+ expect(screen.getByText("Shared worker")).toBeVisible();
201
+ expect(screen.queryByText("Web worker")).not.toBeInTheDocument();
202
+ expect(new URLSearchParams(window.location.search).get("infraFocus")).toBe(
203
+ "api",
204
+ );
205
+ const breadcrumb: HTMLElement = screen.getByRole("navigation", {
206
+ name: "Infrastructure location",
207
+ });
208
+ expect(
209
+ within(breadcrumb).getByRole("button", { name: "Checkout API" }),
210
+ ).toHaveAttribute("aria-current", "location");
211
+ fireEvent.click(screen.getByRole("button", { name: "All infrastructure" }));
212
+ expect(
213
+ screen.getByRole("button", { name: "Explore Web frontend" }),
214
+ ).toBeVisible();
215
+ expect(new URLSearchParams(window.location.search).has("infraFocus")).toBe(
216
+ false,
217
+ );
218
+ });
219
+ test("search reports matches and combines them with a resource-type filter", () => {
220
+ renderExplorer();
221
+ fireEvent.click(types().getByRole("button", { name: /^Host / }));
222
+ search("worker");
223
+ expect(screen.getByRole("status")).toHaveTextContent(
224
+ "3 matching resources",
225
+ );
226
+ expect(screen.getByText("API worker")).toBeVisible();
227
+ expect(screen.queryByText("Quiet host")).not.toBeInTheDocument();
228
+ expect(
229
+ screen.queryByRole("button", { name: /Explore Checkout API/ }),
230
+ ).not.toBeInTheDocument();
231
+ fireEvent.click(screen.getByRole("button", { name: "Clear filters" }));
232
+ expect(screen.getByRole("searchbox")).toHaveValue("");
233
+ expect(
234
+ screen.getByRole("button", { name: "Explore Checkout API" }),
235
+ ).toBeVisible();
236
+ });
237
+ test("a search with no matches has an actionable reset and can expand beyond the group", () => {
238
+ renderExplorer();
239
+ fireEvent.click(
240
+ screen.getByRole("button", { name: "Explore Checkout API" }),
241
+ );
242
+ search("web worker");
243
+ expect(screen.getByText("No resources match your filters")).toBeVisible();
244
+ fireEvent.click(
245
+ screen.getByRole("button", { name: "Search all infrastructure" }),
246
+ );
247
+ expect(screen.getByRole("searchbox")).toHaveValue("");
248
+ expect(
249
+ screen.getByRole("button", { name: "Explore Web frontend" }),
250
+ ).toBeVisible();
251
+ });
252
+ test("restores shareable scope, search, type, and map state and preserves service search parameters", async () => {
253
+ window.history.replaceState(
254
+ {},
255
+ "",
256
+ "?tab=Infrastructure&infraFocus=api&infraSearch=worker&infraType=host&infraView=map&search=checkout&focus=service-id",
257
+ );
258
+ renderExplorer();
259
+ expect(screen.getByRole("searchbox")).toHaveValue("worker");
260
+ const map: HTMLElement = screen.getByTestId("infrastructure-map");
261
+ expect(within(map).getByText("API worker")).toBeVisible();
262
+ expect(within(map).getByText("Shared worker")).toBeVisible();
263
+ expect(within(map).queryByText("Web worker")).not.toBeInTheDocument();
264
+ search("api");
265
+ await waitFor(() => {
266
+ expect(
267
+ new URLSearchParams(window.location.search).get("infraSearch"),
268
+ ).toBe("api");
269
+ });
270
+ expect(new URLSearchParams(window.location.search).get("search")).toBe(
271
+ "checkout",
272
+ );
273
+ expect(new URLSearchParams(window.location.search).get("focus")).toBe(
274
+ "service-id",
275
+ );
276
+ });
277
+ test("switches views without losing a selected group or its resource filter", () => {
278
+ renderExplorer();
279
+ fireEvent.click(
280
+ screen.getByRole("button", { name: "Explore Checkout API" }),
281
+ );
282
+ search("api worker");
283
+ fireEvent.click(
284
+ within(
285
+ screen.getByRole("group", { name: "Infrastructure view" }),
286
+ ).getByRole("button", { name: "Map" }),
287
+ );
288
+ expect(screen.getByTestId("infrastructure-map")).toHaveTextContent(
289
+ "API worker",
290
+ );
291
+ expect(screen.getByTestId("infrastructure-map")).not.toHaveTextContent(
292
+ "Shared worker",
293
+ );
294
+ expect(new URLSearchParams(window.location.search).get("infraView")).toBe(
295
+ "map",
296
+ );
297
+ fireEvent.click(
298
+ within(
299
+ screen.getByRole("group", { name: "Infrastructure view" }),
300
+ ).getByRole("button", { name: "Explore" }),
301
+ );
302
+ expect(screen.queryByTestId("infrastructure-map")).not.toBeInTheDocument();
303
+ expect(screen.getByRole("searchbox")).toHaveValue("api worker");
304
+ expect(new URLSearchParams(window.location.search).get("infraFocus")).toBe(
305
+ "api",
306
+ );
307
+ });
308
+ test("opens inventory details from a result and uses the same selection action from the map", () => {
309
+ renderExplorer();
310
+ search("api worker");
311
+ fireEvent.click(screen.getByRole("button", { name: /API worker Host/ }));
312
+ expect(screen.getByRole("dialog", { name: "API worker" })).toBeVisible();
313
+ fireEvent.click(
314
+ screen.getByRole("button", { name: "Close resource details" }),
315
+ );
316
+ fireEvent.click(
317
+ within(
318
+ screen.getByRole("group", { name: "Infrastructure view" }),
319
+ ).getByRole("button", { name: "Map" }),
320
+ );
321
+ fireEvent.click(
322
+ within(screen.getByTestId("infrastructure-map")).getByRole("button", {
323
+ name: "API worker",
324
+ }),
325
+ );
326
+ expect(screen.getByRole("dialog", { name: "API worker" })).toBeVisible();
327
+ fireEvent.click(
328
+ screen.getByRole("button", { name: "Explore this resource" }),
329
+ );
330
+ expect(new URLSearchParams(window.location.search).get("infraFocus")).toBe(
331
+ "api-host",
332
+ );
333
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
334
+ });
335
+ test("explains missing inventory details and shows known connections for an unresolved endpoint", () => {
336
+ renderExplorer({
337
+ entities: [entity("api", EntityType.Service, "Checkout API")],
338
+ relationships: [edge("api", "missing-host")],
339
+ });
340
+ search("missing-host");
341
+ fireEvent.click(
342
+ screen.getByRole("button", {
343
+ name: /Undiscovered resource · missing-host/,
344
+ }),
345
+ );
346
+ const panel: HTMLElement = screen.getByRole("dialog", {
347
+ name: "Undiscovered resource",
348
+ });
349
+ expect(within(panel).getByText("Known connections")).toBeVisible();
350
+ expect(
351
+ within(panel).getByText(/inventory details have not been discovered yet/),
352
+ ).toBeVisible();
353
+ expect(within(panel).getByText(/Checkout API hosted on/)).toBeVisible();
354
+ });
355
+ test("paginates large inventories and returns to the first page when search changes", () => {
356
+ const entities: Array<InventoryItem> = Array.from(
357
+ { length: 85 },
358
+ (_value: unknown, index: number): InventoryItem => {
359
+ return entity(
360
+ `host-${String(index).padStart(3, "0")}`,
361
+ EntityType.Host,
362
+ `Host ${String(index).padStart(3, "0")}`,
363
+ );
364
+ },
365
+ );
366
+ renderExplorer({ entities, relationships: [] });
367
+ fireEvent.click(types().getByRole("button", { name: /^Host 85$/ }));
368
+ expect(
369
+ within(screen.getByRole("list")).getAllByRole("listitem"),
370
+ ).toHaveLength(40);
371
+ expect(screen.getByRole("button", { name: "Previous" })).toBeDisabled();
372
+ fireEvent.click(screen.getByRole("button", { name: "Next" }));
373
+ expect(screen.getByText("Page 2 / 3")).toBeVisible();
374
+ expect(screen.queryByText("Host 000")).not.toBeInTheDocument();
375
+ fireEvent.click(screen.getByRole("button", { name: "Next" }));
376
+ expect(
377
+ within(screen.getByRole("list")).getAllByRole("listitem"),
378
+ ).toHaveLength(5);
379
+ expect(screen.getByRole("button", { name: "Next" })).toBeDisabled();
380
+ search("host-000");
381
+ expect(screen.getByText("Host 000")).toBeVisible();
382
+ expect(
383
+ screen.queryByRole("button", { name: "Next" }),
384
+ ).not.toBeInTheDocument();
385
+ });
386
+ test("falls back from stale scope links and explains how to populate an empty catalog", () => {
387
+ window.history.replaceState({}, "", "?infraFocus=deleted-item");
388
+ const view: ReturnType<typeof render> = renderExplorer();
389
+ expect(
390
+ screen.getByRole("button", { name: "Explore Checkout API" }),
391
+ ).toBeVisible();
392
+ view.unmount();
393
+ renderExplorer({ entities: [], relationships: [] });
394
+ expect(
395
+ screen.getByText("No infrastructure topology discovered yet"),
396
+ ).toBeVisible();
397
+ expect(
398
+ screen.getByRole("link", { name: "Connect your infrastructure" }),
399
+ ).toBeVisible();
400
+ });
401
+ });
@@ -0,0 +1,232 @@
1
+ import "@testing-library/jest-dom";
2
+ import { afterEach, describe, expect, jest, test } from "@jest/globals";
3
+ import {
4
+ cleanup,
5
+ fireEvent,
6
+ render,
7
+ screen,
8
+ within,
9
+ } from "@testing-library/react";
10
+ import React from "react";
11
+ import { Edge, Node } from "reactflow";
12
+ import InventoryItem from "../../../Models/DatabaseModels/InventoryItem";
13
+ import InventoryItemRelationship from "../../../Models/DatabaseModels/InventoryItemRelationship";
14
+ import EntityType from "../../../Types/Telemetry/EntityType";
15
+ import EntityRelationshipType from "../../../Types/Telemetry/EntityRelationshipType";
16
+ import getJestMockFunction, { MockFunction } from "../../MockType";
17
+ import InfrastructureGraph from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/InfrastructureGraph";
18
+
19
+ jest.mock("react-i18next", () => {
20
+ return {
21
+ useTranslation: () => {
22
+ return {
23
+ t: (value: string): string => {
24
+ return value;
25
+ },
26
+ };
27
+ },
28
+ };
29
+ });
30
+
31
+ /*
32
+ * Inspect the actual nodes and connections handed to the canvas. Layout and
33
+ * nesting run normally; React Flow's browser measurement is the only boundary
34
+ * replaced, so an edge silently filtered before rendering fails these tests.
35
+ */
36
+ jest.mock("reactflow", () => {
37
+ return {
38
+ __esModule: true,
39
+ default: (props: {
40
+ nodes: Array<Node>;
41
+ edges: Array<Edge>;
42
+ onNodeClick: (event: React.MouseEvent, node: Node) => void;
43
+ }): React.ReactElement => {
44
+ return (
45
+ <div data-testid="infrastructure-canvas">
46
+ <div>
47
+ {props.nodes.map((node: Node): React.ReactElement => {
48
+ return (
49
+ <button
50
+ type="button"
51
+ key={node.id}
52
+ data-testid={`node-${node.id}`}
53
+ data-parent={node.parentNode || ""}
54
+ onClick={(event: React.MouseEvent): void => {
55
+ props.onNodeClick(event, node);
56
+ }}
57
+ >
58
+ {node.data.title}
59
+ </button>
60
+ );
61
+ })}
62
+ </div>
63
+ <ul aria-label="Visible infrastructure connections">
64
+ {props.edges.map((edge: Edge): React.ReactElement => {
65
+ return (
66
+ <li
67
+ key={edge.id}
68
+ data-source={edge.source}
69
+ data-target={edge.target}
70
+ data-relationship={edge.data?.relationshipType}
71
+ >
72
+ {edge.source} → {edge.target}
73
+ </li>
74
+ );
75
+ })}
76
+ </ul>
77
+ </div>
78
+ );
79
+ },
80
+ Background: (): null => {
81
+ return null;
82
+ },
83
+ Controls: (): null => {
84
+ return null;
85
+ },
86
+ Handle: (): null => {
87
+ return null;
88
+ },
89
+ BackgroundVariant: { Dots: "dots" },
90
+ MarkerType: { ArrowClosed: "arrowclosed" },
91
+ Position: { Top: "top", Bottom: "bottom" },
92
+ };
93
+ });
94
+
95
+ function entity(key: string, type: EntityType): InventoryItem {
96
+ const item: InventoryItem = new InventoryItem();
97
+ item.entityKey = key;
98
+ item.displayName = key;
99
+ item.entityType = type;
100
+ return item;
101
+ }
102
+ function edge(
103
+ from: string,
104
+ to: string,
105
+ type: EntityRelationshipType = EntityRelationshipType.HostedOn,
106
+ ): InventoryItemRelationship {
107
+ const item: InventoryItemRelationship = new InventoryItemRelationship();
108
+ item.fromEntityKey = from;
109
+ item.toEntityKey = to;
110
+ item.relationshipType = type;
111
+ return item;
112
+ }
113
+ function connections(): Array<HTMLElement> {
114
+ return within(
115
+ screen.getByRole("list", { name: "Visible infrastructure connections" }),
116
+ ).queryAllByRole("listitem");
117
+ }
118
+
119
+ afterEach(() => {
120
+ cleanup();
121
+ });
122
+
123
+ describe("Infrastructure canvas connection fidelity", () => {
124
+ test("keeps a shared host's other service connection after nesting it under its primary service", () => {
125
+ render(
126
+ <InfrastructureGraph
127
+ entities={[
128
+ entity("api", EntityType.Service),
129
+ entity("web", EntityType.Service),
130
+ entity("shared-host", EntityType.Host),
131
+ ]}
132
+ relationships={[edge("web", "shared-host"), edge("api", "shared-host")]}
133
+ />,
134
+ );
135
+ expect(screen.getByTestId("node-shared-host")).toHaveAttribute(
136
+ "data-parent",
137
+ "api",
138
+ );
139
+ expect(connections()).toHaveLength(1);
140
+ expect(connections()[0]).toHaveAttribute("data-source", "web");
141
+ expect(connections()[0]).toHaveAttribute("data-target", "shared-host");
142
+ expect(connections()[0]).toHaveAttribute(
143
+ "data-relationship",
144
+ EntityRelationshipType.HostedOn,
145
+ );
146
+ // api → host is already represented by nesting; web → host is not.
147
+ expect(screen.queryByText("api → shared-host")).not.toBeInTheDocument();
148
+ });
149
+
150
+ test("hides only the relationship actually consumed by nesting when the same pair has another relationship", () => {
151
+ render(
152
+ <InfrastructureGraph
153
+ entities={[
154
+ entity("api", EntityType.Service),
155
+ entity("host", EntityType.Host),
156
+ ]}
157
+ relationships={[
158
+ edge("api", "host"),
159
+ edge("api", "host", EntityRelationshipType.RunsOn),
160
+ ]}
161
+ />,
162
+ );
163
+ expect(screen.getByTestId("node-host")).toHaveAttribute(
164
+ "data-parent",
165
+ "api",
166
+ );
167
+ expect(connections()).toHaveLength(1);
168
+ expect(connections()[0]).toHaveAttribute("data-source", "api");
169
+ expect(connections()[0]).toHaveAttribute(
170
+ "data-relationship",
171
+ EntityRelationshipType.RunsOn,
172
+ );
173
+ });
174
+
175
+ test("preserves workload connections while structural pod and node relationships remain represented by their boxes", () => {
176
+ render(
177
+ <InfrastructureGraph
178
+ entities={[
179
+ entity("cluster", EntityType.KubernetesCluster),
180
+ entity("node", EntityType.KubernetesNode),
181
+ entity("pod", EntityType.KubernetesPod),
182
+ entity("api", EntityType.Service),
183
+ ]}
184
+ relationships={[
185
+ edge("node", "cluster", EntityRelationshipType.MemberOf),
186
+ edge("pod", "node", EntityRelationshipType.RunsOn),
187
+ edge("api", "pod", EntityRelationshipType.RunsOn),
188
+ ]}
189
+ />,
190
+ );
191
+ expect(screen.getByTestId("node-node")).toHaveAttribute(
192
+ "data-parent",
193
+ "cluster",
194
+ );
195
+ expect(screen.getByTestId("node-pod")).toHaveAttribute(
196
+ "data-parent",
197
+ "node",
198
+ );
199
+ expect(connections()).toHaveLength(1);
200
+ expect(connections()[0]).toHaveAttribute("data-source", "api");
201
+ expect(connections()[0]).toHaveAttribute("data-target", "pod");
202
+ });
203
+
204
+ test("keeps unresolved connection endpoints selectable by their actual identity", () => {
205
+ const onSelectResource: MockFunction = getJestMockFunction();
206
+ render(
207
+ <InfrastructureGraph
208
+ entities={[entity("host", EntityType.Host)]}
209
+ relationships={[
210
+ edge("host", "missing-resource", EntityRelationshipType.MemberOf),
211
+ ]}
212
+ onSelectResource={onSelectResource}
213
+ />,
214
+ );
215
+ expect(screen.getByTestId("node-missing-resource")).toHaveTextContent(
216
+ "Undiscovered resource · missing-resource",
217
+ );
218
+ expect(connections()).toHaveLength(1);
219
+ fireEvent.click(screen.getByTestId("node-missing-resource"));
220
+ expect(onSelectResource).toHaveBeenCalledWith("missing-resource");
221
+ });
222
+
223
+ test("explains an empty scoped canvas", () => {
224
+ render(<InfrastructureGraph entities={[]} relationships={[]} />);
225
+ expect(screen.getByRole("status")).toHaveTextContent(
226
+ "No resources in this map",
227
+ );
228
+ expect(
229
+ screen.queryByTestId("infrastructure-canvas"),
230
+ ).not.toBeInTheDocument();
231
+ });
232
+ });