@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
@@ -0,0 +1,237 @@
1
+ import {
2
+ fakeRectFor,
3
+ fitOnScreen,
4
+ AUTO,
5
+ LEFT,
6
+ RIGHT,
7
+ TOP,
8
+ BOTTOM,
9
+ CENTER,
10
+ MIDDLE,
11
+ } from '@shell/utils/position';
12
+
13
+ /**
14
+ * Set up JSDOM window dimensions for all tests in this file.
15
+ * fitOnScreen calls screenRect() which reads window.innerWidth/Height/pageX/YOffset.
16
+ */
17
+ function setScreen(width: number, height: number, scrollX = 0, scrollY = 0) {
18
+ Object.defineProperty(window, 'innerWidth', { configurable: true, value: width });
19
+ Object.defineProperty(window, 'innerHeight', { configurable: true, value: height });
20
+ Object.defineProperty(window, 'pageXOffset', { configurable: true, value: scrollX });
21
+ Object.defineProperty(window, 'pageYOffset', { configurable: true, value: scrollY });
22
+ }
23
+
24
+ /** Build a real MouseEvent so that instanceof Event check in fitOnScreen passes. */
25
+ function makeEvent(clientX: number, clientY: number): MouseEvent {
26
+ return new MouseEvent('click', { clientX, clientY });
27
+ }
28
+
29
+ describe('position.js', () => {
30
+ describe('fakeRectFor', () => {
31
+ it.each([
32
+ {
33
+ desc: 'creates a zero-size rect centred at the event coordinates',
34
+ clientX: 100,
35
+ clientY: 200,
36
+ expected: {
37
+ top: 200, left: 100, bottom: 200, right: 100, width: 0, height: 0
38
+ },
39
+ },
40
+ {
41
+ desc: 'handles origin (0,0)',
42
+ clientX: 0,
43
+ clientY: 0,
44
+ expected: {
45
+ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0
46
+ },
47
+ },
48
+ {
49
+ desc: 'handles negative coordinates',
50
+ clientX: -5,
51
+ clientY: -10,
52
+ expected: {
53
+ top: -10, left: -5, bottom: -10, right: -5, width: 0, height: 0
54
+ },
55
+ },
56
+ ])('$desc', ({ clientX, clientY, expected }) => {
57
+ expect(fakeRectFor(makeEvent(clientX, clientY))).toStrictEqual(expected);
58
+ });
59
+ });
60
+
61
+ describe('fitOnScreen', () => {
62
+ beforeEach(() => {
63
+ setScreen(1000, 800);
64
+ });
65
+
66
+ describe('horizontal AUTO positioning', () => {
67
+ it('chooses LEFT when there is more room on the left than the right', () => {
68
+ // Trigger at x=500 on a 1000px screen. Content is 147px wide.
69
+ // gapIf.left = 1000 - 147 - 500 = 353; gapIf.right = 500 - 147 - 0 = 353
70
+ // condition: gapIf.left < 0 || gapIf.right * 1.5 > gapIf.left → 353*1.5=529.5 > 353 → RIGHT
71
+ // Actually with equal gaps condition picks RIGHT. Let's use a trigger far from left.
72
+ // Trigger at x=700: gapIf.left = 1000-147-700=153; gapIf.right = 700-147-0=553
73
+ // condition: gapIf.right * 1.5 > gapIf.left → 553*1.5=829.5 > 153 → RIGHT
74
+ // For LEFT: need gapIf.right * 1.5 <= gapIf.left
75
+ // Example: trigger at x=200, content=147: gapIf.left=653, gapIf.right=53; 53*1.5=79.5 < 653 → LEFT
76
+ const style = fitOnScreen(null, makeEvent(200, 400), { positionX: AUTO, positionY: AUTO }, true);
77
+
78
+ expect(style.left).toBe('200px'); // originFor.left - fudgeX = 200 - 0
79
+ });
80
+
81
+ it('chooses RIGHT when there is more room on the right', () => {
82
+ // Trigger at x=900: gapIf.left = 1000-147-900=-47 < 0 → RIGHT
83
+ // style.left = originFor.right + 0 - 147 = 900 - 147 = 753
84
+ const style = fitOnScreen(null, makeEvent(900, 400), { positionX: AUTO, positionY: AUTO }, true);
85
+
86
+ expect(style.left).toBe('753px');
87
+ });
88
+
89
+ it('chooses RIGHT when left gap is negative', () => {
90
+ // Trigger at x=50: gapIf.right = 50-147-0 = -97 (negative), gapIf.left = 1000-147-50=803 (positive)
91
+ // condition: gapIf.left < 0 = false, gapIf.right*1.5=-145.5 > 803? No → LEFT
92
+ // Wait let me recalculate: trigger at (50, 400), overlapX=true default
93
+ // originFor.left = trigger.left = 50, originFor.right = trigger.right = 50
94
+ // gapIf.left = screen.right - content.width - originFor.left = 1000 - 147 - 50 = 803
95
+ // 803 < 0? No. gapIf.right * 1.5 > gapIf.left → (-97)*1.5 = -145.5 > 803? No → LEFT
96
+ // style.left = 50 - 0 = 50px
97
+ const style = fitOnScreen(null, makeEvent(50, 400), { positionX: AUTO, positionY: AUTO }, true);
98
+
99
+ expect(style.left).toBe('50px');
100
+ });
101
+ });
102
+
103
+ describe('explicit horizontal positioning', () => {
104
+ it('respects explicit LEFT position', () => {
105
+ // Trigger at x=300 (lots of room both sides)
106
+ // originFor.left = 300, style.left = 300 - 0 = 300px
107
+ const style = fitOnScreen(null, makeEvent(300, 400), { positionX: LEFT, positionY: AUTO }, true);
108
+
109
+ expect(style.left).toBe('300px');
110
+ });
111
+
112
+ it('respects explicit RIGHT position', () => {
113
+ // Trigger at x=300, content.width=147: style.left = 300 + 0 - 147 = 153px
114
+ const style = fitOnScreen(null, makeEvent(300, 400), { positionX: RIGHT, positionY: AUTO }, true);
115
+
116
+ expect(style.left).toBe('153px');
117
+ });
118
+
119
+ it('uses CENTER position when there is enough room', () => {
120
+ // Trigger at x=500: originFor.left=500, originFor.right=500
121
+ // gapIf.center = min(1000 - 73.5 - 500, 500 - 73.5 - 0) = min(426.5, 426.5) = 426.5 >= 0 → stays CENTER
122
+ // style.left = (500+500)/2 - 147/2 - 0 = 500 - 73.5 = 426.5px
123
+ const style = fitOnScreen(null, makeEvent(500, 400), { positionX: CENTER, positionY: AUTO }, true);
124
+
125
+ expect(style.left).toBe('426.5px');
126
+ });
127
+
128
+ it('falls back from CENTER to AUTO when center gap is negative', () => {
129
+ // Trigger at x=50: gapIf.center = min(1000-73.5-50, 50-73.5-0) = min(876.5, -23.5) = -23.5 < 0 → AUTO
130
+ // From AUTO: gapIf.left=803, gapIf.right=-97; gapIf.right*1.5=-145.5 > 803? No → LEFT
131
+ // style.left = 50px
132
+ const style = fitOnScreen(null, makeEvent(50, 400), { positionX: CENTER, positionY: AUTO }, true);
133
+
134
+ expect(style.left).toBe('50px');
135
+ });
136
+ });
137
+
138
+ describe('vertical AUTO positioning', () => {
139
+ it('chooses BOTTOM when the trigger is near the top', () => {
140
+ // Trigger at y=10: gapIf.top = 10 - 80 - 0 = -70 < 0 → BOTTOM
141
+ // style.top = originFor.bottom - 0 = 10px
142
+ const style = fitOnScreen(null, makeEvent(200, 10), { positionX: AUTO, positionY: AUTO }, true);
143
+
144
+ expect(style.top).toBe('10px');
145
+ });
146
+
147
+ it('chooses TOP when the trigger is far from the top and bottom gap is small', () => {
148
+ // Trigger at y=700: gapIf.top=700-80-0=620, gapIf.bottom=800-80-700=20
149
+ // gapIf.top < 0? No. gapIf.bottom * 1.5 = 30 > 620? No → TOP
150
+ // style.top = originFor.top + 0 - 80 = 700 - 80 = 620px
151
+ const style = fitOnScreen(null, makeEvent(200, 700), { positionX: AUTO, positionY: AUTO }, true);
152
+
153
+ expect(style.top).toBe('620px');
154
+ });
155
+
156
+ it('chooses BOTTOM when bottom gap is more than 1.5x top gap', () => {
157
+ // Trigger at y=50: gapIf.top = 50-80-0=-30 < 0 → BOTTOM
158
+ // style.top = 50px
159
+ const style = fitOnScreen(null, makeEvent(200, 50), { positionX: AUTO, positionY: AUTO }, true);
160
+
161
+ expect(style.top).toBe('50px');
162
+ });
163
+ });
164
+
165
+ describe('explicit vertical positioning', () => {
166
+ it('respects explicit TOP position', () => {
167
+ // Trigger at y=400: originFor.top = 400, style.top = 400 + 0 - 80 = 320px
168
+ const style = fitOnScreen(null, makeEvent(200, 400), { positionX: AUTO, positionY: TOP }, true);
169
+
170
+ expect(style.top).toBe('320px');
171
+ });
172
+
173
+ it('respects explicit BOTTOM position', () => {
174
+ // Trigger at y=400: originFor.bottom = 400, style.top = 400 - 0 = 400px
175
+ const style = fitOnScreen(null, makeEvent(200, 400), { positionX: AUTO, positionY: BOTTOM }, true);
176
+
177
+ expect(style.top).toBe('400px');
178
+ });
179
+
180
+ it('falls back from TOP to BOTTOM when top gap is negative', () => {
181
+ // Trigger at y=30: gapIf.top = originFor.bottom - content.height - screen.top = 30 - 80 - 0 = -50 < 0
182
+ // → positionY switches from TOP to BOTTOM
183
+ // style.top = originFor.bottom - fudgeY = 30 - 0 = 30px
184
+ const style = fitOnScreen(null, makeEvent(200, 30), { positionX: AUTO, positionY: TOP }, true);
185
+
186
+ expect(style.top).toBe('30px');
187
+ });
188
+ });
189
+
190
+ describe('fudge offsets', () => {
191
+ it('applies fudgeX to left position', () => {
192
+ // Trigger at x=200, fudgeX=10, positionX=LEFT: style.left = 200 - 10 = 190px
193
+ const style = fitOnScreen(null, makeEvent(200, 400), {
194
+ positionX: LEFT, positionY: AUTO, fudgeX: 10
195
+ }, true);
196
+
197
+ expect(style.left).toBe('190px');
198
+ });
199
+
200
+ it('applies fudgeY to top position', () => {
201
+ // Trigger at y=700 → TOP: style.top = 700 + 5 - 80 = 625px
202
+ const style = fitOnScreen(null, makeEvent(200, 700), {
203
+ positionX: AUTO, positionY: TOP, fudgeY: 5
204
+ }, true);
205
+
206
+ expect(style.top).toBe('625px');
207
+ });
208
+ });
209
+
210
+ describe('mIDDLE vertical positioning', () => {
211
+ it('uses MIDDLE position when there is enough room', () => {
212
+ // Trigger at y=400: originFor.middle = 400
213
+ // gapIf.middle = min(400-40-0, 800-40-400) = min(360, 360) = 360 >= 0 → stays MIDDLE
214
+ // switch MIDDLE == CENTER case: style.top = (400+400)/2 + 0 - 80 = 400-80 = 320px
215
+ const style = fitOnScreen(null, makeEvent(200, 400), { positionX: AUTO, positionY: MIDDLE }, true);
216
+
217
+ expect(style.top).toBe('320px');
218
+ });
219
+
220
+ it('falls back from MIDDLE to AUTO when middle gap is negative', () => {
221
+ // Trigger at y=30: originFor.middle = 30
222
+ // gapIf.middle = min(30-40-0, 800-40-30) = min(-10, 730) = -10 < 0 → positionY = AUTO
223
+ // Auto: gapIf.top = 30-80-0=-50 < 0 → BOTTOM
224
+ // style.top = originFor.bottom - 0 = 30px
225
+ const style = fitOnScreen(null, makeEvent(200, 30), { positionX: AUTO, positionY: MIDDLE }, true);
226
+
227
+ expect(style.top).toBe('30px');
228
+ });
229
+ });
230
+
231
+ it('always sets position to absolute', () => {
232
+ const style = fitOnScreen(null, makeEvent(200, 400), {}, true);
233
+
234
+ expect(style.position).toBe('absolute');
235
+ });
236
+ });
237
+ });
@@ -0,0 +1,346 @@
1
+ import Queue from '@shell/utils/queue';
2
+ import {
3
+ allHash,
4
+ allHashSettled,
5
+ deferred,
6
+ eachLimit,
7
+ setPromiseResult,
8
+ } from '@shell/utils/promise';
9
+
10
+ describe('queue', () => {
11
+ describe('getLength', () => {
12
+ it.each([
13
+ {
14
+ desc: '0 for an empty queue', enqueueCount: 0, dequeueCount: 0, expected: 0
15
+ },
16
+ {
17
+ desc: '1 after one enqueue', enqueueCount: 1, dequeueCount: 0, expected: 1
18
+ },
19
+ {
20
+ desc: 'decrements after dequeue', enqueueCount: 2, dequeueCount: 1, expected: 1
21
+ },
22
+ ])('returns $desc', ({ enqueueCount, dequeueCount, expected }) => {
23
+ const q = new Queue();
24
+
25
+ for (let i = 0; i < enqueueCount; i++) {
26
+ q.enqueue(`item-${ i }`);
27
+ }
28
+ for (let i = 0; i < dequeueCount; i++) {
29
+ q.dequeue();
30
+ }
31
+
32
+ expect(q.getLength()).toStrictEqual(expected);
33
+ });
34
+ });
35
+
36
+ describe('isEmpty', () => {
37
+ it.each([
38
+ {
39
+ desc: 'true when queue is empty', enqueueCount: 0, dequeueCount: 0, expected: true
40
+ },
41
+ {
42
+ desc: 'false after enqueueing an item', enqueueCount: 1, dequeueCount: 0, expected: false
43
+ },
44
+ {
45
+ desc: 'true after all items are dequeued', enqueueCount: 1, dequeueCount: 1, expected: true
46
+ },
47
+ ])('returns $desc', ({ enqueueCount, dequeueCount, expected }) => {
48
+ const q = new Queue();
49
+
50
+ for (let i = 0; i < enqueueCount; i++) {
51
+ q.enqueue('x');
52
+ }
53
+ for (let i = 0; i < dequeueCount; i++) {
54
+ q.dequeue();
55
+ }
56
+
57
+ expect(q.isEmpty()).toStrictEqual(expected);
58
+ });
59
+ });
60
+
61
+ describe('enqueue / dequeue', () => {
62
+ it('dequeues items in FIFO order', () => {
63
+ const q = new Queue();
64
+
65
+ q.enqueue('first');
66
+ q.enqueue('second');
67
+ q.enqueue('third');
68
+
69
+ expect(q.dequeue()).toStrictEqual('first');
70
+ expect(q.dequeue()).toStrictEqual('second');
71
+ expect(q.dequeue()).toStrictEqual('third');
72
+ });
73
+
74
+ it('returns undefined when dequeuing from an empty queue', () => {
75
+ const q = new Queue();
76
+
77
+ expect(q.dequeue()).toBeUndefined();
78
+ });
79
+
80
+ it('works with objects as items', () => {
81
+ const q = new Queue();
82
+ const item = { key: 'value' };
83
+
84
+ q.enqueue(item);
85
+ expect(q.dequeue()).toStrictEqual(item);
86
+ });
87
+ });
88
+
89
+ describe('peek', () => {
90
+ it.each([
91
+ {
92
+ desc: 'the front item without removing it', items: ['a', 'b'], expected: 'a'
93
+ },
94
+ {
95
+ desc: 'undefined when queue is empty', items: [] as string[], expected: undefined
96
+ },
97
+ ])('returns $desc', ({ items, expected }) => {
98
+ const q = new Queue();
99
+
100
+ items.forEach((item) => q.enqueue(item));
101
+
102
+ expect(q.peek()).toStrictEqual(expected);
103
+ expect(q.getLength()).toStrictEqual(items.length);
104
+ });
105
+ });
106
+
107
+ describe('clear', () => {
108
+ it('empties the queue', () => {
109
+ const q = new Queue();
110
+
111
+ q.enqueue('a');
112
+ q.enqueue('b');
113
+ q.clear();
114
+
115
+ expect(q.isEmpty()).toStrictEqual(true);
116
+ expect(q.getLength()).toStrictEqual(0);
117
+ });
118
+
119
+ it('allows re-use after clear', () => {
120
+ const q = new Queue();
121
+
122
+ q.enqueue('a');
123
+ q.clear();
124
+ q.enqueue('b');
125
+
126
+ expect(q.dequeue()).toStrictEqual('b');
127
+ });
128
+ });
129
+ });
130
+
131
+ describe('allHash', () => {
132
+ it('resolves a hash of resolved promises', async() => {
133
+ const result = await allHash({
134
+ a: Promise.resolve(1),
135
+ b: Promise.resolve(2),
136
+ c: Promise.resolve(3),
137
+ });
138
+
139
+ expect(result).toStrictEqual({
140
+ a: 1,
141
+ b: 2,
142
+ c: 3,
143
+ });
144
+ });
145
+
146
+ it('rejects if any promise in the hash rejects', async() => {
147
+ await expect(
148
+ allHash({
149
+ a: Promise.resolve(1),
150
+ b: Promise.reject(new Error('fail')),
151
+ })
152
+ ).rejects.toThrow('fail');
153
+ });
154
+
155
+ it('returns an empty object for an empty hash', async() => {
156
+ const result = await allHash({});
157
+
158
+ expect(result).toStrictEqual({});
159
+ });
160
+
161
+ it('preserves key ordering from the input hash', async() => {
162
+ const result = await allHash({
163
+ z: Promise.resolve('z-val'),
164
+ a: Promise.resolve('a-val'),
165
+ });
166
+
167
+ expect(Object.keys(result)).toStrictEqual(['z', 'a']);
168
+ expect(result).toStrictEqual({ z: 'z-val', a: 'a-val' });
169
+ });
170
+
171
+ it('resolves non-promise values in the hash', async() => {
172
+ const result = await allHash({ x: 42 });
173
+
174
+ expect(result).toStrictEqual({ x: 42 });
175
+ });
176
+ });
177
+
178
+ describe('allHashSettled', () => {
179
+ it('resolves with fulfilled/rejected statuses for each key', async() => {
180
+ const result = await allHashSettled({
181
+ ok: Promise.resolve('success'),
182
+ err: Promise.reject(new Error('oops')),
183
+ });
184
+
185
+ expect(result.ok).toStrictEqual({ status: 'fulfilled', value: 'success' });
186
+ expect(result.err.status).toStrictEqual('rejected');
187
+ expect((result.err as PromiseRejectedResult).reason.message).toStrictEqual('oops');
188
+ });
189
+
190
+ it('returns an empty object for an empty hash', async() => {
191
+ const result = await allHashSettled({});
192
+
193
+ expect(result).toStrictEqual({});
194
+ });
195
+
196
+ it('never rejects — always resolves even when all promises fail', async() => {
197
+ await expect(
198
+ allHashSettled({
199
+ a: Promise.reject(new Error('a-fail')),
200
+ b: Promise.reject(new Error('b-fail')),
201
+ })
202
+ ).resolves.toBeDefined();
203
+ });
204
+ });
205
+
206
+ describe('eachLimit', () => {
207
+ it('processes all items and returns results in index order', async() => {
208
+ const items = [1, 2, 3, 4, 5];
209
+ const result = await eachLimit(items, 2, (item: number) => Promise.resolve(item * 10));
210
+
211
+ expect(result).toStrictEqual([10, 20, 30, 40, 50]);
212
+ });
213
+
214
+ it('returns an empty array for empty input', async() => {
215
+ const result = await eachLimit([], 3, () => Promise.resolve('x'));
216
+
217
+ expect(result).toStrictEqual([]);
218
+ });
219
+
220
+ it('rejects if any iterator rejects', async() => {
221
+ await expect(
222
+ eachLimit([1, 2, 3], 2, (item: number) => {
223
+ if (item === 2) {
224
+ return Promise.reject(new Error('item-2-failed'));
225
+ }
226
+
227
+ return Promise.resolve(item);
228
+ })
229
+ ).rejects.toThrow('item-2-failed');
230
+ });
231
+
232
+ it('handles limit larger than the number of items', async() => {
233
+ const items = ['a', 'b'];
234
+ const result = await eachLimit(items, 100, (item: string) => Promise.resolve(`done-${ item }`));
235
+
236
+ expect(result).toStrictEqual(['done-a', 'done-b']);
237
+ });
238
+
239
+ it('handles limit of 1 (sequential processing)', async() => {
240
+ const order: number[] = [];
241
+ const items = [1, 2, 3];
242
+
243
+ await eachLimit(items, 1, (item: number) => {
244
+ order.push(item);
245
+
246
+ return Promise.resolve(item);
247
+ });
248
+
249
+ expect(order).toStrictEqual([1, 2, 3]);
250
+ });
251
+
252
+ it('respects the concurrency limit', async() => {
253
+ let concurrent = 0;
254
+ let maxConcurrent = 0;
255
+ const limit = 2;
256
+ const items = [1, 2, 3, 4, 5];
257
+
258
+ await eachLimit(items, limit, (item: number) => {
259
+ concurrent++;
260
+ if (concurrent > maxConcurrent) {
261
+ maxConcurrent = concurrent;
262
+ }
263
+
264
+ return new Promise<number>((resolve) => {
265
+ setTimeout(() => {
266
+ concurrent--;
267
+ resolve(item);
268
+ }, 5);
269
+ });
270
+ });
271
+
272
+ expect(maxConcurrent).toStrictEqual(limit);
273
+ });
274
+
275
+ it('passes the item index as the second argument to the iterator', async() => {
276
+ const indices: number[] = [];
277
+
278
+ await eachLimit(['x', 'y', 'z'], 3, (_item: string, idx: number) => {
279
+ indices.push(idx);
280
+
281
+ return Promise.resolve(idx);
282
+ });
283
+
284
+ expect(indices).toStrictEqual([0, 1, 2]);
285
+ });
286
+ });
287
+
288
+ describe('deferred', () => {
289
+ it('returns an object with promise, resolve, and reject', () => {
290
+ const d = deferred('test');
291
+
292
+ expect(d.promise).toBeInstanceOf(Promise);
293
+ expect(typeof d.resolve).toStrictEqual('function');
294
+ expect(typeof d.reject).toStrictEqual('function');
295
+ });
296
+
297
+ it('resolves the promise when resolve is called', async() => {
298
+ const d = deferred('resolve-test');
299
+
300
+ d.resolve('resolved-value');
301
+
302
+ await expect(d.promise).resolves.toStrictEqual('resolved-value');
303
+ });
304
+
305
+ it('rejects the promise when reject is called', async() => {
306
+ const d = deferred('reject-test');
307
+
308
+ d.reject(new Error('deferred-error'));
309
+
310
+ await expect(d.promise).rejects.toThrow('deferred-error');
311
+ });
312
+ });
313
+
314
+ describe('setPromiseResult', () => {
315
+ it('sets the resolved value on the target object property', async() => {
316
+ const obj: { result?: string } = {};
317
+
318
+ setPromiseResult(Promise.resolve('hello'), obj, 'result', 'test label');
319
+ await new Promise((resolve) => setTimeout(resolve, 0));
320
+
321
+ expect(obj.result).toStrictEqual('hello');
322
+ });
323
+
324
+ it('does not throw when the promise rejects — logs a warning instead', async() => {
325
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
326
+ const obj: { value?: string } = {};
327
+
328
+ setPromiseResult(Promise.reject(new Error('boom')), obj, 'value', 'failing label');
329
+ await new Promise((resolve) => setTimeout(resolve, 0));
330
+
331
+ expect(obj.value).toBeUndefined();
332
+ expect(warnSpy).toHaveBeenCalledWith('Failed to: ', 'failing label', expect.any(Error));
333
+ warnSpy.mockRestore();
334
+ });
335
+
336
+ it('leaves the object property unchanged when the promise rejects', async() => {
337
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
338
+ const obj: { data: string } = { data: 'original' };
339
+
340
+ setPromiseResult(Promise.reject(new Error('err')), obj, 'data', 'test');
341
+ await new Promise((resolve) => setTimeout(resolve, 0));
342
+
343
+ expect(obj.data).toStrictEqual('original');
344
+ jest.restoreAllMocks();
345
+ });
346
+ });
@@ -1,4 +1,4 @@
1
- import { getHostedProviders, isHostedProvider } from '../provider';
1
+ import { getHostedProviders, isHostedProvider, getCAPIProviders, isCAPIProvider } from '../provider';
2
2
  import { ClusterProvisionerContext, IClusterProvisioner } from '@shell/core/types';
