@rancher/shell 3.0.8-rc.1 → 3.0.8-rc.12

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 (345) hide show
  1. package/assets/brand/suse/banner.svg +1 -0
  2. package/assets/brand/suse/dark/banner.svg +1 -0
  3. package/assets/brand/suse/dark/login-landscape.svg +1 -0
  4. package/assets/brand/suse/dark/rancher-logo.svg +1 -1
  5. package/assets/brand/suse/favicon.png +0 -0
  6. package/assets/brand/suse/login-landscape.svg +1 -0
  7. package/assets/brand/suse/metadata.json +11 -1
  8. package/assets/brand/suse/rancher-logo.svg +1 -1
  9. package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
  10. package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
  11. package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
  12. package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
  13. package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
  14. package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
  15. package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
  16. package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
  17. package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
  18. package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
  19. package/assets/images/content/README.md +5 -0
  20. package/assets/images/content/cloud-native.svg +84 -0
  21. package/assets/images/content/dark/cloud-native.svg +21 -0
  22. package/assets/images/content/dark/shield.svg +59 -0
  23. package/assets/images/content/dark/suse.svg +10 -0
  24. package/assets/images/content/shield.svg +59 -0
  25. package/assets/images/content/suse.svg +10 -0
  26. package/assets/styles/base/_typography.scss +1 -0
  27. package/assets/styles/fonts/_fontstack.scss +53 -1
  28. package/assets/styles/global/_cards.scss +0 -3
  29. package/assets/styles/global/_layout.scss +21 -35
  30. package/assets/styles/themes/_dark.scss +1 -1
  31. package/assets/styles/themes/_light.scss +1 -1
  32. package/assets/styles/themes/_modern.scss +11 -3
  33. package/assets/styles/themes/_suse.scss +116 -24
  34. package/assets/translations/en-us.yaml +94 -10
  35. package/components/AutoscalerCard.vue +113 -0
  36. package/components/AutoscalerTab.vue +94 -0
  37. package/components/BackLink.vue +8 -0
  38. package/components/BannerGraphic.vue +36 -21
  39. package/components/BrandImage.vue +17 -6
  40. package/components/ClusterIconMenu.vue +1 -1
  41. package/components/ClusterProviderIcon.vue +1 -1
  42. package/components/Cron/CronExpressionEditor.vue +1 -1
  43. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  44. package/components/Drawer/Chrome.vue +2 -6
  45. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
  46. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
  47. package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
  48. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
  49. package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
  50. package/components/Drawer/types.ts +3 -0
  51. package/components/DynamicContent/DynamicContentBanner.vue +102 -0
  52. package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
  53. package/components/DynamicContent/DynamicContentIcon.vue +132 -0
  54. package/components/DynamicContent/DynamicContentPanel.vue +112 -0
  55. package/components/DynamicContent/content.ts +78 -0
  56. package/components/EmberPage.vue +1 -1
  57. package/components/IconOrSvg.vue +2 -2
  58. package/components/PaginatedResourceTable.vue +2 -6
  59. package/components/PopoverCard.vue +192 -0
  60. package/components/Questions/__tests__/index.test.ts +159 -0
  61. package/components/Resource/Detail/CopyToClipboard.vue +4 -1
  62. package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
  63. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  64. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
  65. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
  66. package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
  67. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  68. package/components/Resource/Detail/Metadata/composables.ts +9 -9
  69. package/components/Resource/Detail/Metadata/index.vue +3 -3
  70. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
  71. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
  72. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
  73. package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
  74. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  75. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  76. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  77. package/components/Resource/Detail/composables.ts +12 -0
  78. package/components/ResourceDetail/Masthead/latest.vue +29 -0
  79. package/components/ResourceDetail/index.vue +4 -1
  80. package/components/ResourceList/Masthead.vue +1 -1
  81. package/components/ResourceTable.vue +1 -1
  82. package/components/SortableTable/index.vue +2 -1
  83. package/components/Tabbed/__tests__/index.test.ts +86 -0
  84. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  85. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  86. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  87. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  88. package/components/__tests__/AutoscalerCard.test.ts +154 -0
  89. package/components/__tests__/AutoscalerTab.test.ts +125 -0
  90. package/components/__tests__/PopoverCard.test.ts +204 -0
  91. package/components/auth/SelectPrincipal.vue +24 -6
  92. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  93. package/components/auth/login/ldap.vue +3 -3
  94. package/components/form/NodeScheduling.vue +2 -2
  95. package/components/formatter/Autoscaler.vue +97 -0
  96. package/components/formatter/InternalExternalIP.vue +198 -24
  97. package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
  99. package/components/google/util/__tests__/formatter.test.ts +47 -0
  100. package/components/google/util/formatter.ts +5 -2
  101. package/components/nav/Group.vue +21 -5
  102. package/components/nav/Header.vue +37 -17
  103. package/components/nav/NamespaceFilter.vue +13 -1
  104. package/components/nav/NotificationCenter/index.vue +2 -1
  105. package/components/nav/TopLevelMenu.helper.ts +16 -6
  106. package/components/nav/TopLevelMenu.vue +4 -2
  107. package/components/nav/Type.vue +8 -3
  108. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  109. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  110. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  111. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  112. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  113. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  114. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  115. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  116. package/components/nav/WindowManager/constants.ts +23 -0
  117. package/components/nav/WindowManager/index.vue +61 -575
  118. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  119. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  120. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  121. package/components/nav/__tests__/Type.test.ts +59 -0
  122. package/components/templates/default.vue +4 -40
  123. package/components/templates/home.vue +31 -5
  124. package/components/templates/plain.vue +30 -4
  125. package/components/templates/standalone.vue +1 -1
  126. package/composables/useI18n.ts +10 -1
  127. package/composables/useInterval.ts +15 -0
  128. package/config/__test__/uiplugins.test.ts +309 -0
  129. package/config/labels-annotations.js +9 -1
  130. package/config/product/explorer.js +3 -1
  131. package/config/product/manager.js +20 -9
  132. package/config/router/navigation-guards/clusters.js +3 -3
  133. package/config/router/navigation-guards/products.js +1 -1
  134. package/config/router/routes.js +10 -2
  135. package/config/settings.ts +2 -1
  136. package/config/store.js +4 -2
  137. package/config/table-headers.js +8 -0
  138. package/config/types.js +9 -0
  139. package/config/uiplugins.js +46 -2
  140. package/config/version.js +1 -1
  141. package/core/__test__/extension-manager-impl.test.js +236 -0
  142. package/core/extension-manager-impl.js +21 -4
  143. package/core/plugin-helpers.ts +4 -2
  144. package/core/plugins-loader.js +2 -2
  145. package/core/types-provisioning.ts +8 -1
  146. package/detail/pod.vue +1 -0
  147. package/detail/provisioning.cattle.io.cluster.vue +19 -7
  148. package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
  149. package/dialog/RollbackWorkloadDialog.vue +2 -5
  150. package/dialog/SearchDialog.vue +1 -0
  151. package/directives/ui-context.ts +103 -0
  152. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +2 -2
  153. package/edit/auth/__tests__/oidc.test.ts +26 -0
  154. package/edit/auth/github.vue +5 -0
  155. package/edit/auth/oidc.vue +5 -1
  156. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  157. package/edit/cloudcredential.vue +1 -1
  158. package/edit/configmap.vue +1 -0
  159. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  160. package/edit/fleet.cattle.io.gitrepo.vue +0 -10
  161. package/edit/fleet.cattle.io.helmop.vue +6 -6
  162. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  163. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  164. package/edit/logging-flow/index.vue +1 -0
  165. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  166. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  167. package/edit/management.cattle.io.project.vue +1 -0
  168. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  169. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  170. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  171. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  172. package/edit/monitoring.coreos.com.route.vue +1 -1
  173. package/edit/namespace.vue +1 -0
  174. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  175. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  176. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  177. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  178. package/edit/node.vue +1 -0
  179. package/edit/persistentvolume/index.vue +27 -22
  180. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  181. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  182. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  183. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  184. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  185. package/edit/persistentvolume/plugins/csi.vue +18 -16
  186. package/edit/persistentvolume/plugins/fc.vue +13 -14
  187. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  188. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  189. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  190. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  191. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  192. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  193. package/edit/persistentvolume/plugins/local.vue +1 -3
  194. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  195. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  196. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  197. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  198. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  199. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  200. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  201. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  202. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  203. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
  204. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
  205. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
  206. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
  207. package/edit/provisioning.cattle.io.cluster/index.vue +28 -18
  208. package/edit/provisioning.cattle.io.cluster/rke2.vue +50 -16
  209. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
  210. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
  211. package/edit/secret/index.vue +1 -1
  212. package/edit/service.vue +9 -4
  213. package/edit/serviceaccount.vue +1 -0
  214. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  215. package/edit/workload/index.vue +2 -1
  216. package/edit/workload/mixins/workload.js +1 -1
  217. package/initialize/App.vue +4 -4
  218. package/initialize/install-directives.js +2 -0
  219. package/initialize/install-plugins.js +19 -2
  220. package/list/provisioning.cattle.io.cluster.vue +15 -2
  221. package/machine-config/amazonec2.vue +42 -135
  222. package/machine-config/components/EC2Networking.vue +490 -0
  223. package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
  224. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
  225. package/machine-config/digitalocean.vue +11 -0
  226. package/machine-config/google.vue +1 -1
  227. package/mixins/__tests__/brand.spec.ts +2 -2
  228. package/mixins/__tests__/chart.test.ts +21 -0
  229. package/mixins/brand.js +1 -7
  230. package/mixins/chart.js +7 -1
  231. package/mixins/create-edit-view/index.js +5 -0
  232. package/models/__tests__/chart.test.ts +33 -4
  233. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
  234. package/models/chart.js +25 -13
  235. package/models/cluster/node.js +13 -6
  236. package/models/cluster.x-k8s.io.machine.js +10 -20
  237. package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
  238. package/models/management.cattle.io.cluster.js +21 -3
  239. package/models/management.cattle.io.kontainerdriver.js +1 -0
  240. package/models/provisioning.cattle.io.cluster.js +249 -33
  241. package/package.json +8 -7
  242. package/pages/auth/login.vue +41 -5
  243. package/pages/auth/setup.vue +1 -1
  244. package/pages/auth/verify.vue +3 -3
  245. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  246. package/pages/c/_cluster/apps/charts/chart.vue +33 -15
  247. package/pages/c/_cluster/apps/charts/index.vue +11 -13
  248. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  249. package/pages/c/_cluster/explorer/index.vue +8 -6
  250. package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
  251. package/pages/c/_cluster/settings/brand.vue +1 -1
  252. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  253. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  254. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  255. package/pages/home.vue +14 -4
  256. package/pkg/auto-import.js +3 -3
  257. package/pkg/dynamic-importer.lib.js +5 -1
  258. package/pkg/import.js +1 -1
  259. package/plugins/dashboard-client-init.js +3 -0
  260. package/plugins/dashboard-store/getters.js +19 -2
  261. package/plugins/dashboard-store/model-loader.js +1 -1
  262. package/plugins/dashboard-store/resource-class.js +10 -6
  263. package/plugins/dynamic-content.js +13 -0
  264. package/plugins/i18n.js +8 -0
  265. package/plugins/plugin.js +2 -2
  266. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
  267. package/plugins/steve/steve-class.js +1 -1
  268. package/plugins/steve/steve-pagination-utils.ts +39 -20
  269. package/plugins/steve/subscribe.js +17 -9
  270. package/plugins/subscribe-events.ts +4 -2
  271. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  272. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
  273. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  274. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  275. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  276. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  277. package/rancher-components/Pill/types.ts +0 -1
  278. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
  279. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  280. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  281. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  282. package/rancher-components/RcIcon/index.ts +1 -0
  283. package/rancher-components/RcIcon/types.ts +160 -0
  284. package/rancher-components/utils/status.test.ts +67 -0
  285. package/rancher-components/utils/status.ts +77 -0
  286. package/scripts/typegen.sh +1 -0
  287. package/store/__tests__/catalog.test.ts +1 -1
  288. package/store/action-menu.js +8 -0
  289. package/store/auth.js +4 -4
  290. package/store/catalog.js +6 -0
  291. package/store/features.js +1 -0
  292. package/store/i18n.js +3 -3
  293. package/store/index.js +40 -19
  294. package/store/notifications.ts +51 -4
  295. package/store/plugins.js +7 -3
  296. package/store/prefs.js +6 -6
  297. package/store/type-map.js +7 -7
  298. package/store/ui-context.ts +86 -0
  299. package/store/wm.ts +244 -0
  300. package/types/notifications/index.ts +27 -3
  301. package/types/shell/index.d.ts +80 -4
  302. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  303. package/types/store/pagination.types.ts +157 -9
  304. package/types/store/subscribe-events.types.ts +8 -1
  305. package/types/store/subscribe.types.ts +1 -0
  306. package/types/window-manager.ts +24 -0
  307. package/utils/__tests__/object.test.ts +19 -0
  308. package/utils/__tests__/provider.test.ts +98 -0
  309. package/utils/__tests__/selector-typed.test.ts +263 -0
  310. package/utils/__tests__/version.test.ts +19 -1
  311. package/utils/autoscaler-utils.ts +7 -0
  312. package/utils/back-off.ts +3 -3
  313. package/utils/brand.ts +29 -0
  314. package/utils/chart.js +18 -0
  315. package/utils/color.js +1 -1
  316. package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
  317. package/utils/dynamic-content/__tests__/info.test.ts +21 -9
  318. package/utils/dynamic-content/announcement.ts +142 -0
  319. package/utils/dynamic-content/example.json +40 -0
  320. package/utils/dynamic-content/index.ts +6 -2
  321. package/utils/dynamic-content/info.ts +44 -2
  322. package/utils/dynamic-content/new-release.ts +1 -1
  323. package/utils/dynamic-content/notification-handler.ts +48 -0
  324. package/utils/dynamic-content/types.d.ts +53 -1
  325. package/utils/dynamic-importer.js +2 -2
  326. package/utils/favicon.js +4 -4
  327. package/utils/object.js +20 -2
  328. package/utils/pagination-utils.ts +2 -2
  329. package/utils/pagination-wrapper.ts +13 -9
  330. package/utils/provider.ts +14 -0
  331. package/utils/scroll.js +7 -0
  332. package/utils/selector-typed.ts +6 -2
  333. package/utils/settings.ts +15 -0
  334. package/utils/unit-tests/pagination-utils.spec.ts +8 -8
  335. package/utils/validators/machine-pool.ts +13 -3
  336. package/utils/version.js +15 -0
  337. package/vue.config.js +3 -3
  338. package/assets/images/icons/document.svg +0 -3
  339. package/plugins/nuxt-client-init.js +0 -3
  340. package/store/wm.js +0 -95
  341. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  342. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  343. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  344. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  345. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
