@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.4

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 (473) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/styles/global/_layout.scss +4 -0
  56. package/assets/translations/en-us.yaml +165 -45
  57. package/assets/translations/zh-hans.yaml +1 -7
  58. package/chart/monitoring/ClusterSelector.vue +0 -21
  59. package/chart/monitoring/index.vue +10 -1
  60. package/chart/monitoring/prometheus/index.vue +6 -3
  61. package/components/ActionDropdownShell.vue +2 -1
  62. package/components/CruResource.vue +161 -14
  63. package/components/CruResourceFooter.vue +9 -5
  64. package/components/ExplorerMembers.vue +8 -4
  65. package/components/ExplorerProjectsNamespaces.vue +11 -7
  66. package/components/GrowlManager.vue +4 -0
  67. package/components/InstallHelmCharts.vue +2 -2
  68. package/components/LandingPagePreference.vue +14 -5
  69. package/components/MgmtNodeList.vue +184 -0
  70. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  71. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  72. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  73. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  74. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +17 -1
  75. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  76. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  77. package/components/Resource/Detail/Metadata/index.vue +6 -0
  78. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  79. package/components/Resource/Detail/SpacedRow.vue +3 -1
  80. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  81. package/components/ResourceDetail/index.vue +1 -1
  82. package/components/ResourceList/Masthead.vue +19 -9
  83. package/components/ResourceList/index.vue +82 -1
  84. package/components/RichTranslation.vue +5 -2
  85. package/components/SelectIconGrid.vue +0 -10
  86. package/components/Setting.vue +1 -0
  87. package/components/SingleClusterInfo.vue +1 -0
  88. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  89. package/components/SortableTable/index.vue +6 -9
  90. package/components/SortableTable/selection.js +23 -5
  91. package/components/SortableTable/sorting.js +6 -3
  92. package/components/SubtleLink.vue +31 -6
  93. package/components/Tabbed/Tab.vue +29 -3
  94. package/components/Tabbed/index.vue +25 -3
  95. package/components/TableOfContents/TableOfContents.vue +109 -0
  96. package/components/TableOfContents/composables.ts +258 -0
  97. package/components/Window/ContainerShell.vue +21 -11
  98. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  99. package/components/Wizard.vue +23 -17
  100. package/components/fleet/AppCoChartGrid.vue +401 -0
  101. package/components/fleet/AppCoEmptyState.vue +127 -0
  102. package/components/fleet/AppCoPageHeader.vue +119 -0
  103. package/components/fleet/AppCoVersionSelect.vue +70 -0
  104. package/components/fleet/FleetBundles.vue +100 -12
  105. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  106. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  107. package/components/fleet/FleetClusterTargets/index.vue +226 -161
  108. package/components/fleet/FleetIntro.vue +7 -3
  109. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  110. package/components/fleet/FleetSecretSelector.vue +5 -3
  111. package/components/fleet/FleetValuesFrom.vue +8 -2
  112. package/components/fleet/GitRepoTargetTab.vue +0 -2
  113. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  114. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  115. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  116. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  117. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  118. package/components/fleet/HelmOpTargetTab.vue +64 -60
  119. package/components/fleet/HelmOpValuesTab.vue +129 -105
  120. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  121. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  122. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  123. package/components/fleet/__tests__/FleetClusterTargets.test.ts +402 -115
  124. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  125. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  126. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  127. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  128. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  129. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  130. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  131. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  132. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  133. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  134. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  135. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  136. package/components/fleet/dashboard/Empty.vue +8 -4
  137. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  138. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  139. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  140. package/components/form/ArrayList.vue +61 -4
  141. package/components/form/KeyValue.vue +23 -2
  142. package/components/form/LabeledSelect.vue +59 -4
  143. package/components/form/Labels.vue +22 -3
  144. package/components/form/NameNsDescription.vue +24 -5
  145. package/components/form/ResourceTabs/index.vue +1 -0
  146. package/components/form/Security.vue +6 -2
  147. package/components/form/WorkloadPorts.vue +2 -7
  148. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  149. package/components/form/__tests__/Security.test.ts +76 -0
  150. package/components/formatter/Autoscaler.vue +4 -4
  151. package/components/formatter/ClusterKubeVersion.vue +27 -0
  152. package/components/formatter/ClusterLink.vue +1 -7
  153. package/components/formatter/ClusterProvider.vue +6 -10
  154. package/components/formatter/FleetSummaryGraph.vue +0 -3
  155. package/components/formatter/InternalExternalIP.vue +10 -4
  156. package/components/formatter/MachineSummaryGraph.vue +1 -1
  157. package/components/formatter/PodsUsage.vue +2 -2
  158. package/components/formatter/ServiceTargets.vue +26 -7
  159. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  160. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  161. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  162. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  163. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  164. package/components/nav/Header.vue +4 -0
  165. package/components/nav/NamespaceFilter.vue +2 -2
  166. package/components/nav/TopLevelMenu.helper.ts +15 -3
  167. package/components/nav/TopLevelMenu.vue +22 -6
  168. package/components/nav/__tests__/Header.test.ts +15 -0
  169. package/components/nav/__tests__/TopLevelMenu.test.ts +263 -21
  170. package/components/templates/default.vue +9 -4
  171. package/components/templates/home.vue +23 -0
  172. package/components/templates/plain.vue +23 -0
  173. package/components/templates/standalone.vue +17 -0
  174. package/composables/useFormValidation.ts +93 -0
  175. package/composables/useHelmOpResources.test.ts +56 -0
  176. package/composables/useHelmOpResources.ts +32 -0
  177. package/composables/useStateColor.test.ts +325 -0
  178. package/composables/useStateColor.ts +128 -0
  179. package/composables/useVeeValidateField.test.ts +159 -0
  180. package/composables/useVeeValidateField.ts +67 -0
  181. package/config/home-links.js +1 -1
  182. package/config/labels-annotations.js +1 -0
  183. package/config/pagination-table-headers.js +18 -1
  184. package/config/product/explorer.js +17 -4
  185. package/config/product/manager.js +84 -21
  186. package/config/router/index.js +16 -0
  187. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  188. package/config/router/navigation-guards/authentication.js +10 -4
  189. package/config/router/routes.js +26 -6
  190. package/config/settings.ts +0 -2
  191. package/config/table-headers.js +23 -5
  192. package/config/types.js +11 -1
  193. package/core/__tests__/plugin-products.test.ts +904 -20
  194. package/core/plugin-products-base.ts +110 -10
  195. package/core/plugin-products.ts +4 -0
  196. package/core/plugin-types.ts +194 -31
  197. package/core/plugin.ts +18 -7
  198. package/core/productDebugger.js +9 -4
  199. package/core/types-provisioning.ts +77 -31
  200. package/core/types.ts +72 -22
  201. package/detail/__tests__/pod.test.ts +41 -0
  202. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  203. package/detail/__tests__/workload.test.ts +3 -152
  204. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  205. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  206. package/detail/pod.vue +1 -1
  207. package/detail/provisioning.cattle.io.cluster.vue +34 -14
  208. package/detail/workload/index.vue +12 -55
  209. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  210. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  211. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  212. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  213. package/edit/auth/__tests__/azuread.test.ts +247 -39
  214. package/edit/auth/__tests__/github.test.ts +234 -0
  215. package/edit/auth/__tests__/oidc.test.ts +26 -6
  216. package/edit/auth/__tests__/saml.test.ts +196 -0
  217. package/edit/auth/azuread.vue +197 -56
  218. package/edit/auth/github.vue +72 -13
  219. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  220. package/edit/auth/ldap/config.vue +8 -0
  221. package/edit/auth/ldap/index.vue +75 -1
  222. package/edit/auth/oidc.vue +119 -73
  223. package/edit/auth/saml.vue +76 -12
  224. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  225. package/edit/fleet.cattle.io.helmop.vue +491 -136
  226. package/edit/management.cattle.io.user.vue +5 -2
  227. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  228. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  229. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  230. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  231. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  232. package/edit/provisioning.cattle.io.cluster/rke2.vue +92 -14
  233. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +22 -0
  234. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  235. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  236. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  237. package/edit/secret/__tests__/ssh.test.ts +5 -6
  238. package/edit/secret/basic.vue +31 -0
  239. package/edit/secret/index.vue +68 -17
  240. package/edit/secret/registry.vue +38 -0
  241. package/edit/secret/ssh.vue +29 -0
  242. package/edit/secret/tls.vue +30 -0
  243. package/edit/service.vue +4 -4
  244. package/edit/workload/Upgrading.vue +3 -3
  245. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  246. package/edit/workload/mixins/workload.js +2 -1
  247. package/list/fleet.cattle.io.bundle.vue +7 -104
  248. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  249. package/list/group.principal.vue +5 -4
  250. package/list/harvesterhci.io.management.cluster.vue +8 -9
  251. package/list/management.cattle.io.user.vue +12 -9
  252. package/list/provisioning.cattle.io.cluster.vue +268 -180
  253. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  254. package/mixins/__tests__/auth-config.test.ts +90 -0
  255. package/mixins/__tests__/chart.test.ts +206 -0
  256. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  257. package/mixins/auth-config.js +7 -0
  258. package/mixins/brand.js +2 -1
  259. package/mixins/chart.js +22 -9
  260. package/mixins/child-hook.js +12 -6
  261. package/mixins/create-edit-view/impl.js +5 -3
  262. package/mixins/resource-fetch-api-pagination.js +62 -6
  263. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  264. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  265. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  266. package/models/__tests__/fleet-application.test.ts +175 -0
  267. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  268. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  269. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  270. package/models/__tests__/management.cattle.io.node.ts +28 -5
  271. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  272. package/models/__tests__/namespace.test.ts +36 -0
  273. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +81 -11
  274. package/models/__tests__/workload.test.ts +401 -26
  275. package/models/base-cluster.x-k8s.io.js +26 -0
  276. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  277. package/models/cluster.js +1 -1
  278. package/models/cluster.x-k8s.io.machine.js +4 -22
  279. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  280. package/models/cluster.x-k8s.io.machineset.js +2 -20
  281. package/models/compliance.cattle.io.clusterscan.js +165 -2
  282. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  283. package/models/fleet-application.js +7 -1
  284. package/models/fleet.cattle.io.helmop.js +20 -1
  285. package/models/management.cattle.io.cluster.js +434 -41
  286. package/models/management.cattle.io.node.js +50 -7
  287. package/models/management.cattle.io.nodepool.js +1 -1
  288. package/models/namespace.js +1 -1
  289. package/models/networking.k8s.io.ingress.js +12 -4
  290. package/models/pod.js +33 -1
  291. package/models/provisioning.cattle.io.cluster.js +51 -334
  292. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  293. package/models/workload.js +108 -13
  294. package/models/workload.service.js +5 -0
  295. package/package.json +22 -39
  296. package/pages/__tests__/readme.test.ts +49 -0
  297. package/pages/about.vue +5 -6
  298. package/pages/auth/login.vue +0 -35
  299. package/pages/auth/setup.vue +13 -3
  300. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  301. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  302. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  303. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  304. package/pages/c/_cluster/apps/charts/chart.vue +62 -9
  305. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  306. package/pages/c/_cluster/apps/charts/install.vue +122 -113
  307. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  308. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  309. package/pages/c/_cluster/explorer/index.vue +5 -49
  310. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  311. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  312. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  313. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  314. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  315. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  316. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  317. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  318. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  319. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  320. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  321. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  322. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  323. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  324. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  325. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  326. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  327. package/pages/c/_cluster/fleet/index.vue +2 -2
  328. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  329. package/pages/c/_cluster/istio/index.vue +21 -6
  330. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  331. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +815 -2
  332. package/pages/c/_cluster/uiplugins/index.vue +218 -197
  333. package/pages/diagnostic.vue +13 -17
  334. package/pages/fail-whale.vue +30 -7
  335. package/pages/home.vue +93 -306
  336. package/pages/readme.vue +88 -0
  337. package/plugins/clean-html.d.ts +9 -0
  338. package/plugins/dashboard-store/__tests__/resource-class.test.ts +181 -0
  339. package/plugins/dashboard-store/actions.js +40 -18
  340. package/plugins/dashboard-store/resource-class.js +67 -9
  341. package/plugins/steve/__tests__/actions.test.ts +212 -0
  342. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  343. package/plugins/steve/actions.js +96 -0
  344. package/plugins/steve/steve-pagination-utils.ts +12 -4
  345. package/plugins/steve/subscribe.js +35 -5
  346. package/rancher-components/Accordion/Accordion.vue +53 -9
  347. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  348. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  349. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  350. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  351. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  352. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  353. package/rancher-components/RcButton/RcButton.test.ts +140 -1
  354. package/rancher-components/RcButton/RcButton.vue +126 -17
  355. package/rancher-components/RcButton/types.ts +3 -0
  356. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  357. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  358. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  359. package/rancher-components/RcSection/RcSection.vue +28 -3
  360. package/scripts/extension/helm/package/Dockerfile +1 -1
  361. package/scripts/test-plugins-build.sh +2 -1
  362. package/store/__tests__/catalog.test.ts +115 -1
  363. package/store/__tests__/notifications.test.ts +434 -0
  364. package/store/__tests__/type-map.test.ts +556 -1
  365. package/store/action-menu.js +8 -3
  366. package/store/auth.js +1 -1
  367. package/store/aws.js +27 -16
  368. package/store/catalog.js +84 -3
  369. package/store/digitalocean.js +20 -38
  370. package/store/index.js +2 -0
  371. package/store/linode.js +25 -40
  372. package/store/plugins.js +7 -4
  373. package/store/pnap.js +1 -0
  374. package/store/type-map.js +111 -29
  375. package/tsconfig.paths.json +8 -8
  376. package/types/components/buttonGroup.ts +5 -0
  377. package/types/kube/kube-api.ts +14 -1
  378. package/types/rancher/steve.api.ts +12 -12
  379. package/types/resources/settings.d.ts +2 -1
  380. package/types/shell/index.d.ts +206 -72
  381. package/types/store/dashboard-store.types.ts +108 -11
  382. package/types/store/pagination.types.ts +6 -3
  383. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  384. package/utils/__tests__/async.test.ts +87 -0
  385. package/utils/__tests__/auth.test.ts +273 -0
  386. package/utils/__tests__/aws.test.ts +140 -0
  387. package/utils/__tests__/banners.test.ts +176 -0
  388. package/utils/__tests__/chart.test.ts +64 -1
  389. package/utils/__tests__/color.test.ts +226 -0
  390. package/utils/__tests__/computed.test.ts +193 -0
  391. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  392. package/utils/__tests__/dom.test.ts +81 -0
  393. package/utils/__tests__/duration.test.ts +176 -0
  394. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  395. package/utils/__tests__/fleet-appco.test.ts +312 -0
  396. package/utils/__tests__/fleet.test.ts +340 -0
  397. package/utils/__tests__/ingress.test.ts +553 -0
  398. package/utils/__tests__/kube.test.ts +68 -0
  399. package/utils/__tests__/monitoring.test.ts +130 -0
  400. package/utils/__tests__/namespace-filter.test.ts +109 -0
  401. package/utils/__tests__/object.test.ts +22 -0
  402. package/utils/__tests__/pagination-utils.test.ts +361 -0
  403. package/utils/__tests__/parse-externalid.test.ts +137 -0
  404. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  405. package/utils/__tests__/platform.test.ts +91 -0
  406. package/utils/__tests__/poller-sequential.test.ts +177 -0
  407. package/utils/__tests__/poller.test.ts +170 -0
  408. package/utils/__tests__/position.test.ts +237 -0
  409. package/utils/__tests__/promise.test.ts +346 -0
  410. package/utils/__tests__/provider.test.ts +51 -1
  411. package/utils/__tests__/queue.test.ts +232 -0
  412. package/utils/__tests__/release-notes.test.ts +221 -0
  413. package/utils/__tests__/router.test.js +254 -1
  414. package/utils/__tests__/select.test.ts +208 -0
  415. package/utils/__tests__/settings.test.ts +140 -0
  416. package/utils/__tests__/sort-utils.test.ts +301 -0
  417. package/utils/__tests__/string-utils.test.ts +798 -0
  418. package/utils/__tests__/string.test.ts +23 -1
  419. package/utils/__tests__/style.test.ts +154 -0
  420. package/utils/__tests__/svg-filter.test.ts +184 -0
  421. package/utils/__tests__/time.test.ts +265 -1
  422. package/utils/__tests__/title.test.ts +47 -0
  423. package/utils/__tests__/units.test.ts +417 -0
  424. package/utils/__tests__/versions.test.ts +128 -0
  425. package/utils/__tests__/width.test.ts +53 -0
  426. package/utils/__tests__/window.test.ts +158 -0
  427. package/utils/__tests__/xccdf.test.ts +511 -0
  428. package/utils/chart.js +36 -0
  429. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  430. package/utils/crypto/__tests__/index.test.ts +144 -0
  431. package/utils/duration.ts +104 -0
  432. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  433. package/utils/dynamic-content/info.ts +2 -1
  434. package/utils/error.js +13 -0
  435. package/utils/fleet-appco.ts +323 -0
  436. package/utils/fleet.ts +13 -3
  437. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  438. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  439. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  440. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  441. package/utils/gc/__tests__/gc.test.ts +487 -0
  442. package/utils/ingress.ts +9 -1
  443. package/utils/object.js +22 -2
  444. package/utils/pagination-utils.ts +2 -1
  445. package/utils/provider.ts +12 -0
  446. package/utils/string.js +25 -2
  447. package/utils/uiplugins.ts +5 -5
  448. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  449. package/utils/validators/__tests__/container-images.test.ts +104 -0
  450. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  451. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  452. package/utils/validators/__tests__/index.test.ts +481 -0
  453. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  454. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  455. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  456. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  457. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  458. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  459. package/utils/validators/__tests__/role-template.test.ts +149 -0
  460. package/utils/validators/__tests__/service.test.ts +283 -0
  461. package/utils/validators/__tests__/setting.test.js +32 -0
  462. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  463. package/utils/validators/formRules/index.ts +5 -5
  464. package/utils/validators/machine-pool.ts +1 -1
  465. package/utils/validators/setting.js +18 -3
  466. package/utils/xccdf.ts +415 -0
  467. package/vue.config.js +1 -1
  468. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  469. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  470. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  471. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
  472. package/pages/support/index.vue +0 -264
  473. package/utils/duration.js +0 -43
