@rancher/shell 3.0.7 → 3.0.8-rc.10

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 (375) 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/images/vendor/githubapp.svg +13 -0
  27. package/assets/styles/base/_typography.scss +2 -1
  28. package/assets/styles/fonts/_fontstack.scss +53 -1
  29. package/assets/styles/global/_cards.scss +0 -3
  30. package/assets/styles/global/_layout.scss +21 -35
  31. package/assets/styles/themes/_dark.scss +1 -1
  32. package/assets/styles/themes/_light.scss +1 -1
  33. package/assets/styles/themes/_modern.scss +16 -8
  34. package/assets/styles/themes/_suse.scss +116 -24
  35. package/assets/translations/en-us.yaml +185 -21
  36. package/assets/translations/zh-hans.yaml +0 -4
  37. package/components/AutoscalerCard.vue +113 -0
  38. package/components/AutoscalerTab.vue +94 -0
  39. package/components/BackLink.vue +8 -0
  40. package/components/BannerGraphic.vue +36 -21
  41. package/components/BrandImage.vue +17 -6
  42. package/components/ClusterIconMenu.vue +1 -1
  43. package/components/ClusterProviderIcon.vue +1 -1
  44. package/components/Cron/CronExpressionEditor.vue +1 -1
  45. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  46. package/components/Drawer/Chrome.vue +2 -6
  47. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
  48. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
  49. package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
  50. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
  51. package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
  52. package/components/Drawer/types.ts +3 -0
  53. package/components/DynamicContent/DynamicContentBanner.vue +102 -0
  54. package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
  55. package/components/DynamicContent/DynamicContentIcon.vue +132 -0
  56. package/components/DynamicContent/DynamicContentPanel.vue +112 -0
  57. package/components/DynamicContent/content.ts +78 -0
  58. package/components/EmberPage.vue +1 -1
  59. package/components/IconOrSvg.vue +2 -2
  60. package/components/Inactivity.vue +222 -106
  61. package/components/InstallHelmCharts.vue +2 -2
  62. package/components/PaginatedResourceTable.vue +2 -6
  63. package/components/PopoverCard.vue +192 -0
  64. package/components/Questions/__tests__/index.test.ts +159 -0
  65. package/components/Resource/Detail/CopyToClipboard.vue +4 -1
  66. package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
  67. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  68. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
  69. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
  70. package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
  71. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  72. package/components/Resource/Detail/Metadata/composables.ts +9 -9
  73. package/components/Resource/Detail/Metadata/index.vue +3 -3
  74. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
  75. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
  76. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
  77. package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
  78. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  79. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  80. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  81. package/components/Resource/Detail/composables.ts +12 -0
  82. package/components/ResourceDetail/Masthead/latest.vue +29 -0
  83. package/components/ResourceDetail/index.vue +5 -2
  84. package/components/ResourceList/Masthead.vue +1 -1
  85. package/components/SortableTable/index.vue +18 -2
  86. package/components/Tabbed/__tests__/index.test.ts +86 -0
  87. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  88. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  89. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  90. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  91. package/components/__tests__/AutoscalerCard.test.ts +154 -0
  92. package/components/__tests__/AutoscalerTab.test.ts +125 -0
  93. package/components/__tests__/PopoverCard.test.ts +204 -0
  94. package/components/auth/SelectPrincipal.vue +24 -6
  95. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  96. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  97. package/components/fleet/FleetSecretSelector.vue +127 -0
  98. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  99. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  100. package/components/form/FileImageSelector.vue +13 -4
  101. package/components/form/FileSelector.vue +11 -2
  102. package/components/form/ResourceLabeledSelect.vue +1 -0
  103. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  104. package/components/formatter/Autoscaler.vue +97 -0
  105. package/components/formatter/InternalExternalIP.vue +198 -24
  106. package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
  107. package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
  108. package/components/google/util/__tests__/formatter.test.ts +47 -0
  109. package/components/google/util/formatter.ts +5 -2
  110. package/components/nav/Group.vue +12 -3
  111. package/components/nav/Header.vue +37 -16
  112. package/components/nav/NamespaceFilter.vue +13 -1
  113. package/components/nav/NotificationCenter/index.vue +2 -1
  114. package/components/nav/TopLevelMenu.helper.ts +16 -6
  115. package/components/nav/TopLevelMenu.vue +4 -2
  116. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  117. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  118. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  119. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  120. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  121. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  122. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  123. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  124. package/components/nav/WindowManager/constants.ts +23 -0
  125. package/components/nav/WindowManager/index.vue +61 -575
  126. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  127. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  128. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  129. package/components/templates/default.vue +4 -40
  130. package/components/templates/home.vue +31 -5
  131. package/components/templates/plain.vue +30 -4
  132. package/components/templates/standalone.vue +1 -1
  133. package/composables/useI18n.ts +10 -1
  134. package/composables/useInterval.ts +15 -0
  135. package/config/__test__/uiplugins.test.ts +309 -0
  136. package/config/labels-annotations.js +9 -1
  137. package/config/product/auth.js +1 -0
  138. package/config/product/explorer.js +3 -1
  139. package/config/product/manager.js +20 -9
  140. package/config/query-params.js +1 -0
  141. package/config/router/routes.js +10 -2
  142. package/config/settings.ts +10 -2
  143. package/config/store.js +4 -2
  144. package/config/table-headers.js +8 -0
  145. package/config/types.js +11 -0
  146. package/config/uiplugins.js +46 -2
  147. package/config/version.js +1 -1
  148. package/core/__test__/extension-manager-impl.test.js +236 -0
  149. package/core/extension-manager-impl.js +23 -6
  150. package/core/plugin-helpers.ts +2 -0
  151. package/core/types-provisioning.ts +4 -1
  152. package/detail/pod.vue +1 -0
  153. package/detail/provisioning.cattle.io.cluster.vue +13 -1
  154. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  155. package/dialog/DeveloperLoadExtensionDialog.vue +12 -3
  156. package/dialog/RollbackWorkloadDialog.vue +2 -5
  157. package/directives/ui-context.ts +103 -0
  158. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  159. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  160. package/edit/auth/__tests__/oidc.test.ts +26 -0
  161. package/edit/auth/github-app-steps.vue +97 -0
  162. package/edit/auth/github-steps.vue +75 -0
  163. package/edit/auth/github.vue +99 -65
  164. package/edit/auth/oidc.vue +5 -1
  165. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  166. package/edit/cloudcredential.vue +1 -1
  167. package/edit/configmap.vue +1 -0
  168. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  169. package/edit/fleet.cattle.io.gitrepo.vue +0 -10
  170. package/edit/fleet.cattle.io.helmop.vue +51 -2
  171. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  172. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  173. package/edit/logging-flow/index.vue +1 -0
  174. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  175. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  176. package/edit/management.cattle.io.project.vue +1 -0
  177. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  178. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  179. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  180. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  181. package/edit/monitoring.coreos.com.route.vue +1 -1
  182. package/edit/namespace.vue +1 -0
  183. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  184. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  185. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  186. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  187. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  188. package/edit/node.vue +1 -0
  189. package/edit/persistentvolume/index.vue +27 -22
  190. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  191. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  192. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  193. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  194. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  195. package/edit/persistentvolume/plugins/csi.vue +18 -16
  196. package/edit/persistentvolume/plugins/fc.vue +13 -14
  197. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  198. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  199. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  200. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  201. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  202. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  203. package/edit/persistentvolume/plugins/local.vue +1 -3
  204. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  205. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  206. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  207. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  208. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  209. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  210. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  211. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  212. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  213. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
  214. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
  215. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
  216. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  217. package/edit/provisioning.cattle.io.cluster/index.vue +25 -15
  218. package/edit/provisioning.cattle.io.cluster/rke2.vue +98 -17
  219. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  220. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
  221. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
  222. package/edit/secret/index.vue +1 -1
  223. package/edit/service.vue +9 -4
  224. package/edit/serviceaccount.vue +1 -0
  225. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  226. package/edit/workload/index.vue +2 -1
  227. package/edit/workload/mixins/workload.js +1 -1
  228. package/initialize/App.vue +4 -4
  229. package/initialize/install-directives.js +2 -0
  230. package/initialize/install-plugins.js +19 -2
  231. package/list/projectsecret.vue +1 -1
  232. package/list/provisioning.cattle.io.cluster.vue +15 -2
  233. package/machine-config/amazonec2.vue +42 -135
  234. package/machine-config/azure.vue +1 -1
  235. package/machine-config/components/EC2Networking.vue +490 -0
  236. package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
  237. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
  238. package/machine-config/digitalocean.vue +11 -0
  239. package/machine-config/google.vue +1 -1
  240. package/mixins/__tests__/brand.spec.ts +2 -2
  241. package/mixins/__tests__/chart.test.ts +21 -0
  242. package/mixins/brand.js +1 -7
  243. package/mixins/chart.js +8 -2
  244. package/mixins/create-edit-view/index.js +5 -0
  245. package/models/__tests__/chart.test.ts +49 -12
  246. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  247. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
  248. package/models/catalog.cattle.io.app.js +1 -1
  249. package/models/chart.js +28 -14
  250. package/models/cluster/node.js +13 -6
  251. package/models/cluster.x-k8s.io.machine.js +10 -20
  252. package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
  253. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  254. package/models/management.cattle.io.authconfig.js +1 -0
  255. package/models/management.cattle.io.cluster.js +21 -3
  256. package/models/management.cattle.io.kontainerdriver.js +1 -0
  257. package/models/provisioning.cattle.io.cluster.js +249 -33
  258. package/package.json +6 -5
  259. package/pages/auth/login.vue +43 -4
  260. package/pages/auth/verify.vue +1 -1
  261. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  262. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  263. package/pages/c/_cluster/apps/charts/chart.vue +35 -17
  264. package/pages/c/_cluster/apps/charts/index.vue +11 -13
  265. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  266. package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
  267. package/pages/c/_cluster/explorer/index.vue +8 -6
  268. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  269. package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
  270. package/pages/c/_cluster/settings/brand.vue +1 -1
  271. package/pages/c/_cluster/settings/performance.vue +12 -25
  272. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  273. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  274. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  275. package/pages/home.vue +327 -16
  276. package/pkg/dynamic-importer.lib.js +4 -0
  277. package/plugins/axios.js +2 -1
  278. package/plugins/dashboard-client-init.js +3 -0
  279. package/plugins/dashboard-store/actions.js +1 -1
  280. package/plugins/dashboard-store/getters.js +18 -1
  281. package/plugins/dashboard-store/resource-class.js +21 -6
  282. package/plugins/dynamic-content.js +13 -0
  283. package/plugins/i18n.js +8 -0
  284. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
  285. package/plugins/steve/steve-pagination-utils.ts +41 -22
  286. package/plugins/steve/subscribe.js +17 -9
  287. package/plugins/subscribe-events.ts +4 -2
  288. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  289. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
  290. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  291. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  292. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  293. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  294. package/rancher-components/Pill/types.ts +0 -1
  295. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
  296. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  297. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  298. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  299. package/rancher-components/RcIcon/index.ts +1 -0
  300. package/rancher-components/RcIcon/types.ts +160 -0
  301. package/rancher-components/utils/status.test.ts +67 -0
  302. package/rancher-components/utils/status.ts +77 -0
  303. package/scripts/extension/publish +1 -1
  304. package/scripts/typegen.sh +1 -0
  305. package/store/action-menu.js +8 -0
  306. package/store/auth.js +11 -6
  307. package/store/aws.js +8 -6
  308. package/store/catalog.js +6 -0
  309. package/store/features.js +2 -0
  310. package/store/index.js +45 -20
  311. package/store/notifications.ts +51 -4
  312. package/store/plugins.js +7 -3
  313. package/store/prefs.js +12 -6
  314. package/store/type-map.js +3 -3
  315. package/store/ui-context.ts +86 -0
  316. package/store/wm.ts +244 -0
  317. package/types/kube/kube-api.ts +2 -1
  318. package/types/notifications/index.ts +27 -3
  319. package/types/rancher/index.d.ts +1 -0
  320. package/types/resources/settings.d.ts +29 -7
  321. package/types/shell/index.d.ts +138 -4
  322. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  323. package/types/store/pagination.types.ts +157 -9
  324. package/types/store/subscribe-events.types.ts +8 -1
  325. package/types/store/subscribe.types.ts +1 -0
  326. package/types/window-manager.ts +24 -0
  327. package/utils/__tests__/cluster.test.ts +379 -1
  328. package/utils/__tests__/object.test.ts +19 -0
  329. package/utils/__tests__/provider.test.ts +98 -0
  330. package/utils/__tests__/selector-typed.test.ts +263 -0
  331. package/utils/__tests__/version.test.ts +19 -1
  332. package/utils/autoscaler-utils.ts +7 -0
  333. package/utils/back-off.ts +3 -3
  334. package/utils/brand.ts +29 -0
  335. package/utils/chart.js +18 -0
  336. package/utils/cluster.js +157 -3
  337. package/utils/color.js +1 -1
  338. package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
  339. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  340. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  341. package/utils/dynamic-content/__tests__/info.test.ts +275 -0
  342. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  343. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  344. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  345. package/utils/dynamic-content/announcement.ts +142 -0
  346. package/utils/dynamic-content/config.ts +55 -0
  347. package/utils/dynamic-content/example.json +40 -0
  348. package/utils/dynamic-content/index.ts +277 -0
  349. package/utils/dynamic-content/info.ts +261 -0
  350. package/utils/dynamic-content/new-release.ts +126 -0
  351. package/utils/dynamic-content/notification-handler.ts +48 -0
  352. package/utils/dynamic-content/support-notice.ts +169 -0
  353. package/utils/dynamic-content/types.d.ts +153 -0
  354. package/utils/dynamic-content/util.ts +122 -0
  355. package/utils/dynamic-importer.js +2 -2
  356. package/utils/favicon.js +4 -4
  357. package/utils/inactivity.ts +104 -0
  358. package/utils/object.js +20 -2
  359. package/utils/pagination-utils.ts +19 -4
  360. package/utils/pagination-wrapper.ts +12 -8
  361. package/utils/provider.ts +14 -0
  362. package/utils/release-notes.ts +1 -1
  363. package/utils/scroll.js +7 -0
  364. package/utils/selector-typed.ts +6 -2
  365. package/utils/settings.ts +15 -0
  366. package/utils/validators/machine-pool.ts +13 -3
  367. package/utils/version.js +15 -0
  368. package/assets/images/icons/document.svg +0 -3
  369. package/plugins/nuxt-client-init.js +0 -3
  370. package/store/wm.js +0 -95
  371. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  372. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  373. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  374. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  375. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
