@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
@@ -33,9 +33,12 @@
33
33
  * <p>Section content here</p>
34
34
  * </RcSection>
35
35
  */
36
- import { computed, inject, provide, type Ref } from 'vue';
36
+ import {
37
+ computed, inject, provide, useTemplateRef, type Ref
38
+ } from 'vue';
37
39
  import RcButton from '@components/RcButton/RcButton.vue';
38
40
  import RcIcon from '@components/RcIcon/RcIcon.vue';
41
+ import { useInSummary } from '@shell/components/TableOfContents/composables';
39
42
  import type { RcSectionProps, SectionBackground } from './types';
40
43
 
41
44
  const RC_SECTION_BG_KEY = 'rc-section-background';
@@ -58,6 +61,24 @@ provide(RC_SECTION_BG_KEY, resolvedBackground);
58
61
 
59
62
  const expanded = defineModel<boolean>('expanded', { default: true });
60
63
 
64
+ // Expose summary, name, and a display label on the component public instance so
65
+ // TOC discovery can access component
66
+ const displayTitle = computed(() => props.title);
67
+
68
+ const name = 'RcSection';
69
+
70
+ // Register this section in form summary/table-of-contents context (if provided)
71
+ const sectionRef = useTemplateRef<HTMLElement>('rc-section-summarized-container');
72
+ const { summary } = useInSummary({
73
+ label: displayTitle,
74
+ scrollTo: () => sectionRef.value?.scrollIntoView(true),
75
+ elementRef: sectionRef,
76
+ });
77
+
78
+ defineExpose({
79
+ summary, displayTitle, name
80
+ });
81
+
61
82
  const hasHeader = computed(() => {
62
83
  return props.mode === 'with-header';
63
84
  });
@@ -84,7 +105,10 @@ function toggle() {
84
105
  </script>
85
106
 
86
107
  <template>
87
- <div :class="sectionClass">
108
+ <div
109
+ ref="rc-section-summarized-container"
110
+ :class="sectionClass"
111
+ >
88
112
  <div
89
113
  v-if="hasHeader"
90
114
  class="section-header"
@@ -203,7 +227,8 @@ function toggle() {
203
227
  color: var(--body-text, inherit);
204
228
  }
205
229
 
206
- button.btn-medium.toggle-button {
230
+ // TODO: Considering removing specificity override when RcButton sizes are refactored (#18062)
231
+ .left-wrapper :deep(button.toggle-button.btn-medium:not(.btn-sm)) {
207
232
  flex-shrink: 0;
208
233
  font-size: 16px;
209
234
  color: var(--body-text, inherit);
@@ -1,4 +1,4 @@
1
- FROM registry.suse.com/bci/bci-base:15.5
1
+ FROM registry.suse.com/bci/bci-base:15.7
2
2
 
3
3
  RUN zypper -n install nginx jq
4
4
 
@@ -240,7 +240,8 @@ clone_repo_test_extension_build "rancher" "kubewarden-ui" "kubewarden"
240
240
  # clone_repo_test_extension_build "rancher" "elemental-ui" "elemental"
241
241
  clone_repo_test_extension_build "neuvector" "manager-ext" "neuvector-ui-ext"
242
242
  # clone_repo_test_extension_build "StackVista" "rancher-extension-stackstate" "observability"
243
- clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
243
+ # Uncomment once https://github.com/harvester/harvester/issues/10691 is resolved
244
+ #clone_repo_test_extension_build "harvester" "harvester-ui-extension" "harvester"
244
245
  clone_repo_test_extension_build "rancher" "ali-ui" "ali"
245
246
  clone_repo_test_extension_build "rancher" "virtual-clusters-ui" "virtual-clusters"
246
247
  # clone_repo_test_extension_build "rancher" "rancher-ai-ui" "rancher-ai-ui"
@@ -1,7 +1,7 @@
1
1
  import { CATALOG } from '@shell/config/types';
2
2
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
3
3
  import {
4
- state, getters, actions, mutations, filterAndArrangeCharts
4
+ state, getters, actions, mutations, filterAndArrangeCharts, isRancherRepo, compatibleVersionsFor, getPermittedOSs
5
5
  } from '../catalog';
6
6
  import { createStore } from 'vuex';
7
7
 
@@ -509,4 +509,118 @@ describe('catalog', () => {
509
509
  });
510
510
  });
511
511
  });