@@ -17,6 +17,7 @@ describe('clusterRepo', () => {
17
17
  jest.spyOn(model, 'save').mockImplementation().mockResolvedValue(true);
18
18
  jest.spyOn(model, 'waitForState').mockImplementation().mockResolvedValue(true);
19
19
  jest.spyOn(model, '$dispatch', 'get').mockReturnValue(jest.fn());
20
+ jest.spyOn(model, 't', 'get').mockReturnValue((key: string) => key);
20
21
  });
21
22
 
22
23
  describe('refresh', () => {
@@ -59,6 +60,62 @@ describe('clusterRepo', () => {
59
60
  });
60
61
  });
61
62
 
63
+ describe('defaultRefreshIntervalHours', () => {
64
+ it('returns 24 for OCI repos', () => {
65
+ model.spec.url = 'oci://example.com/chart';
66
+ model.spec.insecurePlainHttp = false;
67
+ expect(model.defaultRefreshIntervalHours).toStrictEqual(24);
68
+ });
69
+
70
+ it('returns 1 for git repos', () => {
71
+ model.spec = { gitRepo: 'https://github.com/example/charts' };
72
+ expect(model.defaultRefreshIntervalHours).toStrictEqual(1);
73
+ });
74
+
75
+ it('returns 1 for helm repos', () => {
76
+ model.spec = { url: 'https://charts.example.com' };
77
+ expect(model.defaultRefreshIntervalHours).toStrictEqual(1);
78
+ });
79
+ });
80
+
81
+ describe('defaultRefreshInterval', () => {
82
+ it('returns seconds for OCI repos (24 hours)', () => {
83
+ model.spec.url = 'oci://example.com/chart';
84
+ model.spec.insecurePlainHttp = false;
85
+ expect(model.defaultRefreshInterval).toStrictEqual(86400);
86
+ });
87
+
88
+ it('returns seconds for non-OCI repos (1 hour)', () => {
89
+ model.spec = { gitRepo: 'https://github.com/example/charts' };
90
+ expect(model.defaultRefreshInterval).toStrictEqual(3600);
91
+ });
92
+ });
93
+
94
+ describe('refreshIntervalDisplay', () => {
95
+ it.each([
96
+ -1,
97
+ -100,
98
+ ])('returns "Disabled" when value is %p', (val) => {
99
+ model.spec.refreshInterval = val;
100
+ expect(model.refreshIntervalDisplay).toStrictEqual('generic.disabled');
101
+ });
102
+
103
+ it('returns formatted duration for positive values', () => {
104
+ model.spec.refreshInterval = 3661;
105
+ expect(model.refreshIntervalDisplay).toStrictEqual('1h 1m 1s');
106
+ });
107
+
108
+ it('returns default formatted duration when not set', () => {
109
+ model.spec = { url: 'https://charts.example.com' };
110
+ expect(model.refreshIntervalDisplay).toStrictEqual('1h');
111
+ });
112
+
113
+ it('returns default formatted duration for OCI when not set', () => {
114
+ model.spec = { url: 'oci://example.com/chart', insecurePlainHttp: false };
115
+ expect(model.refreshIntervalDisplay).toStrictEqual('1d');
116
+ });
117
+ });
118
+
62
119
  describe('refreshBulk', () => {
63
120
  it('calls refresh(false) on all items and then dispatches a single catalog/load with all repoKeys', async() => {
64
121
  const mockItem1 = {
@@ -0,0 +1,144 @@
1
+ import ClusterScan from '@shell/models/compliance.cattle.io.clusterscan';
2
+
3
+ /**
4
+ * Build a ClusterScan model wired to a mocked store `dispatch`.
5
+ * `$dispatch` resolves to `this.$ctx.dispatch`, so injecting it via the
6
+ * constructor context is enough to exercise `_resolveExportMetadata`.
7
+ */
8
+ const makeScan = (dispatch: jest.Mock) => new ClusterScan({}, {
9
+ getters: {},
10
+ rootGetters: {},
11
+ dispatch,
12
+ } as any) as any;
13
+
14
+ const benchmarkWithConfigMap = {
15
+ spec: {
16
+ customBenchmarkConfigMapName: 'metadata-cm',
17
+ customBenchmarkConfigMapNamespace: 'compliance',
18
+ },
19
+ };
20
+
21
+ describe('class: ClusterScan', () => {
22
+ describe('method: _resolveExportMetadata', () => {
23
+ it('returns empty metadata and decorations when the benchmark is undefined', async() => {
24
+ const dispatch = jest.fn();
25
+ const scan = makeScan(dispatch);
26
+
27
+ const result = await scan._resolveExportMetadata(undefined);
28
+
29
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
30
+ expect(dispatch).not.toHaveBeenCalled();
31
+ });
32
+
33
+ it('returns empty when the ConfigMap name is missing', async() => {
34
+ const dispatch = jest.fn();
35
+ const scan = makeScan(dispatch);
36
+
37
+ const result = await scan._resolveExportMetadata({ spec: { customBenchmarkConfigMapNamespace: 'compliance' } });
38
+
39
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
40
+ expect(dispatch).not.toHaveBeenCalled();
41
+ });
42
+
43
+ it('returns empty when the ConfigMap namespace is missing', async() => {
44
+ const dispatch = jest.fn();
45
+ const scan = makeScan(dispatch);
46
+
47
+ const result = await scan._resolveExportMetadata({ spec: { customBenchmarkConfigMapName: 'metadata-cm' } });
48
+
49
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
50
+ expect(dispatch).not.toHaveBeenCalled();
51
+ });
52
+
53
+ it('fetches the ConfigMap by namespace/name from the configmap type', async() => {
54
+ const dispatch = jest.fn().mockResolvedValue({ data: {} });
55
+ const scan = makeScan(dispatch);
56
+
57
+ await scan._resolveExportMetadata(benchmarkWithConfigMap);
58
+
59
+ expect(dispatch).toHaveBeenCalledWith('find', { type: 'configmap', id: 'compliance/metadata-cm' });
60
+ });
61
+
62
+ it('returns empty when the ConfigMap has no metadata.yaml data key', async() => {
63
+ const dispatch = jest.fn().mockResolvedValue({ data: { 'other.yaml': 'title: x' } });
64
+ const scan = makeScan(dispatch);
65
+
66
+ const result = await scan._resolveExportMetadata(benchmarkWithConfigMap);
67
+
68
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
69
+ });
70
+
71
+ it('returns empty when the ConfigMap itself is not found', async() => {
72
+ const dispatch = jest.fn().mockResolvedValue(undefined);
73
+ const scan = makeScan(dispatch);
74
+
75
+ const result = await scan._resolveExportMetadata(benchmarkWithConfigMap);
76
+
77
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
78
+ });
79
+
80
+ it('splits top-level fields into metadata and the checks map into decorations', async() => {
81
+ const yaml = [
82
+ 'title: CIS Kubernetes Benchmark',
83
+ 'description: A CIS profile',
84
+ 'referenceType: cis',
85
+ 'checks:',
86
+ ' "5.1.1":',
87
+ ' ruleId: CIS-5.1.1-rule',
88
+ ' severity: high',
89
+ ' idents:',
90
+ ' - system: https://www.cisecurity.org/controls/',
91
+ ' value: CIS-CSC-3',
92
+ ' "5.1.2":',
93
+ ' ruleId: CIS-5.1.2-rule',
94
+ ].join('\n');
95
+ const dispatch = jest.fn().mockResolvedValue({ data: { 'metadata.yaml': yaml } });
96
+ const scan = makeScan(dispatch);
97
+
98
+ const result = await scan._resolveExportMetadata(benchmarkWithConfigMap);
99
+
100
+ expect(result.metadata).toStrictEqual({
101
+ title: 'CIS Kubernetes Benchmark',
102
+ description: 'A CIS profile',
103
+ referenceType: 'cis',
104
+ });
105
+ expect(result.decorations).toStrictEqual({
106
+ '5.1.1': {
107
+ ruleId: 'CIS-5.1.1-rule',
108
+ severity: 'high',
109
+ idents: [{ system: 'https://www.cisecurity.org/controls/', value: 'CIS-CSC-3' }],
110
+ },
111
+ '5.1.2': { ruleId: 'CIS-5.1.2-rule' },
112
+ });
113
+ });
114
+
115
+ it('returns empty decorations when metadata.yaml has no checks map', async() => {
116
+ const yaml = ['title: STIG', 'source: disa'].join('\n');
117
+ const dispatch = jest.fn().mockResolvedValue({ data: { 'metadata.yaml': yaml } });
118
+ const scan = makeScan(dispatch);
119
+
120
+ const result = await scan._resolveExportMetadata(benchmarkWithConfigMap);
121
+
122
+ expect(result.metadata).toStrictEqual({ title: 'STIG', source: 'disa' });
123
+ expect(result.decorations).toStrictEqual({});
124
+ });
125
+
126
+ it('returns empty when the ConfigMap lookup rejects', async() => {
127
+ const dispatch = jest.fn().mockRejectedValue(new Error('not found'));
128
+ const scan = makeScan(dispatch);
129
+
130
+ const result = await scan._resolveExportMetadata(benchmarkWithConfigMap);
131
+
132
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
133
+ });
134
+
135
+ it('returns empty when metadata.yaml is malformed YAML', async() => {
136
+ const dispatch = jest.fn().mockResolvedValue({ data: { 'metadata.yaml': 'title: "unterminated' } });
137
+ const scan = makeScan(dispatch);
138
+
139
+ const result = await scan._resolveExportMetadata(benchmarkWithConfigMap);
140
+
141
+ expect(result).toStrictEqual({ metadata: {}, decorations: {} });
142
+ });
143
+ });
144
+ });
@@ -1,5 +1,5 @@
1
1
  import Kubeconfig from '@shell/models/ext.cattle.io.kubeconfig';
2
- import { CAPI, MANAGEMENT } from '@shell/config/types';
2
+ import { MANAGEMENT } from '@shell/config/types';
3
3
 
4
4
  // SteveModel is JS, so we need to type the constructor
5
5
  const KubeconfigModel = Kubeconfig as unknown as new (data: object) => Kubeconfig;
@@ -19,8 +19,9 @@ describe('class Kubeconfig', () => {
19
19
  // Mock $rootGetters before any getters are accessed
20
20
  // Cast to any since $rootGetters is inherited from JS SteveModel
21
21
  jest.spyOn(kubeconfig as any, '$rootGetters', 'get').mockReturnValue({
22
- 'i18n/t': mockT,
23
- 'management/all': () => [],
22
+ 'i18n/t': mockT,
23
+ 'management/all': () => [],
24
+ 'management/byId': () => null,
24
25
  ...rootGetters
25
26
  });
26
27
 
@@ -70,11 +71,12 @@ describe('class Kubeconfig', () => {
70
71
  });
71
72
 
72
73
  describe('referencedClusters', () => {
73
- const mockProvCluster = {
74
- mgmt: { id: 'c-m-abc123' },
75
- status: { clusterName: 'c-m-abc123' },
76
- nameDisplay: 'my-cluster',
77
- detailLocation: { name: 'c-cluster-product-resource-id', params: { cluster: 'my-cluster' } }
74
+ const mockProvCluster = { detailLocation: { name: 'c-cluster-product-resource-id', params: { cluster: 'my-cluster' } } };
75
+
76
+ const mockMgmtClusterWithProv = {
77
+ id: 'c-m-abc123',
78
+ nameDisplay: 'my-cluster',
79
+ provCluster: mockProvCluster
78
80
  };
79
81
 
80
82
  const mockMgmtCluster = {
@@ -99,12 +101,12 @@ describe('class Kubeconfig', () => {
99
101
  spec: { clusters: ['c-m-abc123'] }
100
102
  },
101
103
  {
102
- 'management/all': (type: string) => {
103
- if (type === CAPI.RANCHER_CLUSTER) {
104
- return [mockProvCluster];
104
+ 'management/byId': (type: string, id: string) => {
105
+ if (type === MANAGEMENT.CLUSTER && id === 'c-m-abc123') {
106
+ return mockMgmtClusterWithProv;
105
107
  }
106
108
 
107
- return [];
109
+ return null;
108
110
  }
109
111
  }
110
112
  );
@@ -124,12 +126,12 @@ describe('class Kubeconfig', () => {
124
126
  spec: { clusters: ['c-m-def456'] }
125
127
  },
126
128
  {
127
- 'management/all': (type: string) => {
128
- if (type === MANAGEMENT.CLUSTER) {
129
- return [mockMgmtCluster];
129
+ 'management/byId': (type: string, id: string) => {
130
+ if (type === MANAGEMENT.CLUSTER && id === 'c-m-def456') {
131
+ return mockMgmtCluster;
130
132
  }
131
133
 
132
- return [];
134
+ return null;
133
135
  }
134
136
  }
135
137
  );
@@ -157,11 +159,12 @@ describe('class Kubeconfig', () => {
157
159
  expect(mockT).toHaveBeenCalledWith('"ext.cattle.io.kubeconfig".deleted', { name: 'c-m-deleted' });
158
160
  });
159
161
 
160
- it('should prefer provisioning cluster over management cluster', () => {
161
- const mgmtClusterSameId = {
162
+ it('should prefer provisioning cluster over management cluster for location', () => {
163
+ const mgmtClusterBothLocs = {
162
164
  id: 'c-m-abc123',
163
- nameDisplay: 'mgmt-version',
164
- detailLocation: { name: 'mgmt-location' }
165
+ nameDisplay: 'my-cluster',
166
+ detailLocation: { name: 'mgmt-location' },
167
+ provCluster: { detailLocation: { name: 'prov-location' } }
165
168
  };
166
169
 
167
170
  const kubeconfig = createKubeconfig(
@@ -170,15 +173,12 @@ describe('class Kubeconfig', () => {
170
173
  spec: { clusters: ['c-m-abc123'] }
171
174
  },
172
175
  {
173
- 'management/all': (type: string) => {
174
- if (type === CAPI.RANCHER_CLUSTER) {
175
- return [mockProvCluster];
176
- }
177
- if (type === MANAGEMENT.CLUSTER) {
178
- return [mgmtClusterSameId];
176
+ 'management/byId': (type: string, id: string) => {
177
+ if (type === MANAGEMENT.CLUSTER && id === 'c-m-abc123') {
178
+ return mgmtClusterBothLocs;
179
179
  }
180
180
 
181
- return [];
181
+ return null;
182
182
  }
183
183
  }
184
184
  );
@@ -186,7 +186,7 @@ describe('class Kubeconfig', () => {
186
186
  expect(kubeconfig.referencedClusters).toStrictEqual([
187
187
  {
188
188
  label: 'my-cluster',
189
- location: mockProvCluster.detailLocation
189
+ location: { name: 'prov-location' }
190
190
  }
191
191
  ]);
192
192
  });
@@ -195,7 +195,7 @@ describe('class Kubeconfig', () => {
195
195
  describe('sortedReferencedClusters', () => {
196
196
  it('should sort existing clusters before deleted clusters', () => {
197
197
  const existingCluster = {
198
- mgmt: { id: 'c-m-exists' },
198
+ id: 'c-m-exists',
199
199
  nameDisplay: 'existing-cluster',
200
200
  detailLocation: { name: 'location' }
201
201
  };
@@ -206,12 +206,12 @@ describe('class Kubeconfig', () => {
206
206
  spec: { clusters: ['deleted-1', 'c-m-exists', 'deleted-2'] }
207
207
  },
208
208
  {
209
- 'management/all': (type: string) => {
210
- if (type === CAPI.RANCHER_CLUSTER) {
211
- return [existingCluster];
209
+ 'management/byId': (type: string, id: string) => {
210
+ if (type === MANAGEMENT.CLUSTER && id === 'c-m-exists') {
211
+ return existingCluster;
212
212
  }
213
213
 
214
- return [];
214
+ return null;
215
215
  }
216
216
  }
217
217
  );
@@ -225,17 +225,17 @@ describe('class Kubeconfig', () => {
225
225
  });
226
226
 
227
227
  it('should sort existing clusters alphabetically', () => {
228
- const clusters = [
229
- {
230
- mgmt: { id: 'c-m-zebra' }, nameDisplay: 'zebra', detailLocation: { name: 'z' }
228
+ const clusters: Record<string, any> = {
229
+ 'c-m-zebra': {
230
+ id: 'c-m-zebra', nameDisplay: 'zebra', detailLocation: { name: 'z' }
231
231
  },
232
- {
233
- mgmt: { id: 'c-m-alpha' }, nameDisplay: 'alpha', detailLocation: { name: 'a' }
232
+ 'c-m-alpha': {
233
+ id: 'c-m-alpha', nameDisplay: 'alpha', detailLocation: { name: 'a' }
234
234
  },
235
- {
236
- mgmt: { id: 'c-m-beta' }, nameDisplay: 'beta', detailLocation: { name: 'b' }
235
+ 'c-m-beta': {
236
+ id: 'c-m-beta', nameDisplay: 'beta', detailLocation: { name: 'b' }
237
237
  }
238
- ];
238
+ };
239
239
 
240
240
  const kubeconfig = createKubeconfig(
241
241
  {
@@ -243,12 +243,12 @@ describe('class Kubeconfig', () => {
243
243
  spec: { clusters: ['c-m-zebra', 'c-m-alpha', 'c-m-beta'] }
244
244
  },
245
245
  {
246
- 'management/all': (type: string) => {
247
- if (type === CAPI.RANCHER_CLUSTER) {
248
- return clusters;
246
+ 'management/byId': (type: string, id: string) => {
247
+ if (type === MANAGEMENT.CLUSTER) {
248
+ return clusters[id] || null;
249
249
  }
250
250
 
251
- return [];
251
+ return null;
252
252
  }
253
253
  }
254
254
  );
@@ -259,17 +259,17 @@ describe('class Kubeconfig', () => {
259
259
  });
260
260
 
261
261
  it('should sort numerically when names contain numbers', () => {
262
- const clusters = [
263
- {
264
- mgmt: { id: 'c-m-2' }, nameDisplay: 'cluster2', detailLocation: { name: 'c2' }
262
+ const clusters: Record<string, any> = {
263
+ 'c-m-2': {
264
+ id: 'c-m-2', nameDisplay: 'cluster2', detailLocation: { name: 'c2' }
265
265
  },
266
- {
267
- mgmt: { id: 'c-m-10' }, nameDisplay: 'cluster10', detailLocation: { name: 'c10' }
266
+ 'c-m-10': {
267
+ id: 'c-m-10', nameDisplay: 'cluster10', detailLocation: { name: 'c10' }
268
268
  },
269
- {
270
- mgmt: { id: 'c-m-1' }, nameDisplay: 'cluster1', detailLocation: { name: 'c1' }
269
+ 'c-m-1': {
270
+ id: 'c-m-1', nameDisplay: 'cluster1', detailLocation: { name: 'c1' }
271
271
  }
272
- ];
272
+ };
273
273
 
274
274
  const kubeconfig = createKubeconfig(
275
275
  {
@@ -277,12 +277,12 @@ describe('class Kubeconfig', () => {
277
277
  spec: { clusters: ['c-m-2', 'c-m-10', 'c-m-1'] }
278
278
  },
279
279
  {
280
- 'management/all': (type: string) => {
281
- if (type === CAPI.RANCHER_CLUSTER) {
282
- return clusters;
280
+ 'management/byId': (type: string, id: string) => {
281
+ if (type === MANAGEMENT.CLUSTER) {
282
+ return clusters[id] || null;
283
283
  }
284
284
 
285
- return [];
285
+ return null;
286
286
  }
287
287
  }
288
288
  );
@@ -295,14 +295,14 @@ describe('class Kubeconfig', () => {
295
295
 
296
296
  describe('referencedClustersSortable', () => {
297
297
  it('should return comma-separated lowercase labels', () => {
298
- const clusters = [
299
- {
300
- mgmt: { id: 'c-m-1' }, nameDisplay: 'Alpha', detailLocation: { name: 'a' }
298
+ const clusters: Record<string, any> = {
299
+ 'c-m-1': {
300
+ id: 'c-m-1', nameDisplay: 'Alpha', detailLocation: { name: 'a' }
301
301
  },
302
- {
303
- mgmt: { id: 'c-m-2' }, nameDisplay: 'Beta', detailLocation: { name: 'b' }
302
+ 'c-m-2': {
303
+ id: 'c-m-2', nameDisplay: 'Beta', detailLocation: { name: 'b' }
304
304
  }
305
- ];
305
+ };
306
306
 
307
307
  const kubeconfig = createKubeconfig(
308
308
  {
@@ -310,12 +310,12 @@ describe('class Kubeconfig', () => {
310
310
  spec: { clusters: ['c-m-1', 'c-m-2'] }
311
311
  },
312
312
  {
313
- 'management/all': (type: string) => {
314
- if (type === CAPI.RANCHER_CLUSTER) {
315
- return clusters;
313
+ 'management/byId': (type: string, id: string) => {
314
+ if (type === MANAGEMENT.CLUSTER) {
315
+ return clusters[id] || null;
316
316
  }
317
317
 
318
- return [];
318
+ return null;
319
319
  }
320
320
  }
321
321
  );
@@ -0,0 +1,175 @@
1
+ import FleetApplication from '@shell/models/fleet-application.js';
2
+
3
+ describe('class FleetApplication', () => {
4
+ afterEach(() => {
5
+ jest.restoreAllMocks();
6
+ });
7
+
8
+ describe('applicationType', () => {
9
+ it('should return the kind property', () => {
10
+ const instance = new FleetApplication({ kind: 'GitRepo' });
11
+
12
+ expect(instance.applicationType).toStrictEqual('GitRepo');
13
+ });
14
+ });
15
+
16
+ describe('targetClusters', () => {
17
+ function createFleetApplication(targets: any[] | undefined, clusters: any[], workspaceId = 'fleet-default', groups: any[] = []) {
18
+ const workspace = {
19
+ id: workspaceId,
20
+ clusters,
21
+ clusterGroups: groups,
22
+ };
23
+
24
+ jest.spyOn(FleetApplication.prototype, '$getters', 'get').mockReturnValue({ byId: () => workspace });
25
+
26
+ return new FleetApplication({
27
+ metadata: { namespace: workspaceId },
28
+ spec: { targets },
29
+ });
30
+ }
31
+
32
+ it.each([
33
+ [
34
+ 'metadata.name',
35
+ [{ clusterName: 'c-m-abc123' }],
36
+ [{
37
+ id: 'fleet-default/c-m-abc123', metadata: { name: 'c-m-abc123' }, nameDisplay: 'my-cluster'
38
+ }],
39
+ [{
40
+ id: 'fleet-default/c-m-abc123', metadata: { name: 'c-m-abc123' }, nameDisplay: 'my-cluster'
41
+ }],
42
+ ],
43
+ [
44
+ 'nameDisplay when metadata.name does not match',
45
+ [{ clusterName: 'my-display-name' }],
46
+ [{
47
+ id: 'fleet-default/c-m-abc123', metadata: { name: 'c-m-abc123' }, nameDisplay: 'my-display-name'
48
+ }],
49
+ [{
50
+ id: 'fleet-default/c-m-abc123', metadata: { name: 'c-m-abc123' }, nameDisplay: 'my-display-name'
51
+ }],
52
+ ],
53
+ ])('should find cluster by %s', (_label, targets, clusters, expected) => {
54
+ const app = createFleetApplication(targets, clusters);
55
+
56
+ expect(app.targetClusters).toStrictEqual(expected);
57
+ });
58
+
59
+ it('should prefer metadata.name match over nameDisplay match', () => {
60
+ const clusters = [
61
+ {
62
+ id: 'fleet-default/exact-match',
63
+ metadata: { name: 'exact-match' },
64
+ nameDisplay: 'display-a',
65
+ },
66
+ {
67
+ id: 'fleet-default/c-m-other',
68
+ metadata: { name: 'c-m-other' },
69
+ nameDisplay: 'exact-match',
70
+ }
71
+ ];
72
+
73
+ const app = createFleetApplication([{ clusterName: 'exact-match' }], clusters);
74
+
75
+ expect(app.targetClusters).toStrictEqual([clusters[0]]);
76
+ });
77
+
78
+ it('should return empty array when no cluster matches by name or nameDisplay', () => {
79
+ const clusters = [
80
+ {
81
+ id: 'fleet-default/c-m-abc123',
82
+ metadata: { name: 'c-m-abc123' },
83
+ nameDisplay: 'my-cluster',
84
+ }
85
+ ];
86
+
87
+ const app = createFleetApplication([{ clusterName: 'non-existent' }], clusters);
88
+
89
+ expect(app.targetClusters).toStrictEqual([]);
90
+ });
91
+
92
+ it('should handle multiple targets with mixed name and nameDisplay matches', () => {
93
+ const clusters = [
94
+ {
95
+ id: 'fleet-default/c-m-abc123',
96
+ metadata: { name: 'c-m-abc123' },
97
+ nameDisplay: 'cluster-alpha',
98
+ },
99
+ {
100
+ id: 'fleet-default/c-m-def456',
101
+ metadata: { name: 'c-m-def456' },
102
+ nameDisplay: 'cluster-beta',
103
+ }
104
+ ];
105
+
106
+ const targets = [
107
+ { clusterName: 'c-m-abc123' },
108
+ { clusterName: 'cluster-beta' },
109
+ ];
110
+
111
+ const app = createFleetApplication(targets, clusters);
112
+
113
+ expect(app.targetClusters).toStrictEqual([clusters[0], clusters[1]]);
114
+ });
115
+
116
+ it('should return empty array when targets is empty', () => {
117
+ const app = createFleetApplication([], []);
118
+
119
+ expect(app.targetClusters).toStrictEqual([]);
120
+ });
121
+
122
+ it('should return empty array when targets is undefined', () => {
123
+ const app = createFleetApplication(undefined, []);
124
+
125
+ expect(app.targetClusters).toStrictEqual([]);
126
+ });
127
+
128
+ it('should return empty array when workspace has no clusters', () => {
129
+ const app = createFleetApplication([{ clusterName: 'any-name' }], []);
130
+
131
+ expect(app.targetClusters).toStrictEqual([]);
132
+ });
133
+
134
+ it('should handle cluster with undefined nameDisplay gracefully', () => {
135
+ const clusters = [
136
+ {
137
+ id: 'fleet-default/c-m-abc123',
138
+ metadata: { name: 'c-m-abc123' },
139
+ nameDisplay: undefined,
140
+ }
141
+ ];
142
+
143
+ const app = createFleetApplication([{ clusterName: 'c-m-abc123' }], clusters);
144
+
145
+ expect(app.targetClusters).toStrictEqual([clusters[0]]);
146
+ });
147
+
148
+ it('should return local cluster targets when workspace is fleet-local', () => {
149
+ const localTargetClusters = [
150
+ {
151
+ id: 'fleet-local/local',
152
+ metadata: { name: 'local' },
153
+ nameDisplay: 'local',
154
+ }
155
+ ];
156
+
157
+ const groups = [
158
+ {
159
+ id: 'fleet-local/default',
160
+ targetClusters: localTargetClusters,
161
+ }
162
+ ];
163
+
164
+ const app = createFleetApplication([], [], 'fleet-local', groups);
165
+
166
+ expect(app.targetClusters).toStrictEqual(localTargetClusters);
167
+ });
168
+
169
+ it('should return empty array when workspace is fleet-local and default group is missing', () => {
170
+ const app = createFleetApplication([], [], 'fleet-local', []);
171
+
172
+ expect(app.targetClusters).toStrictEqual([]);
173
+ });
174
+ });
175
+ });