@@ -0,0 +1,390 @@
1
+ import { fetchAndProcessDynamicContent, fetchDynamicContent, UPDATE_DATE_FORMAT } from '../index';
2
+ import * as config from '../config';
3
+ import * as util from '../util';
4
+ import * as newRelease from '../new-release';
5
+ import * as supportNotice from '../support-notice';
6
+ import * as info from '../info';
7
+ import * as typeMap from '@shell/store/type-map';
8
+ import * as version from '@shell/config/version';
9
+ import * as jsyaml from 'js-yaml';
10
+ import dayjs from 'dayjs';
11
+ import { Context } from '../types';
12
+
13
+ // Mock dependencies
14
+ jest.mock('../config');
15
+ jest.mock('../util');
16
+ jest.mock('../new-release');
17
+ jest.mock('../support-notice');
18
+ jest.mock('../info');
19
+ jest.mock('@shell/store/type-map');
20
+ jest.mock('@shell/config/version');
21
+ jest.mock('js-yaml');
22
+
23
+ // Mock dayjs to control time
24
+ const mockDayInstance = {
25
+ format: jest.fn(() => '2023-01-01'),
26
+ add: jest.fn((amount, unit) => {
27
+ return dayjs('2023-01-01').add(amount, unit);
28
+ }),
29
+ diff: jest.fn(() => 100), // a value > 30 for concurrent check
30
+ toString: jest.fn(() => new Date('2023-01-01T12:00:00Z').toString()),
31
+ isAfter: jest.fn(),
32
+ isValid: jest.fn(() => true),
33
+ };
34
+
35
+ const mockGetConfig = config.getConfig as jest.Mock;
36
+ const mockCreateLogger = util.createLogger as jest.Mock;
37
+ const mockProcessReleaseVersion = newRelease.processReleaseVersion as jest.Mock;
38
+ const mockProcessSupportNotices = supportNotice.processSupportNotices as jest.Mock;
39
+ const mockIsAdminUser = typeMap.isAdminUser as jest.Mock;
40
+ const mockGetVersionData = version.getVersionData as jest.Mock;
41
+ const mockYamlLoad = jsyaml.load as jest.Mock;
42
+ const mockSystemInfoProvider = info.SystemInfoProvider as jest.Mock;
43
+
44
+ describe('dynamic content', () => {
45
+ let mockDispatch: jest.Mock;
46
+ let mockGetters: any;
47
+ let mockAxios: jest.Mock;
48
+ let mockLogger: any;
49
+ let mockConfig: any;
50
+ let localStorageMock: any;
51
+ let consoleLogSpy: jest.SpyInstance;
52
+
53
+ beforeEach(() => {
54
+ mockDispatch = jest.fn();
55
+ mockGetters = { isSingleProduct: false };
56
+ mockAxios = jest.fn();
57
+ mockLogger = {
58
+ debug: jest.fn(),
59
+ info: jest.fn(),
60
+ error: jest.fn()
61
+ };
62
+ mockConfig = {
63
+ enabled: true,
64
+ debug: false,
65
+ log: false,
66
+ prime: false,
67
+ endpoint: '$dist'
68
+ };
69
+
70
+ mockGetConfig.mockReturnValue(mockConfig);
71
+ mockCreateLogger.mockReturnValue(mockLogger);
72
+ mockIsAdminUser.mockReturnValue(true);
73
+ mockGetVersionData.mockReturnValue({ Version: '2.9.0', RancherPrime: 'false' });
74
+ mockSystemInfoProvider.mockImplementation(() => ({ buildQueryString: () => 'qs=1' }));
75
+
76
+ // Mock localStorage
77
+ let store: { [key: string]: string } = {};
78
+
79
+ localStorageMock = {
80
+ getItem: (key: string) => store[key] || null,
81
+ setItem: (key: string, value: string) => {
82
+ store[key] = value.toString();
83
+ },
84
+ removeItem: (key: string) => {
85
+ delete store[key];
86
+ },
87
+ clear: () => {
88
+ store = {};
89
+ }
90
+ };
91
+ Object.defineProperty(window, 'localStorage', { value: localStorageMock });
92
+
93
+ jest.useFakeTimers();
94
+
95
+ // Mock console
96
+ consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
97
+ });
98
+
99
+ afterEach(() => {
100
+ jest.clearAllMocks();
101
+ jest.useRealTimers();
102
+ localStorageMock.clear();
103
+ });
104
+
105
+ describe('fetchAndProcessDynamicContent', () => {
106
+ it('should exit early if in single product mode', async() => {
107
+ mockGetters.isSingleProduct = true;
108
+
109
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
110
+
111
+ jest.runAllTimers();
112
+
113
+ await ret;
114
+ expect(mockGetConfig).not.toHaveBeenCalled();
115
+ });
116
+
117
+ it('should exit early if dynamic content is disabled', async() => {
118
+ mockConfig.enabled = false;
119
+ await fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
120
+
121
+ expect(mockLogger.debug).not.toHaveBeenCalled();
122
+ expect(consoleLogSpy).toHaveBeenCalledWith('Dynamic content disabled through configuration'); // eslint-disable-line no-console
123
+ });
124
+
125
+ it('should exit early if version is invalid', async() => {
126
+ mockGetVersionData.mockReturnValue({ Version: 'invalid' });
127
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
128
+
129
+ jest.runAllTimers();
130
+
131
+ await ret;
132
+ expect(mockProcessReleaseVersion).not.toHaveBeenCalled();
133
+ });
134
+
135
+ it('should process content for community users', async() => {
136
+ const content = { releases: [], support: {} };
137
+
138
+ mockAxios.mockResolvedValue({ data: 'yaml: data' });
139
+ mockYamlLoad.mockReturnValue(content);
140
+
141
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
142
+
143
+ jest.runAllTimers();
144
+ await ret;
145
+
146
+ expect(mockProcessReleaseVersion).toHaveBeenCalledTimes(1);
147
+ expect(mockProcessSupportNotices).toHaveBeenCalledTimes(1);
148
+ });
149
+
150
+ it('should process content for prime admin users', async() => {
151
+ mockConfig.prime = true;
152
+ mockIsAdminUser.mockReturnValue(true);
153
+ const content = { releases: [], support: {} };
154
+
155
+ mockAxios.mockResolvedValue({ data: 'yaml: data' });
156
+ mockYamlLoad.mockReturnValue(content);
157
+
158
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
159
+
160
+ jest.runAllTimers();
161
+ await ret;
162
+
163
+ expect(mockProcessReleaseVersion).toHaveBeenCalledTimes(1);
164
+ expect(mockProcessSupportNotices).toHaveBeenCalledTimes(1);
165
+ });
166
+
167
+ it('should NOT process content for prime non-admin users', async() => {
168
+ mockConfig.prime = true;
169
+ mockIsAdminUser.mockReturnValue(false);
170
+ const content = { releases: [], support: {} };
171
+
172
+ mockAxios.mockResolvedValue({ data: 'yaml: data' });
173
+ mockYamlLoad.mockReturnValue(content);
174
+
175
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
176
+
177
+ jest.runAllTimers();
178
+ await ret;
179
+
180
+ expect(mockProcessReleaseVersion).not.toHaveBeenCalled();
181
+ expect(mockProcessSupportNotices).not.toHaveBeenCalled();
182
+ });
183
+
184
+ it('should override version if debugVersion is set', async() => {
185
+ const content = { settings: { debugVersion: '2.10.0' } };
186
+
187
+ mockAxios.mockResolvedValue({ data: content });
188
+ mockYamlLoad.mockReturnValue(content);
189
+
190
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
191
+
192
+ jest.runAllTimers();
193
+ await ret;
194
+
195
+ expect(mockLogger.debug).toHaveBeenCalledWith('Read configuration', {
196
+ debug: false, enabled: true, endpoint: '$dist', log: false, prime: false
197
+ });
198
+ expect(mockLogger.debug).toHaveBeenCalledWith('Overriding version number to 2.10.0');
199
+ expect(mockProcessReleaseVersion).toHaveBeenCalledWith(
200
+ expect.any(Object),
201
+ undefined,
202
+ expect.objectContaining({ version: expect.objectContaining({ version: '2.10.0' }) })
203
+ );
204
+ });
205
+
206
+ it('should clear logs if logging is disabled', async() => {
207
+ mockConfig.log = false;
208
+ localStorageMock.setItem('rancher-updates-debug-log', '[]');
209
+ mockAxios.mockResolvedValue({ data: '' });
210
+
211
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
212
+
213
+ jest.runAllTimers();
214
+ await ret;
215
+
216
+ expect(localStorageMock.getItem('rancher-updates-debug-log')).toBeNull();
217
+ });
218
+
219
+ it('should handle errors during processing', async() => {
220
+ const error = new Error('Processing failed');
221
+
222
+ mockProcessReleaseVersion.mockImplementation(() => {
223
+ throw error;
224
+ });
225
+ mockAxios.mockResolvedValue({ data: '' });
226
+
227
+ const ret = fetchAndProcessDynamicContent(mockDispatch, mockGetters, mockAxios);
228
+
229
+ jest.runAllTimers();
230
+ await ret;
231
+
232
+ expect(mockLogger.error).toHaveBeenCalledWith('Error reading or processing dynamic content', error);
233
+ });
234
+ });
235
+
236
+ describe('fetchDynamicContent', () => {
237
+ let context: Context;
238
+
239
+ beforeEach(() => {
240
+ context = {
241
+ dispatch: mockDispatch,
242
+ getters: mockGetters,
243
+ axios: mockAxios,
244
+ logger: mockLogger,
245
+ config: mockConfig,
246
+ isAdmin: true,
247
+ settings: { releaseNotesUrl: '', suseExtensions: [] },
248
+ };
249
+ });
250
+
251
+ it('should fetch and parse content when needed', async() => {
252
+ const content = { version: '1.0' };
253
+
254
+ mockAxios.mockResolvedValue({ data: 'yaml: data' });
255
+ mockYamlLoad.mockReturnValue(content);
256
+
257
+ const fetchPromise = fetchDynamicContent(context);
258
+
259
+ jest.runAllTimers();
260
+ const result = await fetchPromise;
261
+
262
+ expect(mockAxios).toHaveBeenCalledTimes(1);
263
+ expect(mockYamlLoad).toHaveBeenCalledWith('yaml: data');
264
+ expect(result).toStrictEqual(content);
265
+
266
+ const tomorrow = dayjs().add(1, 'day').format(UPDATE_DATE_FORMAT);
267
+
268
+ expect(localStorageMock.getItem('rancher-updates-last-content')).toBe(JSON.stringify(content));
269
+ // Check updateFetchInfo(false) was called
270
+ expect(localStorageMock.getItem('rancher-updates-fetch-next')).toBe(tomorrow);
271
+ expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBeNull();
272
+ });
273
+
274
+ it('should skip fetch if not due', async() => {
275
+ const today = dayjs().format(UPDATE_DATE_FORMAT);
276
+ const tomorrow = dayjs().add(1, 'day').format(UPDATE_DATE_FORMAT);
277
+
278
+ localStorageMock.setItem('rancher-updates-fetch-next', tomorrow);
279
+ const fetchPromise = fetchDynamicContent(context);
280
+
281
+ jest.runAllTimers();
282
+ await fetchPromise;
283
+
284
+ expect(mockAxios).not.toHaveBeenCalled();
285
+ expect(mockLogger.info).toHaveBeenCalledWith(`Skipping update check for dynamic content - next check due on ${ tomorrow } (today is ${ today })`);
286
+ });
287
+
288
+ it('should skip fetch if another is in progress', async() => {
289
+ const today = dayjs().format(UPDATE_DATE_FORMAT);
290
+ const later = dayjs().subtract(10, 'second').toString();
291
+
292
+ localStorageMock.setItem('rancher-updates-fetch-next', today);
293
+ localStorageMock.setItem('rancher-updates-fetching', later);
294
+
295
+ const fetchPromise = fetchDynamicContent(context);
296
+
297
+ jest.runAllTimers();
298
+ await fetchPromise;
299
+
300
+ expect(mockAxios).not.toHaveBeenCalled();
301
+ expect(mockLogger.debug).toHaveBeenCalledWith('Already fetching dynamic content in another tab (or previous tab closed while fetching) - skipping');
302
+ });
303
+
304
+ it('should proceed with fetch if a stale fetch was in progress', async() => {
305
+ (mockDayInstance.diff as jest.Mock).mockReturnValue(100); // more than 30s
306
+ localStorageMock.setItem('rancher-updates-fetching', new Date('2023-01-01T11:00:00Z').toString());
307
+ mockAxios.mockResolvedValue({ data: '' });
308
+
309
+ const fetchPromise = fetchDynamicContent(context);
310
+
311
+ jest.runAllTimers();
312
+ await fetchPromise;
313
+
314
+ expect(mockAxios).toHaveBeenCalledTimes(1);
315
+ });
316
+
317
+ it('should handle axios fetch error and backoff', async() => {
318
+ const error = new Error('Network Error');
319
+
320
+ mockAxios.mockRejectedValue(error);
321
+
322
+ const fetchPromise = fetchDynamicContent(context);
323
+
324
+ jest.runAllTimers();
325
+ await fetchPromise;
326
+
327
+ const tomorrow = dayjs().add(1, 'day').format(UPDATE_DATE_FORMAT);
328
+
329
+ expect(mockLogger.error).toHaveBeenCalledWith('Error occurred reading dynamic content', error);
330
+ // Check updateFetchInfo(true) was called
331
+ expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBe('1');
332
+ expect(localStorageMock.getItem('rancher-updates-fetch-next')).toBe(tomorrow); // 1 day backoff
333
+ });
334
+
335
+ it('should handle YAML parsing error', async() => {
336
+ const error = new Error('YAML Error');
337
+
338
+ mockAxios.mockResolvedValue({ data: 'invalid yaml' });
339
+ mockYamlLoad.mockImplementation(() => {
340
+ throw error;
341
+ });
342
+
343
+ const fetchPromise = fetchDynamicContent(context);
344
+
345
+ jest.runAllTimers();
346
+ await fetchPromise;
347
+
348
+ expect(mockLogger.error).toHaveBeenCalledWith('Failed to parse YAML/JSON from dynamic content package', error);
349
+ });
350
+
351
+ it('should handle axios fetch error with unexpected data', async() => {
352
+ mockAxios.mockResolvedValue({ data: null });
353
+
354
+ const fetchPromise = fetchDynamicContent(context);
355
+
356
+ jest.runAllTimers();
357
+ await fetchPromise;
358
+
359
+ expect(mockLogger.error).toHaveBeenCalledWith('Error fetching dynamic content package (unexpected data)');
360
+ });
361
+
362
+ it('should increment backoff on multiple consecutive errors', async() => {
363
+ const error = new Error('Network Error');
364
+
365
+ mockAxios.mockRejectedValue(error);
366
+ localStorageMock.setItem('rancher-updates-fetch-errors', '2'); // 2 previous errors
367
+
368
+ const fetchPromise = fetchDynamicContent(context);
369
+
370
+ jest.runAllTimers();
371
+ await fetchPromise;
372
+
373
+ expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBe('3');
374
+ });
375
+
376
+ it('should cap backoff at max value', async() => {
377
+ const error = new Error('Network Error');
378
+
379
+ mockAxios.mockRejectedValue(error);
380
+ localStorageMock.setItem('rancher-updates-fetch-errors', '10');
381
+
382
+ const fetchPromise = fetchDynamicContent(context);
383
+
384
+ jest.runAllTimers();
385
+ await fetchPromise;
386
+
387
+ expect(localStorageMock.getItem('rancher-updates-fetch-errors')).toBe('6');
388
+ });
389
+ });
390
+ });
@@ -0,0 +1,275 @@
1
+ import { SystemInfoProvider } from '../info';
2
+ import { MANAGEMENT, COUNT } from '@shell/config/types';
3
+ import { SETTING } from '@shell/config/settings';
4
+ import * as version from '@shell/config/version';
5
+ import { sha256 } from '@shell/utils/crypto';
6
+
7
+ // Mock dependencies from @shell
8
+ jest.mock('@shell/config/version', () => ({ getVersionData: jest.fn(), isRancherPrime: jest.fn() }));
9
+
10
+ jest.mock('@shell/utils/crypto', () => ({ sha256: jest.fn((val: string) => `hashed_${ val }`) }));
11
+
12
+ describe('systemInfoProvider', () => {
13
+ let mockGetters: any;
14
+ let mockSettings: any[];
15
+ let mockClusters: any[];
16
+ let mockCounts: any;
17
+ let mockPlugins: any[];
18
+ let originalWindowLocation: Location;
19
+
20
+ beforeAll(() => {
21
+ originalWindowLocation = window.location;
22
+ // Mock window.location
23
+ delete (window as any).location;
24
+ (window as any).location = { host: 'fallback.host' };
25
+ });
26
+
27
+ afterAll(() => {
28
+ (window as any).location = originalWindowLocation;
29
+ });
30
+
31
+ beforeEach(() => {
32
+ // Reset mocks
33
+ (version.getVersionData as jest.Mock).mockClear();
34
+ (version.isRancherPrime as jest.Mock).mockClear();
35
+ (sha256 as jest.Mock).mockClear();
36
+
37
+ // Mock window properties
38
+ Object.defineProperty(window, 'screen', {
39
+ value: { width: 1920, height: 1080 },
40
+ writable: true
41
+ });
42
+ Object.defineProperty(window, 'innerWidth', { value: 1024, writable: true });
43
+ Object.defineProperty(window, 'innerHeight', { value: 768, writable: true });
44
+ Object.defineProperty(window.navigator, 'language', { value: 'en-US', writable: true });
45
+
46
+ mockSettings = [
47
+ { id: SETTING.SERVER_URL, value: 'https://rancher.test' },
48
+ { id: 'install-uuid', value: 'test-uuid' },
49
+ ];
50
+
51
+ mockClusters = [
52
+ {
53
+ id: 'local',
54
+ isLocal: true,
55
+ kubernetesVersionBase: '1.25',
56
+ provisioner: 'k3s',
57
+ status: { nodeCount: 3 },
58
+ },
59
+ {
60
+ id: 'c-12345',
61
+ isLocal: false,
62
+ }
63
+ ];
64
+
65
+ mockCounts = [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 2 } } } }];
66
+
67
+ mockPlugins = [
68
+ { name: 'harvester', builtin: false },
69
+ { name: 'some-custom-ext', builtin: false },
70
+ { name: 'core', builtin: true },
71
+ ];
72
+
73
+ mockGetters = {
74
+ 'management/typeRegistered': jest.fn().mockReturnValue(true),
75
+ 'management/all': jest.fn((type: string) => {
76
+ if (type === MANAGEMENT.SETTING) {
77
+ return mockSettings;
78
+ }
79
+ if (type === MANAGEMENT.CLUSTER) {
80
+ return mockClusters;
81
+ }
82
+ if (type === COUNT) {
83
+ return mockCounts;
84
+ }
85
+
86
+ return [];
87
+ }),
88
+ 'auth/principalId': 'user-123',
89
+ 'uiplugins/plugins': mockPlugins,
90
+ isSingleProduct: false,
91
+ 'management/byId': jest.fn((type: string, id: string) => {
92
+ if (type === MANAGEMENT.SETTING) {
93
+ return mockSettings.find((s) => s.id === id) || null;
94
+ }
95
+
96
+ return undefined;
97
+ }),
98
+ 'management/schemaFor': jest.fn(),
99
+ localCluster: mockClusters.find((c) => c.id === 'local') || null,
100
+ 'features/get': jest.fn(() => 'abc'),
101
+ };
102
+
103
+ (version.getVersionData as jest.Mock).mockReturnValue({
104
+ Version: '2.8.0-rc1',
105
+ RancherPrime: 'false',
106
+ });
107
+
108
+ (version.isRancherPrime as jest.Mock).mockReturnValue(false);
109
+ });
110
+
111
+ it('should build a complete query string with all available data', () => {
112
+ const infoProvider = new SystemInfoProvider(mockGetters, {});
113
+ const qs = infoProvider.buildQueryString();
114
+
115
+ expect(qs).toContain('dcv=v1');
116
+ expect(qs).toContain('s=hashed_https://rancher.test');
117
+ expect(qs).toContain('u=hashed_user-123');
118
+ expect(qs).toContain('uuid=test-uuid');
119
+ expect(qs).toContain('v=2.8.0');
120
+ expect(qs).toContain('dev=true');
121
+ expect(qs).toContain('p=false');
122
+ // Add back when LTS is added
123
+ // expect(qs).toContain('lts=false');
124
+ expect(qs).toContain('cc=2');
125
+ expect(qs).toContain('lkv=1.25');
126
+ expect(qs).toContain('lcp=k3s');
127
+ expect(qs).toContain('lnc=3');
128
+ expect(qs).toContain('xkn=harvester');
129
+ expect(qs).toContain('xcc=1');
130
+ expect(qs).toContain('bl=en-US');
131
+ expect(qs).toContain('bs=1024x768');
132
+ expect(qs).toContain('ss=1920x1080');
133
+ expect(qs).toContain('ff-usc=abc');
134
+ });
135
+
136
+ it('should handle missing or partial data gracefully', () => {
137
+ // Override mocks for this test
138
+ (version.getVersionData as jest.Mock).mockReturnValue({
139
+ Version: '2.9.0', // Not a dev version
140
+ RancherPrime: 'true',
141
+ });
142
+ (version.isRancherPrime as jest.Mock).mockReturnValue(true);
143
+
144
+ mockGetters['management/all'].mockImplementation((type: string) => {
145
+ if (type === MANAGEMENT.SETTING) {
146
+ return [{ id: 'install-uuid', value: 'only-uuid' }]; // No server-url
147
+ }
148
+ if (type === COUNT) {
149
+ return [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 27 } } } }];
150
+ }
151
+ if (type === MANAGEMENT.CLUSTER) {
152
+ return []; // No clusters
153
+ }
154
+
155
+ return [];
156
+ });
157
+ mockGetters['management/byId'].mockImplementation((type: string, id: string) => {
158
+ if (type === MANAGEMENT.SETTING && id === 'install-uuid') {
159
+ return { id: 'install-uuid', value: 'only-uuid' }; // No server-url
160
+ }
161
+ });
162
+
163
+ mockGetters['uiplugins/plugins'] = null; // No plugins
164
+ mockGetters['auth/principalId'] = null; // No user
165
+ mockGetters['localCluster'] = null; // No clusters
166
+ mockGetters['features/get'] = () => {
167
+ throw new Error('unknown feature');
168
+ };
169
+
170
+ const infoProvider = new SystemInfoProvider(mockGetters, {});
171
+ const qs = infoProvider.buildQueryString();
172
+
173
+ expect(qs).toContain('s=hashed_fallback.host');
174
+ expect(qs).toContain('u=hashed_unknown');
175
+ expect(qs).toContain('uuid=only-uuid');
176
+ expect(qs).toContain('v=2.9.0');
177
+ expect(qs).toContain('dev=false');
178
+ expect(qs).toContain('p=true');
179
+ expect(qs).toContain('cc=27');
180
+ expect(qs).not.toContain('lkv=');
181
+ expect(qs).not.toContain('lcp=');
182
+ expect(qs).not.toContain('lnc=');
183
+ expect(qs).not.toContain('xkn=');
184
+ expect(qs).not.toContain('xcc=');
185
+ expect(qs).not.toContain('ff-usc=');
186
+ });
187
+
188
+ it('should handle getAll returning undefined when types are not registered', () => {
189
+ // Override mocks for this test
190
+ (version.getVersionData as jest.Mock).mockReturnValue({
191
+ Version: '2.9.1',
192
+ RancherPrime: 'false',
193
+ });
194
+
195
+ // Simulate that the management types are not registered in the store
196
+ mockGetters['management/typeRegistered'].mockReturnValue(false);
197
+ mockGetters['management/all'].mockImplementation();
198
+ mockGetters['management/byId'].mockImplementation();
199
+
200
+ mockGetters['auth/principalId'] = 'user-456';
201
+ mockGetters['uiplugins/plugins'] = []; // No plugins
202
+ mockGetters['localCluster'] = null; // No clusters
203
+
204
+ const infoProvider = new SystemInfoProvider(mockGetters, {});
205
+ const qs = infoProvider.buildQueryString();
206
+
207
+ expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'server-url');
208
+ expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'install-uuid');
209
+ expect(mockGetters['management/byId']).toHaveBeenCalledWith(MANAGEMENT.SETTING, 'server-version-type');
210
+ expect(mockGetters['management/typeRegistered']).toHaveBeenCalledWith(COUNT);
211
+ expect(mockGetters['management/all']).not.toHaveBeenCalled();
212
+
213
+ // Verify the query string is built with fallback or empty values
214
+ expect(qs).toContain('s=hashed_fallback.host');
215
+ expect(qs).toContain('u=hashed_user-456');
216
+ expect(qs).toContain('v=2.9.1');
217
+ expect(qs).toContain('p=false');
218
+ expect(qs).toContain('xcc=0');
219
+ expect(qs).not.toContain('uuid=');
220
+ expect(qs).not.toContain('lkv=');
221
+ });
222
+
223
+ it('should use UNKNOWN for missing system properties', () => {
224
+ // Override mocks for this test
225
+ (version.getVersionData as jest.Mock).mockReturnValue({
226
+ Version: '2.9.0',
227
+ RancherPrime: 'false',
228
+ });
229
+ (version.isRancherPrime as jest.Mock).mockReturnValue(false);
230
+
231
+ mockGetters['management/byId'].mockImplementation((type: string, id: string) => {
232
+ if (type === MANAGEMENT.SETTING) {
233
+ return { id, value: '' }; // Empty values for all settings
234
+ }
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
+
246
+ mockGetters['management/all'].mockImplementation((type: string) => {
247
+ if (type === MANAGEMENT.SETTING) {
248
+ // Return settings, but with empty values
249
+ return [
250
+ { id: SETTING.SERVER_URL, value: '' },
251
+ { id: 'install-uuid', value: '' },
252
+ ];
253
+ }
254
+ if (type === COUNT) {
255
+ return [{ counts: { [MANAGEMENT.CLUSTER]: { summary: { count: 1 } } } }];
256
+ }
257
+ if (type === MANAGEMENT.CLUSTER) {
258
+ return [localCluster];
259
+ }
260
+
261
+ return [];
262
+ });
263
+
264
+ mockGetters['auth/principalId'] = null; // No user
265
+ mockGetters['localCluster'] = localCluster;
266
+
267
+ const infoProvider = new SystemInfoProvider(mockGetters, {});
268
+ const qs = infoProvider.buildQueryString();
269
+
270
+ expect(qs).toContain('u=hashed_unknown');
271
+ expect(qs).toContain('lkv=unknown');
272
+ expect(qs).toContain('lcp=unknown');
273
+ expect(qs).not.toContain('uuid='); // systemUUID is UNKNOWN, so it's skipped
274
+ });
275
+ });