3
3
 
4
4
  const DEFAULT_CONTEXT = {
@@ -13,6 +13,8 @@ const MOCK_PROVIDERS: IClusterProvisioner[] = [
13
13
  { id: 'EKS', group: 'hosted' } as IClusterProvisioner,
14
14
  { id: 'GKE', group: 'hosted' } as IClusterProvisioner,
15
15
  { id: 'alibaba', group: 'hosted' } as IClusterProvisioner,
16
+ { id: 'capa', group: 'capi' } as IClusterProvisioner,
17
+ { id: 'capv', group: 'capi' } as IClusterProvisioner,
16
18
  { id: 'other', group: 'other' } as IClusterProvisioner,
17
19
  ];
18
20
 
@@ -42,6 +44,22 @@ describe('utils/provider', () => {
42
44
  expect(context.$extension.getProviders).toHaveBeenCalledWith(context);
43
45
  });
44
46
 
47
+ it('should return capi providers when context.$extension is defined', () => {
48
+ const context = {
49
+ ...DEFAULT_CONTEXT,
50
+ $extension: { getProviders: jest.fn().mockReturnValue(MOCK_PROVIDERS) }
51
+ } as unknown as ClusterProvisionerContext;
52
+
53
+ const result = getCAPIProviders(context);
54
+
55
+ expect(result).toStrictEqual([
56
+ { id: 'capa', group: 'capi' },
57
+ { id: 'capv', group: 'capi' },
58
+ ]);
59
+
60
+ expect(context.$extension.getProviders).toHaveBeenCalledWith(context);
61
+ });
62
+
45
63
  it('should return an empty array if getProviders returns null', () => {
46
64
  const context = {
47
65
  ...DEFAULT_CONTEXT,
@@ -85,10 +103,42 @@ describe('utils/provider', () => {
85
103
 
86
104
  expect(isHostedProvider(context, 'AKS')).toBe(true);
87
105
  expect(isHostedProvider(context, 'eks')).toBe(true);
106
+ expect(isHostedProvider(context, 'capa')).toBe(false);
88
107
  expect(isHostedProvider(context, 'different')).toBe(false); // case-insensitive check
89
108
  expect(isHostedProvider(context, 'other')).toBe(false); // case-insensitive check
90
109
  });
91
110
 
111
+ it('should return false if there are no hosted providers', () => {
112
+ const context = { ...DEFAULT_CONTEXT, $extension: { getProviders: jest.fn().mockReturnValue([]) } } as ClusterProvisionerContext;
113
+
114
+ expect(isHostedProvider(context, 'prov1')).toBe(false);
115
+ });
116
+ });
117
+ describe('isCAPIProvider', () => {
118
+ it('should return false if provisioner is not provided', () => {
119
+ const context = {
120
+ ...DEFAULT_CONTEXT,
121
+ $extension: { getProviders: jest.fn().mockReturnValue(MOCK_PROVIDERS) }
122
+ } as ClusterProvisionerContext;
123
+
124
+ expect(isCAPIProvider(context, '')).toBe(false);
125
+ expect(isCAPIProvider(context, undefined as any)).toBe(false);
126
+ expect(isCAPIProvider(context, null as any)).toBe(false);
127
+ });
128
+
129
+ it('should return true only if provisioner is in the list of CAPI providers', () => {
130
+ const context = {
131
+ ...DEFAULT_CONTEXT,
132
+ $extension: { getProviders: jest.fn().mockReturnValue(MOCK_PROVIDERS) }
133
+ } as ClusterProvisionerContext;
134
+
135
+ expect(isCAPIProvider(context, 'capa')).toBe(true);
136
+ expect(isCAPIProvider(context, 'capv')).toBe(true);
137
+ expect(isCAPIProvider(context, 'eks')).toBe(false);
138
+ expect(isCAPIProvider(context, 'different')).toBe(false); // case-insensitive check
139
+ expect(isCAPIProvider(context, 'other')).toBe(false); // case-insensitive check
140
+ });
141
+
92
142
  it('should return false if there are no hosted providers', () => {
93
143
  const context = { ...DEFAULT_CONTEXT, $extension: { getProviders: jest.fn().mockReturnValue([]) } } as ClusterProvisionerContext;
94
144