@@ -0,0 +1,7 @@
1
+ import { AUTOSCALER } from '@shell/store/features';
2
+
3
+ export function isAutoscalerFeatureFlagEnabled(store: any): boolean {
4
+ const rootGetters = store.rootGetters || store.getters;
5
+
6
+ return rootGetters['features/get'](AUTOSCALER);
7
+ }
package/utils/back-off.ts CHANGED
@@ -137,9 +137,9 @@ class BackOff {
137
137
 
138
138
  // First step is immediate (0.001s)
139
139
  // Second and others are exponential
140
- // 1, 2, 3, 4, 5, 6, 7, 8, 9
141
- // 1, 4, 9, 16, 25, 36, 49, 64, 81
142
- // 0.25s, 1s, 2.25s, 4s, 6.25s, 9s, 12.25s, 16s, 20.25s
140
+ // Try: 1, 2, 3, 4, 5, 6, 7, 8, 9
141
+ // Multiple: 1, 4, 9, 16, 25, 36, 49, 64, 81
142
+ // Actual Time: 0.25s, 1s, 2.25s, 4s, 6.25s, 9s, 12.25s, 16s, 20.25s
143
143
  const delay = backOffTry === 0 ? 1 : Math.pow(backOffTry, 2) * 250;
144
144
 
145
145
  this.log('info', id, `Delaying call (attempt ${ backOffTry + 1 }, delayed by ${ delay }ms)`, description);
package/utils/brand.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Brand/Theme metadata
3
+ */
4
+ export interface BrandMeta {
5
+ // Does the banner have a stylesheet?
6
+ hasStylesheet?: string;
7
+ banner?: {
8
+ // Text alignment for the banner text overlayed on the banner image
9
+ textAlign?: string;
10
+ }
11
+ }
12
+
13
+ /**
14
+ * Get the brand/theme meta information for the specified brand
15
+ *
16
+ * @param brand - The brand identifier
17
+ * @returns Brand meta information or empty object if none available
18
+ */
19
+ export function getBrandMeta(brand: string): BrandMeta {
20
+ let brandMeta: BrandMeta = {};
21
+
22
+ if (brand) {
23
+ try {
24
+ brandMeta = require(`~shell/assets/brand/${ brand }/metadata.json`);
25
+ } catch {}
26
+ }
27
+
28
+ return brandMeta;
29
+ }
package/utils/chart.js ADDED
@@ -0,0 +1,18 @@
1
+ import { compatibleVersionsFor } from '@shell/store/catalog';
2
+
3
+ /**
4
+ * Get the latest chart version that is compatible with the cluster's OS and user's pre-release preference.
5
+ * @param {Object} chart - The chart object.
6
+ * @param {Array<string>} workerOSs - The list of worker OSs for the cluster.
7
+ * @param {boolean} showPrerelease - Whether to include pre-release versions.
8
+ * @returns {Object} The latest compatible chart version object.
9
+ */
10
+ export function getLatestCompatibleVersion(chart, workerOSs, showPrerelease) {
11
+ if (!chart?.versions?.length) {
12
+ return {};
13
+ }
14
+
15
+ const compatible = compatibleVersionsFor(chart, workerOSs, showPrerelease);
16
+
17
+ return (compatible.length ? compatible[0] : chart.versions[0]) || {};
18
+ }
package/utils/color.js CHANGED
@@ -13,7 +13,7 @@ Primary color classes from _light.scss
13
13
 
14
14
  */
15
15
 
16
- const Color = require('color');
16
+ import Color from 'color';
17
17
 
18
18
  export function createCssVars(color, theme = 'light', name = 'primary') {
19
19
  const contrastOpts = theme === 'light' ? LIGHT_CONTRAST_COLORS : DARK_CONTRAST_COLORS;
@@ -0,0 +1,498 @@
1
+ import { processAnnouncements, ANNOUNCEMENT_PREFIX } from '../announcement';
2
+ import { NotificationLevel, Notification } from '@shell/types/notifications';
3
+ import { READ_ANNOUNCEMENTS } from '@shell/store/prefs';
4
+ import { DynamicContentAnnouncementHandlerName } from '../notification-handler';
5
+ import { Context, VersionInfo, Announcement } from '../types';
6
+ import semver from 'semver';
7
+
8
+ jest.mock('semver', () => ({
9
+ ...jest.requireActual('semver'),
10
+ satisfies: jest.fn(),
11
+ }));
12
+
13
+ describe('processAnnouncements', () => {
14
+ let mockDispatch: jest.Mock;
15
+ let mockGetters: any;
16
+ let mockLogger: any;
17
+ let mockContext: Context;
18
+
19
+ const VERSION_270 = { version: semver.coerce('v2.7.0') as semver.SemVer, isPrime: false };
20
+
21
+ beforeEach(() => {
22
+ mockDispatch = jest.fn();
23
+ mockGetters = {
24
+ 'notifications/item': jest.fn(),
25
+ 'prefs/get': jest.fn(),
26
+ };
27
+ mockLogger = {
28
+ error: jest.fn(),
29
+ info: jest.fn(),
30
+ };
31
+ mockContext = {
32
+ dispatch: mockDispatch,
33
+ getters: mockGetters,
34
+ logger: mockLogger,
35
+ isAdmin: false, // Default to non-admin
36
+ } as unknown as Context;
37
+
38
+ // Reset all mocks before each test
39
+ jest.clearAllMocks();
40
+
41
+ // Default mock for semver.satisfies to return true
42
+ (semver.satisfies as jest.Mock).mockReturnValue(true);
43
+ });
44
+
45
+ // --- Early Exit Conditions ---
46
+ it('should return early if no announcements are provided', async() => {
47
+ await processAnnouncements(mockContext, undefined, VERSION_270);
48
+ expect(mockDispatch).not.toHaveBeenCalled();
49
+ expect(mockLogger.info).not.toHaveBeenCalled();
50
+ });
51
+
52
+ it('should return early if announcements array is empty', async() => {
53
+ await processAnnouncements(mockContext, [], VERSION_270);
54
+ expect(mockDispatch).not.toHaveBeenCalled();
55
+ expect(mockLogger.info).not.toHaveBeenCalled();
56
+ });
57
+
58
+ it('should return early if versionInfo is undefined', async() => {
59
+ await processAnnouncements(mockContext, [{
60
+ id: '1',
61
+ target: 'notification/announcement',
62
+ title: 'Test',
63
+ message: 'Msg'
64
+ }], undefined as any);
65
+ expect(mockDispatch).not.toHaveBeenCalled();
66
+ expect(mockLogger.info).not.toHaveBeenCalled();
67
+ });
68
+
69
+ it('should return early if versionInfo.version is undefined', async() => {
70
+ await processAnnouncements(mockContext, [{
71
+ id: '1',
72
+ target: 'notification/announcement',
73
+ title: 'Test',
74
+ message: 'Msg'
75
+ }], { version: undefined as any, isPrime: false });
76
+ expect(mockDispatch).not.toHaveBeenCalled();
77
+ expect(mockLogger.info).not.toHaveBeenCalled();
78
+ });
79
+
80
+ // --- Version and Audience Filtering ---
81
+ it('should not process announcement if version does not satisfy requirement', async() => {
82
+ (semver.satisfies as jest.Mock).mockReturnValue(false);
83
+ const announcements: Announcement[] = [
84
+ {
85
+ id: '1',
86
+ target: 'notification/announcement',
87
+ title: 'Test',
88
+ message: 'Msg',
89
+ version: 'v2.8.0'
90
+ },
91
+ ];
92
+
93
+ await processAnnouncements(mockContext, announcements, VERSION_270);
94
+
95
+ expect(semver.satisfies).toHaveBeenCalledWith(VERSION_270.version, announcements[0].version);
96
+ expect(mockDispatch).not.toHaveBeenCalled();
97
+ expect(mockLogger.info).not.toHaveBeenCalled();
98
+ });
99
+
100
+ it('should not process admin-only announcement if user is not admin', async() => {
101
+ mockContext.isAdmin = false;
102
+ const announcements: Announcement[] = [
103
+ {
104
+ id: '1',
105
+ target: 'notification/announcement',
106
+ title: 'Test',
107
+ message: 'Msg',
108
+ audience: 'admin'
109
+ },
110
+ ];
111
+
112
+ await processAnnouncements(mockContext, announcements, VERSION_270);
113
+
114
+ expect(mockDispatch).not.toHaveBeenCalled();
115
+ expect(mockLogger.info).not.toHaveBeenCalled();
116
+ });
117
+
118
+ it('should process admin-only announcement if user is admin', async() => {
119
+ mockContext.isAdmin = true;
120
+ const announcements: Announcement[] = [
121
+ {
122
+ id: '1',
123
+ target: 'notification/announcement',
124
+ title: 'Test',
125
+ message: 'Msg',
126
+ audience: 'admin'
127
+ },
128
+ ];
129
+ const versionInfo: VersionInfo = { version: VERSION_270.version };
130
+
131
+ await processAnnouncements(mockContext, announcements, versionInfo);
132
+
133
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
134
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expect.any(Object));
135
+ });
136
+
137
+ it('should process all-audience announcement regardless of admin status', async() => {
138
+ mockContext.isAdmin = false; // Test with non-admin
139
+ const announcements: Announcement[] = [
140
+ {
141
+ id: '1',
142
+ target: 'notification/announcement',
143
+ title: 'Test',
144
+ message: 'Msg',
145
+ audience: 'all'
146
+ },
147
+ ];
148
+
149
+ await processAnnouncements(mockContext, announcements, VERSION_270);
150
+
151
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
152
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expect.any(Object));
153
+ });
154
+
155
+ // --- Target Type Handling ---
156
+ it('should log error for unsupported announcement target type', async() => {
157
+ const announcements: Announcement[] = [
158
+ {
159
+ id: '1',
160
+ target: 'unsupported/type',
161
+ title: 'Test',
162
+ message: 'Msg'
163
+ },
164
+ ];
165
+
166
+ await processAnnouncements(mockContext, announcements, VERSION_270);
167
+
168
+ expect(mockLogger.error).toHaveBeenCalledWith('Announcement type unsupported/type is not supported');
169
+ expect(mockDispatch).not.toHaveBeenCalled();
170
+ });
171
+
172
+ it('should log error for unsupported notification sub-type', async() => {
173
+ const announcements: Announcement[] = [
174
+ {
175
+ id: '1',
176
+ target: 'notification/unsupported',
177
+ title: 'Test',
178
+ message: 'Msg'
179
+ },
180
+ ];
181
+
182
+ await processAnnouncements(mockContext, announcements, VERSION_270);
183
+
184
+ expect(mockLogger.error).toHaveBeenCalledWith('Announcement notification type unsupported is not supported');
185
+ expect(mockDispatch).not.toHaveBeenCalled();
186
+ });
187
+
188
+ // --- Notification Creation Logic ---
189
+ it('should log error and not add notification if announcement has no ID', async() => {
190
+ const announcements: Announcement[] = [
191
+ {
192
+ target: 'notification/announcement',
193
+ title: 'Test',
194
+ message: 'Msg'
195
+ } as Announcement, // Missing ID
196
+ ];
197
+
198
+ await processAnnouncements(mockContext, announcements, VERSION_270);
199
+
200
+ expect(mockLogger.error).toHaveBeenCalledWith('No ID For announcement - not going to add a notification for the announcement');
201
+ expect(mockDispatch).not.toHaveBeenCalled();
202
+ });
203
+
204
+ it('should not add notification if one with the same ID already exists', async() => {
205
+ const announcementId = 'existing-announcement';
206
+
207
+ mockGetters['notifications/item'].mockReturnValue({ id: `${ ANNOUNCEMENT_PREFIX }${ announcementId }` });
208
+ const announcements: Announcement[] = [
209
+ {
210
+ id: announcementId,
211
+ target: 'notification/announcement',
212
+ title: 'Test',
213
+ message: 'Msg'
214
+ },
215
+ ];
216
+
217
+ await processAnnouncements(mockContext, announcements, VERSION_270);
218
+
219
+ expect(mockGetters['notifications/item']).toHaveBeenCalledWith(`${ ANNOUNCEMENT_PREFIX }${ announcementId }`);
220
+ expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining('Not adding announcement with ID'));
221
+ expect(mockDispatch).not.toHaveBeenCalled();
222
+ });
223
+
224
+ it('should not add notification if announcement ID is in READ_ANNOUNCEMENTS preference', async() => {
225
+ const announcementId = 'read-announcement';
226
+
227
+ mockGetters['notifications/item'].mockReturnValue(undefined); // No existing notification
228
+ mockGetters['prefs/get'].mockImplementation((key: string) => {
229
+ if (key === READ_ANNOUNCEMENTS) {
230
+ return `some-other-id,${ announcementId },another-one`;
231
+ }
232
+
233
+ return '';
234
+ });
235
+ const announcements: Announcement[] = [
236
+ {
237
+ id: announcementId,
238
+ target: 'notification/announcement',
239
+ title: 'Test',
240
+ message: 'Msg'
241
+ },
242
+ ];
243
+
244
+ await processAnnouncements(mockContext, announcements, VERSION_270);
245
+
246
+ expect(mockGetters['prefs/get']).toHaveBeenCalledWith(READ_ANNOUNCEMENTS);
247
+ expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining('Not adding announcement with ID'));
248
+ expect(mockDispatch).not.toHaveBeenCalled();
249
+ });
250
+
251
+ it('should add a new announcement notification with default level (announcement)', async() => {
252
+ const announcementId = 'new-announcement';
253
+
254
+ mockGetters['notifications/item'].mockReturnValue(undefined);
255
+ mockGetters['prefs/get'].mockReturnValue('');
256
+ const announcements: Announcement[] = [
257
+ {
258
+ id: announcementId,
259
+ target: 'notification/announcement',
260
+ title: 'New Announcement',
261
+ message: 'This is a new message.'
262
+ },
263
+ ];
264
+
265
+ await processAnnouncements(mockContext, announcements, VERSION_270);
266
+
267
+ const expectedNotification: Notification = {
268
+ id: `${ ANNOUNCEMENT_PREFIX }${ announcementId }`,
269
+ level: NotificationLevel.Announcement,
270
+ title: 'New Announcement',
271
+ message: 'This is a new message.',
272
+ handlerName: DynamicContentAnnouncementHandlerName,
273
+ };
274
+
275
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
276
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expectedNotification);
277
+ expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining(`Adding announcement with ID ${ ANNOUNCEMENT_PREFIX }${ announcementId }`));
278
+ });
279
+
280
+ it('should add a new info level notification', async() => {
281
+ const announcementId = 'new-info';
282
+
283
+ mockGetters['notifications/item'].mockReturnValue(undefined);
284
+ mockGetters['prefs/get'].mockReturnValue('');
285
+ const announcements: Announcement[] = [
286
+ {
287
+ id: announcementId,
288
+ target: 'notification/info',
289
+ title: 'Info Title',
290
+ message: 'Info Message'
291
+ },
292
+ ];
293
+
294
+ await processAnnouncements(mockContext, announcements, VERSION_270);
295
+
296
+ const expectedNotification: Notification = {
297
+ id: `${ ANNOUNCEMENT_PREFIX }${ announcementId }`,
298
+ level: NotificationLevel.Info,
299
+ title: 'Info Title',
300
+ message: 'Info Message',
301
+ handlerName: DynamicContentAnnouncementHandlerName,
302
+ };
303
+
304
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
305
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expectedNotification);
306
+ });
307
+
308
+ it('should add a new warning level notification', async() => {
309
+ const announcementId = 'new-warning';
310
+
311
+ mockGetters['notifications/item'].mockReturnValue(undefined);
312
+ mockGetters['prefs/get'].mockReturnValue('');
313
+ const announcements: Announcement[] = [
314
+ {
315
+ id: announcementId,
316
+ target: 'notification/warning',
317
+ title: 'Warning Title',
318
+ message: 'Warning Message'
319
+ },
320
+ ];
321
+
322
+ await processAnnouncements(mockContext, announcements, VERSION_270);
323
+
324
+ const expectedNotification: Notification = {
325
+ id: `${ ANNOUNCEMENT_PREFIX }${ announcementId }`,
326
+ level: NotificationLevel.Warning,
327
+ title: 'Warning Title',
328
+ message: 'Warning Message',
329
+ handlerName: DynamicContentAnnouncementHandlerName,
330
+ };
331
+
332
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
333
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expectedNotification);
334
+ });
335
+
336
+ // --- Call To Action (CTA) Handling ---
337
+ it('should add notification with primary CTA', async() => {
338
+ const announcementId = 'cta-primary';
339
+
340
+ mockGetters['notifications/item'].mockReturnValue(undefined);
341
+ mockGetters['prefs/get'].mockReturnValue('');
342
+
343
+ const announcements: Announcement[] = [
344
+ {
345
+ id: announcementId,
346
+ target: 'notification/announcement',
347
+ title: 'CTA Primary',
348
+ message: 'Message',
349
+ cta: { primary: { action: 'Click Me', link: '/some/path' } },
350
+ },
351
+ ];
352
+
353
+ await processAnnouncements(mockContext, announcements, VERSION_270);
354
+
355
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
356
+ const notification = mockDispatch.mock.calls[0][1];
357
+
358
+ expect(notification.primaryAction).toStrictEqual({
359
+ label: 'Click Me',
360
+ target: '/some/path'
361
+ });
362
+ expect(notification.secondaryAction).toBeUndefined();
363
+ });
364
+
365
+ it('should add notification with secondary CTA', async() => {
366
+ const announcementId = 'cta-secondary';
367
+
368
+ mockGetters['notifications/item'].mockReturnValue(undefined);
369
+ mockGetters['prefs/get'].mockReturnValue('');
370
+ const announcements: Announcement[] = [
371
+ {
372
+ id: announcementId,
373
+ target: 'notification/announcement',
374
+ title: 'CTA Secondary',
375
+ message: 'Message',
376
+ cta: { secondary: { action: 'More Info', link: 'http://example.com' } },
377
+ },
378
+ ];
379
+
380
+ await processAnnouncements(mockContext, announcements, VERSION_270);
381
+
382
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
383
+ const notification = mockDispatch.mock.calls[0][1];
384
+
385
+ expect(notification.secondaryAction).toStrictEqual({ label: 'More Info', target: 'http://example.com' });
386
+ expect(notification.primaryAction).toBeUndefined();
387
+ });
388
+
389
+ it('should add notification with both primary and secondary CTAs', async() => {
390
+ const announcementId = 'cta-both';
391
+
392
+ mockGetters['notifications/item'].mockReturnValue(undefined);
393
+ mockGetters['prefs/get'].mockReturnValue('');
394
+ const announcements: Announcement[] = [
395
+ {
396
+ id: announcementId,
397
+ target: 'notification/announcement',
398
+ title: 'CTA Both',
399
+ message: 'Message',
400
+ cta: {
401
+ primary: {
402
+ action: 'Primary',
403
+ link: '/primary'
404
+ },
405
+ secondary: {
406
+ action: 'Secondary',
407
+ link: '/secondary'
408
+ },
409
+ },
410
+ },
411
+ ];
412
+
413
+ await processAnnouncements(mockContext, announcements, VERSION_270);
414
+
415
+ expect(mockDispatch).toHaveBeenCalledTimes(1);
416
+ const notification = mockDispatch.mock.calls[0][1];
417
+
418
+ expect(notification.primaryAction).toStrictEqual({ label: 'Primary', target: '/primary' });
419
+ expect(notification.secondaryAction).toStrictEqual({ label: 'Secondary', target: '/secondary' });
420
+ });
421
+
422
+ // --- Multiple Announcements ---
423
+ it('should process multiple announcements correctly, skipping invalid ones', async() => {
424
+ mockContext.isAdmin = true; // Ensure admin-only can be processed
425
+ mockGetters['notifications/item'].mockImplementation((id: string) => {
426
+ if (id === `${ ANNOUNCEMENT_PREFIX }existing-id`) {
427
+ return { id };
428
+ }
429
+
430
+ return undefined;
431
+ });
432
+ mockGetters['prefs/get'].mockImplementation((key: string) => {
433
+ if (key === READ_ANNOUNCEMENTS) {
434
+ return 'read-id';
435
+ }
436
+
437
+ return '';
438
+ });
439
+
440
+ const announcements: Announcement[] = [
441
+ {
442
+ id: 'valid-1',
443
+ target: 'notification/info',
444
+ title: 'Valid 1',
445
+ message: 'Msg 1',
446
+ audience: 'all'
447
+ },
448
+ {
449
+ id: 'existing-id',
450
+ target: 'notification/announcement',
451
+ title: 'Existing',
452
+ message: 'Msg Existing'
453
+ }, // Should be skipped
454
+ {
455
+ id: 'read-id',
456
+ target: 'notification/warning',
457
+ title: 'Read',
458
+ message: 'Msg Read'
459
+ }, // Should be skipped
460
+ {
461
+ id: 'valid-2',
462
+ target: 'notification/announcement',
463
+ title: 'Valid 2',
464
+ message: 'Msg 2',
465
+ audience: 'admin'
466
+ },
467
+ {
468
+ id: 'invalid-target',
469
+ target: 'unsupported/type',
470
+ title: 'Invalid',
471
+ message: 'Msg Invalid'
472
+ }, // Should log error
473
+ {
474
+ id: 'valid-3',
475
+ target: 'notification/info',
476
+ title: 'Valid 3',
477
+ message: 'Msg 3',
478
+ version: 'v1.0.0'
479
+ }, // semver.satisfies is mocked to true by default
480
+ ];
481
+
482
+ await processAnnouncements(mockContext, announcements, VERSION_270);
483
+
484
+ // Expect 3 notifications to be added (valid-1, valid-2, valid-3)
485
+ expect(mockDispatch).toHaveBeenCalledTimes(3);
486
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expect.objectContaining({ id: `${ ANNOUNCEMENT_PREFIX }valid-1` }));
487
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expect.objectContaining({ id: `${ ANNOUNCEMENT_PREFIX }valid-2` }));
488
+ expect(mockDispatch).toHaveBeenCalledWith('notifications/add', expect.objectContaining({ id: `${ ANNOUNCEMENT_PREFIX }valid-3` }));
489
+
490
+ // Expect errors for invalid target
491
+ expect(mockLogger.error).toHaveBeenCalledWith('Announcement type unsupported/type is not supported');
492
+
493
+ // Expect info logs for skipped announcements
494
+ expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining('Not adding announcement with ID '));
495
+ expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining(`${ ANNOUNCEMENT_PREFIX }existing-id`));
496
+ expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining(`${ ANNOUNCEMENT_PREFIX }read-id`));
497
+ });
498
+ });
@@ -96,6 +96,8 @@ describe('systemInfoProvider', () => {
96
96
  return undefined;
97
97
  }),