512
+
513
+ describe('isRancherRepo', () => {
514
+ it('should return true if chart.isRancherRepo is true', () => {
515
+ const chart = { isRancherRepo: true } as any;
516
+
517
+ expect(isRancherRepo(null, chart)).toBe(true);
518
+ });
519
+
520
+ it('should return true if repo.isRancherSource is true', () => {
521
+ const repo = { isRancherSource: true } as any;
522
+
523
+ expect(isRancherRepo(repo, null)).toBe(true);
524
+ });
525
+
526
+ it('should return false if repo is not a rancher source', () => {
527
+ const repo = { isRancherSource: false } as any;
528
+
529
+ expect(isRancherRepo(repo, null)).toBe(false);
530
+ });
531
+ });
532
+
533
+ describe('compatibleVersionsFor', () => {
534
+ it('should allow versions if no OS constraint is provided', () => {
535
+ const chart = { versions: [{ version: '1.0.0' }] } as any;
536
+ const versions = compatibleVersionsFor(chart, undefined);
537
+
538
+ expect(versions).toHaveLength(1);
539
+ });
540
+
541
+ it('should allow windows nodes if permitted-os includes windows', () => {
542
+ const chart = {
543
+ versions: [{
544
+ version: '1.0.0',
545
+ annotations: { 'catalog.cattle.io/permits-os': 'linux,windows' }
546
+ }]
547
+ } as any;
548
+ const versions = compatibleVersionsFor(chart, 'windows');
549
+
550
+ expect(versions).toHaveLength(1);
551
+ });
552
+
553
+ it('should block windows nodes if permitted-os does not include windows', () => {
554
+ const chart = {
555
+ versions: [{
556
+ version: '1.0.0',
557
+ annotations: { 'catalog.cattle.io/permits-os': 'linux' }
558
+ }]
559
+ } as any;
560
+ const versions = compatibleVersionsFor(chart, 'windows');
561
+
562
+ expect(versions).toHaveLength(0);
563
+ });
564
+
565
+ it('should fallback to LINUX for rancher repos and block windows nodes', () => {
566
+ const chart = { isRancherRepo: true, versions: [{ version: '1.0.0' }] } as any;
567
+ const versions = compatibleVersionsFor(chart, 'windows');
568
+
569
+ expect(versions).toHaveLength(0);
570
+ });
571
+
572
+ it('should not fallback to LINUX for non-rancher repos and allow windows nodes', () => {
573
+ const chart = { isRancherRepo: false, versions: [{ version: '1.0.0' }] } as any;
574
+ const versions = compatibleVersionsFor(chart, 'windows');
575
+
576
+ expect(versions).toHaveLength(1);
577
+ });
578
+ });
579
+
580
+ describe('getPermittedOSs', () => {
581
+ it('should return explicitly permitted OSs when the annotation is present on a Rancher repo', () => {
582
+ const annotations = { [CATALOG_ANNOTATIONS.PERMITTED_OS]: 'linux,windows' };
583
+ const result = getPermittedOSs(annotations, true);
584
+
585
+ expect(result).toHaveLength(2);
586
+ expect(result).toContain('linux');
587
+ expect(result).toContain('windows');
588
+ });
589
+
590
+ it('should return explicitly permitted OSs when the annotation is present on a non-Rancher repo', () => {
591
+ const annotations = { [CATALOG_ANNOTATIONS.PERMITTED_OS]: 'linux' };
592
+ const result = getPermittedOSs(annotations, false);
593
+
594
+ expect(result).toHaveLength(1);
595
+ expect(result).toContain('linux');
596
+ });
597
+
598
+ it('should fallback to linux if the annotation is missing on a Rancher repo', () => {
599
+ const annotations = {};
600
+ const result = getPermittedOSs(annotations, true);
601
+
602
+ expect(result).toHaveLength(1);
603
+ expect(result).toContain('linux');
604
+ });
605
+
606
+ it('should return an empty array (no restrictions) if the annotation is missing on a non-Rancher repo', () => {
607
+ const annotations = {};
608
+ const result = getPermittedOSs(annotations, false);
609
+
610
+ expect(result).toHaveLength(0);
611
+ });
612
+
613
+ it('should handle undefined annotations safely for Rancher repos', () => {
614
+ const result = getPermittedOSs(undefined, true);
615
+
616
+ expect(result).toHaveLength(1);
617
+ expect(result).toContain('linux');
618
+ });
619
+
620
+ it('should handle undefined annotations safely for non-Rancher repos', () => {
621
+ const result = getPermittedOSs(undefined, false);
622
+
623
+ expect(result).toHaveLength(0);
624
+ });
625
+ });
512
626
  });