98
98
  'management/schemaFor': jest.fn(),
99
+ localCluster: mockClusters.find((c) => c.id === 'local') || null,
100
+ 'features/get': jest.fn(() => 'abc'),
99
101
  };
100
102
 
101
103
  (version.getVersionData as jest.Mock).mockReturnValue({
@@ -128,6 +130,7 @@ describe('systemInfoProvider', () => {
128
130
  expect(qs).toContain('bl=en-US');
129
131
  expect(qs).toContain('bs=1024x768');
130
132
  expect(qs).toContain('ss=1920x1080');
133
+ expect(qs).toContain('ff-usc=abc');
131
134
  });
132
135
 
133
136
  it('should handle missing or partial data gracefully', () => {
@@ -159,6 +162,10 @@ describe('systemInfoProvider', () => {
159
162
 
160
163
  mockGetters['uiplugins/plugins'] = null; // No plugins
161
164
  mockGetters['auth/principalId'] = null; // No user
165
+ mockGetters['localCluster'] = null; // No clusters
166
+ mockGetters['features/get'] = () => {
167
+ throw new Error('unknown feature');
168
+ };
162
169
 
163
170
  const infoProvider = new SystemInfoProvider(mockGetters, {});
164
171
  const qs = infoProvider.buildQueryString();
@@ -175,6 +182,7 @@ describe('systemInfoProvider', () => {
175
182
  expect(qs).not.toContain('lnc=');
176
183
  expect(qs).not.toContain('xkn=');
177
184
  expect(qs).not.toContain('xcc=');
185
+ expect(qs).not.toContain('ff-usc=');
178
186
  });
179
187
 
180
188
  it('should handle getAll returning undefined when types are not registered', () => {
@@ -191,6 +199,7 @@ describe('systemInfoProvider', () => {
191
199
 
192
200
  mockGetters['auth/principalId'] = 'user-456';
193
201
  mockGetters['uiplugins/plugins'] = []; // No plugins
202
+ mockGetters['localCluster'] = null; // No clusters
194
203
 
195
204
  const infoProvider = new SystemInfoProvider(mockGetters, {});
196
205
  const qs = infoProvider.buildQueryString();
@@ -199,7 +208,6 @@ describe('systemInfoProvider', () => {
199
208
  expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'install-uuid');
200
209
  expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'server-version-type');
201
210
  expect(mockGetters['management/typeRegistered']).toHaveBeenCalledWith(COUNT);
202
- expect(mockGetters['management/typeRegistered']).toHaveBeenCalledWith(MANAGEMENT.CLUSTER);
203
211
  expect(mockGetters['management/all']).not.toHaveBeenCalled();
204
212
 
205
213
  // Verify the query string is built with fallback or empty values
@@ -225,6 +233,16 @@ describe('systemInfoProvider', () => {
225
233
  return { id, value: '' }; // Empty values for all settings
226
234
  }
227
235
  });
236
+
237
+ // local cluster with missing properties
238
+ const localCluster = {
239
+ id: 'local',
240
+ isLocal: true,
241
+ status: { nodeCount: 1 },
242
+ // kubernetesVersionBase is missing
243
+ // provisioner is missing
244
+ };
245
+
228
246
  mockGetters['management/all'].mockImplementation((type: string) => {
229
247
  if (type === MANAGEMENT.SETTING) {
230
248
  // Return settings, but with empty values
@@ -237,20 +255,14 @@ describe('systemInfoProvider', () => {
237
255
  return [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 1 } } } }];
238
256
  }
239
257
  if (type === MANAGEMENT.CLUSTER) {
240
- // local cluster with missing properties
241
- return [{
242
- id: 'local',
243
- isLocal: true,
244
- status: { nodeCount: 1 },
245
- // kubernetesVersionBase is missing
246
- // provisioner is missing
247
- }];
258
+ return [localCluster];
248
259
  }
249
260
 
250
261
  return [];
251
262
  });
252
263
 
253
264
  mockGetters['auth/principalId'] = null; // No user
265
+ mockGetters['localCluster'] = localCluster;
254
266
 
255
267
  const infoProvider = new SystemInfoProvider(mockGetters, {});
256
268
  const qs = infoProvider.buildQueryString();