@@ -0,0 +1,434 @@
1
+ import { getters, mutations, state } from '@shell/store/notifications';
2
+ import { NotificationLevel, Notification, StoredNotification } from '@shell/types/notifications';
3
+
4
+ jest.mock('@shell/utils/string', () => ({ randomStr: jest.fn(() => 'mock-random-id') }));
5
+ jest.mock('@shell/utils/crypto', () => ({ md5: jest.fn((v: string) => `hash-${ v }`) }));
6
+ jest.mock('@shell/utils/crypto/encryption', () => ({
7
+ encrypt: jest.fn(),
8
+ decrypt: jest.fn(),
9
+ deriveKey: jest.fn(),
10
+ }));
11
+
12
+ function makeNotification(overrides: Partial<Notification> = {}): Notification {
13
+ return {
14
+ id: 'test-id',
15
+ title: 'test notification',
16
+ level: NotificationLevel.Info,
17
+ ...overrides,
18
+ };
19
+ }
20
+
21
+ function makeStoredNotification(overrides: Partial<StoredNotification> = {}): StoredNotification {
22
+ return {
23
+ ...makeNotification(),
24
+ read: false,
25
+ created: new Date('2026-01-01'),
26
+ ...overrides,
27
+ };
28
+ }
29
+
30
+ describe('notifications store', () => {
31
+ describe('state', () => {
32
+ it('returns initial state with empty notifications', () => {
33
+ const result = state();
34
+
35
+ expect(result.notifications).toStrictEqual([]);
36
+ expect(result.localStorageKey).toStrictEqual('');
37
+ expect(result.userId).toStrictEqual('');
38
+ expect(result.encryptionKey).toBeUndefined();
39
+ });
40
+ });
41
+
42
+ describe('getters', () => {
43
+ let mockState: ReturnType<typeof state>;
44
+ const hidden = makeStoredNotification({
45
+ id: 'h1', level: NotificationLevel.Hidden, read: false
46
+ });
47
+ const unreadVisible = makeStoredNotification({
48
+ id: 'u1', level: NotificationLevel.Info, read: false
49
+ });
50
+ const readVisible = makeStoredNotification({
51
+ id: 'r1', level: NotificationLevel.Warning, read: true
52
+ });
53
+
54
+ beforeEach(() => {
55
+ mockState = state();
56
+ mockState.notifications = [hidden, unreadVisible, readVisible];
57
+ });
58
+
59
+ describe('all', () => {
60
+ it('returns all notifications including hidden', () => {
61
+ expect(getters.all(mockState)).toStrictEqual([hidden, unreadVisible, readVisible]);
62
+ });
63
+
64
+ it('returns empty array when there are no notifications', () => {
65
+ mockState.notifications = [];
66
+ expect(getters.all(mockState)).toStrictEqual([]);
67
+ });
68
+ });
69
+
70
+ describe('visible', () => {
71
+ it('returns only non-hidden notifications', () => {
72
+ expect(getters.visible(mockState)).toStrictEqual([unreadVisible, readVisible]);
73
+ });
74
+
75
+ it('returns empty array when all notifications are hidden', () => {
76
+ mockState.notifications = [hidden];
77
+ expect(getters.visible(mockState)).toStrictEqual([]);
78
+ });
79
+ });
80
+
81
+ describe('hidden', () => {
82
+ it('returns only hidden notifications', () => {
83
+ expect(getters.hidden(mockState)).toStrictEqual([hidden]);
84
+ });
85
+
86
+ it('returns empty array when no notifications are hidden', () => {
87
+ mockState.notifications = [unreadVisible, readVisible];
88
+ expect(getters.hidden(mockState)).toStrictEqual([]);
89
+ });
90
+ });
91
+
92
+ describe('item', () => {
93
+ it('returns a function that finds a notification by id', () => {
94
+ const itemFn = getters.item(mockState);
95
+
96
+ expect(itemFn('u1')).toStrictEqual(unreadVisible);
97
+ });
98
+
99
+ it('returns undefined when the notification id does not exist', () => {
100
+ const itemFn = getters.item(mockState);
101
+
102
+ expect(itemFn('nonexistent')).toBeUndefined();
103
+ });
104
+ });
105
+
106
+ describe('unreadCount', () => {
107
+ it('counts only unread visible notifications', () => {
108
+ expect(getters.unreadCount(mockState)).toStrictEqual(1);
109
+ });
110
+
111
+ it('returns zero when all visible notifications are read', () => {
112
+ mockState.notifications = [readVisible];
113
+ expect(getters.unreadCount(mockState)).toStrictEqual(0);
114
+ });
115
+
116
+ it('excludes hidden notifications from the unread count', () => {
117
+ mockState.notifications = [hidden]; // hidden and unread
118
+ expect(getters.unreadCount(mockState)).toStrictEqual(0);
119
+ });
120
+ });
121
+
122
+ describe('localStorageKey', () => {
123
+ it('returns the localStorageKey from state', () => {
124
+ mockState.localStorageKey = 'rancher-notifications-abc';
125
+ expect(getters.localStorageKey(mockState)).toStrictEqual('rancher-notifications-abc');
126
+ });
127
+ });
128
+
129
+ describe('userId', () => {
130
+ it('returns the userId from state', () => {
131
+ mockState.userId = 'user-123';
132
+ expect(getters.userId(mockState)).toStrictEqual('user-123');
133
+ });
134
+ });
135
+
136
+ describe('encryptionKey', () => {
137
+ it('returns undefined by default', () => {
138
+ expect(getters.encryptionKey(mockState)).toBeUndefined();
139
+ });
140
+
141
+ it('returns the encryptionKey when set', () => {
142
+ const mockKey = {} as CryptoKey;
143
+
144
+ mockState.encryptionKey = mockKey;
145
+ expect(getters.encryptionKey(mockState)).toStrictEqual(mockKey);
146
+ });
147
+ });
148
+ });
149
+
150
+ describe('mutations', () => {
151
+ let mockState: ReturnType<typeof state>;
152
+
153
+ beforeEach(() => {
154
+ mockState = state();
155
+ mockState.localStorageKey = 'rancher-notifications-test';
156
+ localStorage.clear();
157
+ });
158
+
159
+ describe('localStorageKey', () => {
160
+ it('sets localStorageKey with the store prefix', () => {
161
+ mutations.localStorageKey(mockState, 'abc123');
162
+ expect(mockState.localStorageKey).toStrictEqual('rancher-notifications-abc123');
163
+ });
164
+ });
165
+
166
+ describe('userId', () => {
167
+ it('sets the userId on state', () => {
168
+ mutations.userId(mockState, 'user-456');
169
+ expect(mockState.userId).toStrictEqual('user-456');
170
+ });
171
+ });
172
+
173
+ describe('encryptionKey', () => {
174
+ it('sets the encryptionKey on state', () => {
175
+ const mockKey = {} as CryptoKey;
176
+
177
+ mutations.encryptionKey(mockState, mockKey);
178
+ expect(mockState.encryptionKey).toStrictEqual(mockKey);
179
+ });
180
+ });
181
+
182
+ describe('load', () => {
183
+ it('replaces all notifications with the provided list', () => {
184
+ mockState.notifications = [makeStoredNotification({ id: 'old' })];
185
+ const newNotifications = [makeStoredNotification({ id: 'n1' }), makeStoredNotification({ id: 'n2' })];
186
+
187
+ mutations.load(mockState, newNotifications);
188
+ expect(mockState.notifications).toStrictEqual(newNotifications);
189
+ });
190
+
191
+ it('clears notifications when given an empty array', () => {
192
+ mockState.notifications = [makeStoredNotification({ id: 'old' })];
193
+ mutations.load(mockState, []);
194
+ expect(mockState.notifications).toStrictEqual([]);
195
+ });
196
+ });
197
+
198
+ describe('add', () => {
199
+ it('adds a notification with the provided id to the front of the list', () => {
200
+ mutations.add(mockState, makeNotification({ id: 'custom-id' }));
201
+ expect(mockState.notifications[0].id).toStrictEqual('custom-id');
202
+ expect(mockState.notifications).toHaveLength(1);
203
+ });
204
+
205
+ it('generates an id via randomStr when none is provided', () => {
206
+ mutations.add(mockState, makeNotification({ id: '' as any }));
207
+ expect(mockState.notifications[0].id).toStrictEqual('mock-random-id');
208
+ });
209
+
210
+ it('sets read to false on the newly added notification', () => {
211
+ mutations.add(mockState, makeNotification({ id: 'n1' }));
212
+ expect(mockState.notifications[0].read).toStrictEqual(false);
213
+ });
214
+
215
+ it('prepends the new notification before existing ones', () => {
216
+ mockState.notifications = [makeStoredNotification({ id: 'existing' })];
217
+ mutations.add(mockState, makeNotification({ id: 'new' }));
218
+ expect(mockState.notifications[0].id).toStrictEqual('new');
219
+ expect(mockState.notifications[1].id).toStrictEqual('existing');
220
+ });
221
+
222
+ it('does not add a notification whose id already exists', () => {
223
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
224
+
225
+ mockState.notifications = [makeStoredNotification({ id: 'dup' })];
226
+ mutations.add(mockState, makeNotification({ id: 'dup' }));
227
+ expect(mockState.notifications).toHaveLength(1);
228
+ consoleErrorSpy.mockRestore();
229
+ });
230
+
231
+ it('enforces the maximum of 50 notifications by removing the oldest', () => {
232
+ for (let i = 0; i < 50; i++) {
233
+ mockState.notifications.push(makeStoredNotification({ id: `n${ i }` }));
234
+ }
235
+ mutations.add(mockState, makeNotification({ id: 'newest' }));
236
+ expect(mockState.notifications).toHaveLength(50);
237
+ expect(mockState.notifications[0].id).toStrictEqual('newest');
238
+ });
239
+
240
+ it('removes the oldest notification from localStorage when the limit is exceeded', () => {
241
+ const removeItemSpy = jest.spyOn(Storage.prototype, 'removeItem');
242
+
243
+ for (let i = 0; i < 50; i++) {
244
+ mockState.notifications.push(makeStoredNotification({ id: `n${ i }` }));
245
+ }
246
+ mutations.add(mockState, makeNotification({ id: 'newest' }));
247
+ expect(removeItemSpy).toHaveBeenCalledWith('rancher-notifications-test-n49');
248
+ removeItemSpy.mockRestore();
249
+ });
250
+
251
+ it('syncs the notifications index to localStorage after adding', () => {
252
+ const setItemSpy = jest.spyOn(Storage.prototype, 'setItem');
253
+
254
+ mutations.add(mockState, makeNotification({ id: 'n1' }));
255
+ expect(setItemSpy).toHaveBeenCalledWith('rancher-notifications-test', expect.any(String));
256
+ setItemSpy.mockRestore();
257
+ });
258
+
259
+ it('stores id, created, read, and progress fields in the localStorage index', () => {
260
+ mutations.add(mockState, makeNotification({ id: 'n1', progress: 42 }));
261
+ const stored = JSON.parse(localStorage.getItem('rancher-notifications-test') || '[]');
262
+
263
+ expect(stored[0]).toStrictEqual({
264
+ id: 'n1',
265
+ created: expect.any(String),
266
+ read: false,
267
+ progress: 42,
268
+ });
269
+ });
270
+ });
271
+
272
+ describe('markRead', () => {
273
+ it('marks a notification as read', () => {
274
+ mockState.notifications = [makeStoredNotification({ id: 'n1', read: false })];
275
+ mutations.markRead(mockState, 'n1');
276
+ expect(mockState.notifications[0].read).toStrictEqual(true);
277
+ });
278
+
279
+ it('does not modify state when the notification is not found', () => {
280
+ mockState.notifications = [makeStoredNotification({ id: 'n1', read: false })];
281
+ mutations.markRead(mockState, 'nonexistent');
282
+ expect(mockState.notifications[0].read).toStrictEqual(false);
283
+ });
284
+
285
+ it('does not change a notification that is already read', () => {
286
+ mockState.notifications = [makeStoredNotification({ id: 'n1', read: true })];
287
+ mutations.markRead(mockState, 'n1');
288
+ expect(mockState.notifications[0].read).toStrictEqual(true);
289
+ });
290
+ });
291
+
292
+ describe('markUnread', () => {
293
+ it('marks a notification as unread', () => {
294
+ mockState.notifications = [makeStoredNotification({ id: 'n1', read: true })];
295
+ mutations.markUnread(mockState, 'n1');
296
+ expect(mockState.notifications[0].read).toStrictEqual(false);
297
+ });
298
+
299
+ it('does not modify state when the notification is not found', () => {
300
+ mockState.notifications = [makeStoredNotification({ id: 'n1', read: true })];
301
+ mutations.markUnread(mockState, 'nonexistent');
302
+ expect(mockState.notifications[0].read).toStrictEqual(true);
303
+ });
304
+
305
+ it('does not change a notification that is already unread', () => {
306
+ mockState.notifications = [makeStoredNotification({ id: 'n1', read: false })];
307
+ mutations.markUnread(mockState, 'n1');
308
+ expect(mockState.notifications[0].read).toStrictEqual(false);
309
+ });
310
+ });
311
+
312
+ describe('markAllRead', () => {
313
+ it('marks all visible unread notifications as read', () => {
314
+ mockState.notifications = [
315
+ makeStoredNotification({
316
+ id: 'n1', level: NotificationLevel.Info, read: false
317
+ }),
318
+ makeStoredNotification({
319
+ id: 'n2', level: NotificationLevel.Warning, read: false
320
+ }),
321
+ ];
322
+ mutations.markAllRead(mockState);
323
+ expect(mockState.notifications[0].read).toStrictEqual(true);
324
+ expect(mockState.notifications[1].read).toStrictEqual(true);
325
+ });
326
+
327
+ it('does not mark hidden notifications as read', () => {
328
+ mockState.notifications = [
329
+ makeStoredNotification({
330
+ id: 'h1', level: NotificationLevel.Hidden, read: false
331
+ }),
332
+ ];
333
+ mutations.markAllRead(mockState);
334
+ expect(mockState.notifications[0].read).toStrictEqual(false);
335
+ });
336
+
337
+ it('leaves already-read visible notifications unchanged', () => {
338
+ mockState.notifications = [
339
+ makeStoredNotification({
340
+ id: 'n1', level: NotificationLevel.Info, read: true
341
+ }),
342
+ ];
343
+ mutations.markAllRead(mockState);
344
+ expect(mockState.notifications[0].read).toStrictEqual(true);
345
+ });
346
+ });
347
+
348
+ describe('update', () => {
349
+ it('updates notification fields by id', () => {
350
+ mockState.notifications = [makeStoredNotification({ id: 'n1', title: 'original' })];
351
+ mutations.update(mockState, { id: 'n1', title: 'updated' } as any);
352
+ expect(mockState.notifications[0].title).toStrictEqual('updated');
353
+ });
354
+
355
+ it('preserves fields not included in the update', () => {
356
+ mockState.notifications = [makeStoredNotification({
357
+ id: 'n1',
358
+ title: 'original',
359
+ level: NotificationLevel.Success,
360
+ })];
361
+ mutations.update(mockState, { id: 'n1', title: 'updated' } as any);
362
+ expect(mockState.notifications[0].level).toStrictEqual(NotificationLevel.Success);
363
+ });
364
+
365
+ it('does not modify state when the id is not found', () => {
366
+ mockState.notifications = [makeStoredNotification({ id: 'n1', title: 'original' })];
367
+ mutations.update(mockState, { id: 'nonexistent', title: 'updated' } as any);
368
+ expect(mockState.notifications[0].title).toStrictEqual('original');
369
+ });
370
+
371
+ it('does nothing when no id is provided', () => {
372
+ mockState.notifications = [makeStoredNotification({ id: 'n1', title: 'original' })];
373
+ mutations.update(mockState, {} as any);
374
+ expect(mockState.notifications[0].title).toStrictEqual('original');
375
+ });
376
+ });
377
+
378
+ describe('remove', () => {
379
+ it('removes the notification with the given id', () => {
380
+ mockState.notifications = [
381
+ makeStoredNotification({ id: 'n1' }),
382
+ makeStoredNotification({ id: 'n2' }),
383
+ ];
384
+ mutations.remove(mockState, 'n1');
385
+ expect(mockState.notifications).toHaveLength(1);
386
+ expect(mockState.notifications[0].id).toStrictEqual('n2');
387
+ });
388
+
389
+ it('removes the encrypted notification entry from localStorage', () => {
390
+ const removeItemSpy = jest.spyOn(Storage.prototype, 'removeItem');
391
+
392
+ mockState.notifications = [makeStoredNotification({ id: 'n1' })];
393
+ mutations.remove(mockState, 'n1');
394
+ expect(removeItemSpy).toHaveBeenCalledWith('rancher-notifications-test-n1');
395
+ removeItemSpy.mockRestore();
396
+ });
397
+
398
+ it('does not change state when the id is not found', () => {
399
+ mockState.notifications = [makeStoredNotification({ id: 'n1' })];
400
+ mutations.remove(mockState, 'nonexistent');
401
+ expect(mockState.notifications).toHaveLength(1);
402
+ });
403
+ });
404
+
405
+ describe('clearAll', () => {
406
+ it('removes all notifications from state', () => {
407
+ mockState.notifications = [
408
+ makeStoredNotification({ id: 'n1' }),
409
+ makeStoredNotification({ id: 'n2' }),
410
+ ];
411
+ mutations.clearAll(mockState);
412
+ expect(mockState.notifications).toStrictEqual([]);
413
+ });
414
+
415
+ it('removes each notification encrypted entry from localStorage', () => {
416
+ const removeItemSpy = jest.spyOn(Storage.prototype, 'removeItem');
417
+
418
+ mockState.notifications = [
419
+ makeStoredNotification({ id: 'n1' }),
420
+ makeStoredNotification({ id: 'n2' }),
421
+ ];
422
+ mutations.clearAll(mockState);
423
+ expect(removeItemSpy).toHaveBeenCalledWith('rancher-notifications-test-n1');
424
+ expect(removeItemSpy).toHaveBeenCalledWith('rancher-notifications-test-n2');
425
+ removeItemSpy.mockRestore();
426
+ });
427
+
428
+ it('does nothing when there are no notifications', () => {
429
+ mutations.clearAll(mockState);
430
+ expect(mockState.notifications).toStrictEqual([]);
431
+ });
432
+ });
433
+ });
434
+ });