@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
@@ -1,4 +1,4 @@
1
- import { decodeHtml, resourceNames, pathArrayToTree } from '@shell/utils/string';
1
+ import { decodeHtml, resourceNames, pathArrayToTree, isBase64EncodedCert } from '@shell/utils/string';
2
2
 
3
3
  describe('fx: decodeHtml', () => {
4
4
  it('should decode HTML values from escaped string into valid markup', () => {
@@ -361,3 +361,25 @@ describe('fx: pathArrayToTree', () => {
361
361
  expect(actual).toStrictEqual(expected);
362
362
  });
363
363
  });
364
+
365
+ describe('fx: isBase64EncodedCert', () => {
366
+ it.each([
367
+ ['valid base64 cert', 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t', true],
368
+ ['base64 with padding', 'c3NoIGtleQ==', false],
369
+ ['base64 with newlines', 'LS0tLS1CRUdJTiBDRVJU\nSUZJQ0FURS0tLS0t\nTUlJQmtUQ0I=', true],
370
+ ['base64 with CRLF', 'LS0tLS1CRUdJTiBDRVJU\r\nSUZJQ0FURS0tLS0t\r\nTUlJQmtUQ0I=', true],
371
+ ])('should return %p for %p', (_, value, expected) => {
372
+ expect(isBase64EncodedCert(value as string)).toBe(expected);
373
+ });
374
+
375
+ it.each([
376
+ ['empty string', ''],
377
+ ['null', null],
378
+ ['undefined', undefined],
379
+ ['short string like "test"', 'test'],
380
+ ['short valid base64', 'Zm9v'],
381
+ ['invalid characters', 'not-valid-base64!'],
382
+ ])('should return false for %p', (_, value) => {
383
+ expect(isBase64EncodedCert(value as string)).toBe(false);
384
+ });
385
+ });
@@ -0,0 +1,154 @@
1
+ import {
2
+ ALL_STATE_COLORS,
3
+ BLANK_IMAGE,
4
+ StateColor,
5
+ getHighestAlertColor,
6
+ isHigherAlert,
7
+ isTruncated,
8
+ stateColorCssVar,
9
+ toBgColor,
10
+ } from '@shell/utils/style';
11
+
12
+ describe('stateColorCssVar', () => {
13
+ it.each([
14
+ ['success', 'var(--success)'],
15
+ ['warning', 'var(--warning)'],
16
+ ['error', 'var(--error)'],
17
+ ['info', 'var(--info)'],
18
+ ['disabled', 'var(--disabled)'],
19
+ ] as [StateColor, string][])('returns css var for %s', (color, expected) => {
20
+ expect(stateColorCssVar(color)).toBe(expected);
21
+ });
22
+ });
23
+
24
+ describe('toBgColor', () => {
25
+ it('returns bg-info when no color provided', () => {
26
+ expect(toBgColor(undefined)).toBe('bg-info');
27
+ });
28
+
29
+ it.each([
30
+ ['success', 'bg-success'],
31
+ ['warning', 'bg-warning'],
32
+ ['error', 'bg-error'],
33
+ ['info', 'bg-info'],
34
+ ['disabled', 'bg-disabled'],
35
+ ] as [StateColor, string][])('returns bg-%s for color %s', (color, expected) => {
36
+ expect(toBgColor(color)).toBe(expected);
37
+ });
38
+ });
39
+
40
+ describe('isHigherAlert', () => {
41
+ it.each([
42
+ ['error', 'warning', true],
43
+ ['error', 'success', true],
44
+ ['error', 'info', true],
45
+ ['warning', 'success', true],
46
+ ['warning', 'info', true],
47
+ ['success', 'info', true],
48
+ ['warning', 'error', false],
49
+ ['success', 'error', false],
50
+ ['info', 'error', false],
51
+ ['success', 'warning', false],
52
+ ['info', 'warning', false],
53
+ ['info', 'success', false],
54
+ ['error', 'error', false],
55
+ ['warning', 'warning', false],
56
+ ['info', 'info', false],
57
+ ['disabled', 'info', false],
58
+ ['disabled', 'error', false],
59
+ ['info', 'disabled', true],
60
+ ['error', 'disabled', true],
61
+ ] as [StateColor, StateColor, boolean][])('%s vs %s → %s', (a, b, expected) => {
62
+ expect(isHigherAlert(a, b)).toBe(expected);
63
+ });
64
+ });
65
+
66
+ describe('getHighestAlertColor', () => {
67
+ it.each([
68
+ {
69
+ desc: 'info for an empty array', colors: [], expected: 'info'
70
+ },
71
+ {
72
+ desc: 'error when single error element', colors: ['error'], expected: 'error'
73
+ },
74
+ {
75
+ desc: 'warning when single warning element', colors: ['warning'], expected: 'warning'
76
+ },
77
+ {
78
+ desc: 'error when present among mixed levels', colors: ['info', 'warning', 'error', 'success'], expected: 'error'
79
+ },
80
+ {
81
+ desc: 'warning when no error is present', colors: ['info', 'success', 'warning'], expected: 'warning'
82
+ },
83
+ {
84
+ desc: 'success when only info and success are present', colors: ['info', 'success'], expected: 'success'
85
+ },
86
+ {
87
+ desc: 'info when all colors are info', colors: ['info', 'info', 'info'], expected: 'info'
88
+ },
89
+ {
90
+ desc: 'info (default) when all colors are disabled', colors: ['disabled', 'disabled'], expected: 'info'
91
+ },
92
+ ])('returns $desc', ({ colors, expected }) => {
93
+ expect(getHighestAlertColor(colors as StateColor[])).toBe(expected);
94
+ });
95
+ });
96
+
97
+ describe('isTruncated', () => {
98
+ it('returns false when element is null', () => {
99
+ expect(isTruncated(null)).toBe(false);
100
+ });
101
+
102
+ it.each([
103
+ {
104
+ desc: 'false when text fits within element bounds',
105
+ el: {
106
+ scrollWidth: 100, clientWidth: 200, scrollHeight: 20, clientHeight: 20
107
+ },
108
+ expected: false,
109
+ },
110
+ {
111
+ desc: 'true when text is horizontally truncated',
112
+ el: {
113
+ scrollWidth: 300, clientWidth: 200, scrollHeight: 20, clientHeight: 20
114
+ },
115
+ expected: true,
116
+ },
117
+ {
118
+ desc: 'true when text is vertically truncated',
119
+ el: {
120
+ scrollWidth: 100, clientWidth: 100, scrollHeight: 50, clientHeight: 40
121
+ },
122
+ expected: true,
123
+ },
124
+ {
125
+ desc: 'false for single-pixel vertical overflow (1px tolerance)',
126
+ el: {
127
+ scrollWidth: 100, clientWidth: 100, scrollHeight: 21, clientHeight: 20
128
+ },
129
+ expected: false,
130
+ },
131
+ {
132
+ desc: 'true for 2+ pixel vertical overflow',
133
+ el: {
134
+ scrollWidth: 100, clientWidth: 100, scrollHeight: 22, clientHeight: 20
135
+ },
136
+ expected: true,
137
+ },
138
+ ])('returns $desc', ({ el, expected }) => {
139
+ expect(isTruncated(el as HTMLElement)).toBe(expected);
140
+ });
141
+ });
142
+
143
+ describe('all state colors constant', () => {
144
+ it('contains all expected state colors', () => {
145
+ expect(ALL_STATE_COLORS).toStrictEqual(['success', 'warning', 'error', 'info', 'disabled']);
146
+ });
147
+ });
148
+
149
+ describe('blank image constant', () => {
150
+ it('is a valid base64 data URI', () => {
151
+ expect(BLANK_IMAGE).toMatch(/^data:image\//);
152
+ expect(BLANK_IMAGE).toContain('base64,');
153
+ });
154
+ });
@@ -0,0 +1,184 @@
1
+ import { Solver } from '@shell/utils/svg-filter';
2
+
3
+ describe('solver', () => {
4
+ describe('css', () => {
5
+ it.each([
6
+ {
7
+ desc: 'all-zero filter values produce zero percentages and zero hue-rotate',
8
+ filters: [0, 0, 0, 0, 0, 0],
9
+ expected: 'filter: invert(0%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(0%) contrast(0%);',
10
+ },
11
+ {
12
+ desc: 'integer filter values are rounded and formatted correctly',
13
+ filters: [50, 20, 3750, 50, 100, 100],
14
+ expected: 'filter: invert(50%) sepia(20%) saturate(3750%) hue-rotate(180deg) brightness(100%) contrast(100%);',
15
+ },
16
+ {
17
+ desc: 'fractional filter values are rounded to nearest integer',
18
+ filters: [10.6, 30.4, 100.5, 25.2, 80.9, 120.1],
19
+ expected: 'filter: invert(11%) sepia(30%) saturate(101%) hue-rotate(91deg) brightness(81%) contrast(120%);',
20
+ },
21
+ {
22
+ desc: 'hue-rotate index (3) uses 3.6 multiplier',
23
+ filters: [0, 0, 0, 100, 0, 0],
24
+ expected: 'filter: invert(0%) sepia(0%) saturate(0%) hue-rotate(360deg) brightness(0%) contrast(0%);',
25
+ },
26
+ ])('$desc', ({ filters, expected }) => {
27
+ const solver = new Solver({
28
+ r: 255, g: 0, b: 0
29
+ });
30
+
31
+ expect(solver.css(filters)).toStrictEqual(expected);
32
+ });
33
+ });
34
+
35
+ describe('loss', () => {
36
+ it('returns 0 for filters that reproduce the target color exactly', () => {
37
+ // Target: black (0,0,0). Starting from black and applying:
38
+ // invert(0%), sepia(0%), saturate(0%), hueRotate(0), brightness(100%), contrast(100%)
39
+ // should leave color as (0,0,0) — perfect match.
40
+ const solver = new Solver({
41
+ r: 0, g: 0, b: 0
42
+ });
43
+ const loss = solver.loss([0, 0, 0, 0, 100, 100]);
44
+
45
+ expect(loss).toStrictEqual(0);
46
+ });
47
+
48
+ it('returns a positive value when filters produce a color that differs from the target', () => {
49
+ const solver = new Solver({
50
+ r: 255, g: 0, b: 0
51
+ });
52
+ // Filters that are all zero → color stays black → far from red target
53
+ const loss = solver.loss([0, 0, 0, 0, 0, 0]);
54
+
55
+ expect(loss).toBeGreaterThan(0);
56
+ });
57
+
58
+ it('loss is smaller when color produced by filters is closer to target', () => {
59
+ const solver = new Solver({
60
+ r: 0, g: 0, b: 0
61
+ });
62
+ // filters [0,0,0,0,0,0]: contrast(0) pushes everything to mid-gray → far from black target
63
+ const highLoss = solver.loss([0, 0, 0, 0, 0, 0]);
64
+ // filters [0,0,0,0,100,100]: identity transforms → stays black → matches target
65
+ const lowerLoss = solver.loss([0, 0, 0, 0, 100, 100]);
66
+
67
+ expect(lowerLoss).toBeLessThan(highLoss);
68
+ });
69
+
70
+ it('uses the same reusedColor instance without side effects between calls', () => {
71
+ const solver = new Solver({
72
+ r: 100, g: 150, b: 200
73
+ });
74
+ const loss1 = solver.loss([10, 5, 200, 30, 90, 110]);
75
+ const loss2 = solver.loss([10, 5, 200, 30, 90, 110]);
76
+
77
+ // Same inputs must produce the same loss regardless of call order
78
+ expect(loss1).toStrictEqual(loss2);
79
+ });
80
+ });
81
+
82
+ describe('constructor', () => {
83
+ it('stores target color components', () => {
84
+ const solver = new Solver({
85
+ r: 100, g: 150, b: 200
86
+ });
87
+
88
+ expect(solver.target.r).toStrictEqual(100);
89
+ expect(solver.target.g).toStrictEqual(150);
90
+ expect(solver.target.b).toStrictEqual(200);
91
+ });
92
+
93
+ it('clamps target components above 255 to 255', () => {
94
+ const solver = new Solver({
95
+ r: 300, g: 0, b: 0
96
+ });
97
+
98
+ expect(solver.target.r).toStrictEqual(255);
99
+ });
100
+
101
+ it('clamps target components below 0 to 0', () => {
102
+ const solver = new Solver({
103
+ r: 0, g: -10, b: 0
104
+ });
105
+
106
+ expect(solver.target.g).toStrictEqual(0);
107
+ });
108
+
109
+ it('computes targetHSL from the provided color', () => {
110
+ // Pure white: hsl should be h=0, s=0, l=100
111
+ const solver = new Solver({
112
+ r: 255, g: 255, b: 255
113
+ });
114
+
115
+ expect(solver.targetHSL.h).toStrictEqual(0);
116
+ expect(solver.targetHSL.s).toStrictEqual(0);
117
+ expect(solver.targetHSL.l).toBeCloseTo(100, 1);
118
+ });
119
+
120
+ it('computes targetHSL for pure red', () => {
121
+ // Pure red: r=255, g=0, b=0 → hsl h≈0 (normalised *100 → 0), s=100, l=50
122
+ const solver = new Solver({
123
+ r: 255, g: 0, b: 0
124
+ });
125
+
126
+ expect(solver.targetHSL.h).toBeCloseTo(0, 1);
127
+ expect(solver.targetHSL.s).toBeCloseTo(100, 1);
128
+ expect(solver.targetHSL.l).toBeCloseTo(50, 1);
129
+ });
130
+ });
131
+
132
+ describe('solve', () => {
133
+ it('returns an object with values, loss, filter and filterVal properties', () => {
134
+ const solver = new Solver({
135
+ r: 255, g: 0, b: 0
136
+ });
137
+ const result = solver.solve();
138
+
139
+ expect(result).toHaveProperty('values');
140
+ expect(result).toHaveProperty('loss');
141
+ expect(result).toHaveProperty('filter');
142
+ expect(result).toHaveProperty('filterVal');
143
+ });
144
+
145
+ it('filter starts with "filter: " and ends with ";"', () => {
146
+ const solver = new Solver({
147
+ r: 0, g: 128, b: 255
148
+ });
149
+ const result = solver.solve();
150
+
151
+ expect(result.filter.startsWith('filter: ')).toBe(true);
152
+ expect(result.filter.endsWith(';')).toBe(true);
153
+ });
154
+
155
+ it('filterVal is filter without the "filter: " prefix and trailing ";"', () => {
156
+ const solver = new Solver({
157
+ r: 0, g: 200, b: 100
158
+ });
159
+ const result = solver.solve();
160
+
161
+ expect(result.filterVal).toStrictEqual(
162
+ result.filter.replace('filter: ', '').replace(';', '')
163
+ );
164
+ });
165
+
166
+ it('loss is a non-negative number', () => {
167
+ const solver = new Solver({
168
+ r: 50, g: 100, b: 150
169
+ });
170
+ const result = solver.solve();
171
+
172
+ expect(result.loss).toBeGreaterThanOrEqual(0);
173
+ });
174
+
175
+ it('values array has exactly 6 elements', () => {
176
+ const solver = new Solver({
177
+ r: 10, g: 20, b: 30
178
+ });
179
+ const result = solver.solve();
180
+
181
+ expect(result.values).toHaveLength(6);
182
+ });
183
+ });
184
+ });
@@ -1,5 +1,8 @@
1
+ import day from 'dayjs';
1
2
  import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
2
- import { dateTimeFormat, isMissingDate } from '@shell/utils/time';
3
+ import {
4
+ dateTimeFormat, diffFrom, elapsedTime, getSecondsDiff, isMissingDate, safeSetTimeout
5
+ } from '@shell/utils/time';
3
6
  import { type Store } from 'vuex';
4
7
  import { ZERO_TIME } from '@shell/config/types';
5
8
 
@@ -42,3 +45,264 @@ describe('function: dateTimeFormat', () => {
42
45
  expect(formattedDate).toBe('');
43
46
  });
44
47
  });
48
+
49
+ describe('function: elapsedTime', () => {
50
+ it.each([
51
+ {
52
+ desc: 'returns empty object for 0',
53
+ seconds: 0,
54
+ expected: {},
55
+ },
56
+ {
57
+ desc: 'returns empty object for null',
58
+ seconds: null,
59
+ expected: {},
60
+ },
61
+ {
62
+ desc: 'returns empty object for undefined',
63
+ seconds: undefined,
64
+ expected: {},
65
+ },
66
+ {
67
+ desc: 'returns seconds label for 1 second',
68
+ seconds: 1,
69
+ expected: { diff: 1, label: '1s' },
70
+ },
71
+ {
72
+ desc: 'returns seconds label for 60 seconds',
73
+ seconds: 60,
74
+ expected: { diff: 1, label: '60s' },
75
+ },
76
+ {
77
+ desc: 'returns seconds label for 119 seconds (upper bound)',
78
+ seconds: 119,
79
+ expected: { diff: 1, label: '119s' },
80
+ },
81
+ {
82
+ desc: 'returns minutes+seconds label for 120 seconds',
83
+ seconds: 120,
84
+ expected: { diff: 1, label: '2m0s' },
85
+ },
86
+ {
87
+ desc: 'returns minutes+seconds label for 599 seconds',
88
+ seconds: 599,
89
+ expected: { diff: 1, label: '9m59s' },
90
+ },
91
+ {
92
+ desc: 'returns minutes-only label for 600 seconds',
93
+ seconds: 600,
94
+ expected: { diff: 60, label: '10m' },
95
+ },
96
+ {
97
+ desc: 'returns minutes-only label for 10799 seconds (under 3 hours)',
98
+ seconds: 10799,
99
+ expected: { diff: 60, label: '179m' },
100
+ },
101
+ {
102
+ desc: 'returns hours+minutes label for 10800 seconds (3 hours)',
103
+ seconds: 10800,
104
+ expected: { diff: 60, label: '3h0m' },
105
+ },
106
+ {
107
+ desc: 'returns hours+minutes label for 25200 seconds (7 hours)',
108
+ seconds: 25200,
109
+ expected: { diff: 60, label: '7h0m' },
110
+ },
111
+ {
112
+ desc: 'returns hours-only label for 28800 seconds (8 hours)',
113
+ seconds: 28800,
114
+ expected: { diff: 60, label: '8h' },
115
+ },
116
+ {
117
+ desc: 'returns hours-only label for 86400 seconds (1 day as hours)',
118
+ seconds: 86400,
119
+ expected: { diff: 60, label: '24h' },
120
+ },
121
+ {
122
+ desc: 'returns days+hours label for 172800 seconds (2 days)',
123
+ seconds: 172800,
124
+ expected: { diff: 60, label: '2d0h' },
125
+ },
126
+ {
127
+ desc: 'returns days+hours label for 176400 seconds (2 days 1 hour)',
128
+ seconds: 176400,
129
+ expected: { diff: 60, label: '2d1h' },
130
+ },
131
+ ])('$desc', ({ seconds, expected }) => {
132
+ expect(elapsedTime(seconds)).toStrictEqual(expected);
133
+ });
134
+ });
135
+
136
+ describe('function: safeSetTimeout', () => {
137
+ beforeEach(() => {
138
+ jest.useFakeTimers();
139
+ });
140
+
141
+ afterEach(() => {
142
+ jest.clearAllTimers();
143
+ jest.useRealTimers();
144
+ });
145
+
146
+ it('invokes the callback after the given delay', () => {
147
+ const callback = jest.fn();
148
+
149
+ safeSetTimeout(100, callback, null);
150
+ jest.runAllTimers();
151
+
152
+ expect(callback).toHaveBeenCalledTimes(1);
153
+ });
154
+
155
+ it('invokes the callback with the provided context object', () => {
156
+ let capturedContext: unknown;
157
+
158
+ function cb(this: unknown) {
159
+ capturedContext = this;
160
+ }
161
+ const ctx = { id: 'ctx' };
162
+
163
+ safeSetTimeout(100, cb, ctx);
164
+ jest.runAllTimers();
165
+
166
+ expect(capturedContext).toStrictEqual(ctx);
167
+ });
168
+
169
+ it('does not schedule a timeout when delay exceeds 32-bit maximum', () => {
170
+ const callback = jest.fn();
171
+ const result = safeSetTimeout(2147483648, callback, null);
172
+
173
+ jest.runAllTimers();
174
+
175
+ expect(result).toBeUndefined();
176
+ expect(callback).not.toHaveBeenCalled();
177
+ });
178
+
179
+ it('schedules a timeout for delay equal to 32-bit maximum', () => {
180
+ const callback = jest.fn();
181
+
182
+ safeSetTimeout(2147483647, callback, null);
183
+ jest.runAllTimers();
184
+
185
+ expect(callback).toHaveBeenCalledTimes(1);
186
+ });
187
+ });
188
+
189
+ describe('function: getSecondsDiff', () => {
190
+ it.each([
191
+ {
192
+ desc: 'returns 0 for identical date strings',
193
+ startDate: '2024-01-01T00:00:00Z',
194
+ endDate: '2024-01-01T00:00:00Z',
195
+ expected: 0,
196
+ },
197
+ {
198
+ desc: 'returns 60 for dates one minute apart',
199
+ startDate: '2024-01-01T00:00:00Z',
200
+ endDate: '2024-01-01T00:01:00Z',
201
+ expected: 60,
202
+ },
203
+ {
204
+ desc: 'returns 3600 for dates one hour apart',
205
+ startDate: '2024-01-01T00:00:00Z',
206
+ endDate: '2024-01-01T01:00:00Z',
207
+ expected: 3600,
208
+ },
209
+ {
210
+ desc: 'returns absolute difference when end is before start',
211
+ startDate: '2024-01-01T01:00:00Z',
212
+ endDate: '2024-01-01T00:00:00Z',
213
+ expected: 3600,
214
+ },
215
+ {
216
+ desc: 'returns 86400 for dates one day apart',
217
+ startDate: '2024-01-01T00:00:00Z',
218
+ endDate: '2024-01-02T00:00:00Z',
219
+ expected: 86400,
220
+ },
221
+ ])('$desc', ({ startDate, endDate, expected }) => {
222
+ expect(getSecondsDiff(startDate, endDate)).toStrictEqual(expected);
223
+ });
224
+ });
225
+
226
+ describe('function: diffFrom', () => {
227
+ const from = day('2024-01-01T12:00:00Z');
228
+
229
+ it.each([
230
+ {
231
+ desc: '30 seconds reports seconds unit',
232
+ value: from.subtract(30, 'second'),
233
+ expected: {
234
+ diff: -30,
235
+ absDiff: 30,
236
+ label: 30,
237
+ unitsKey: 'unit.sec',
238
+ units: 'sec',
239
+ next: 1,
240
+ },
241
+ },
242
+ {
243
+ desc: '120 seconds reports minutes unit with small label',
244
+ value: from.subtract(120, 'second'),
245
+ expected: {
246
+ diff: -120,
247
+ absDiff: 2,
248
+ label: 2,
249
+ unitsKey: 'unit.min',
250
+ units: 'min',
251
+ next: 6,
252
+ },
253
+ },
254
+ {
255
+ desc: '600 seconds reports minutes unit with integer label',
256
+ value: from.subtract(600, 'second'),
257
+ expected: {
258
+ diff: -600,
259
+ absDiff: 10,
260
+ label: 10,
261
+ unitsKey: 'unit.min',
262
+ units: 'min',
263
+ next: 6,
264
+ },
265
+ },
266
+ {
267
+ desc: '5400 seconds reports hours unit with fractional label',
268
+ value: from.subtract(5400, 'second'),
269
+ expected: {
270
+ diff: -5400,
271
+ absDiff: 1.5,
272
+ label: 1.5,
273
+ unitsKey: 'unit.hour',
274
+ units: 'hour',
275
+ next: 36,
276
+ },
277
+ },
278
+ {
279
+ desc: '172800 seconds reports days unit',
280
+ value: from.subtract(172800, 'second'),
281
+ expected: {
282
+ diff: -172800,
283
+ absDiff: 2,
284
+ label: 2,
285
+ unitsKey: 'unit.day',
286
+ units: 'day',
287
+ next: 72,
288
+ },
289
+ },
290
+ ])('$desc', ({ value, expected }) => {
291
+ expect(diffFrom(value, from, null)).toStrictEqual(expected);
292
+ });
293
+
294
+ it('includes string property when t function is provided', () => {
295
+ const value = from.subtract(30, 'second');
296
+ const t = (key: string, args: unknown) => `${ key }:${ JSON.stringify(args) }`;
297
+ const result = diffFrom(value, from, t);
298
+
299
+ expect(result.string).toStrictEqual(`30 unit.sec:${ JSON.stringify({ count: 30 }) }`);
300
+ });
301
+
302
+ it('omits string property when t function is not provided', () => {
303
+ const value = from.subtract(30, 'second');
304
+ const result = diffFrom(value, from, null);
305
+
306
+ expect(result).not.toHaveProperty('string');
307
+ });
308
+ });
@@ -0,0 +1,47 @@
1
+ import { updatePageTitle } from '@shell/utils/title';
2
+
3
+ describe('updatePageTitle', () => {
4
+ afterEach(() => {
5
+ document.title = '';
6
+ });
7
+
8
+ it('sets document.title to a single breadcrumb', () => {
9
+ updatePageTitle('Home');
10
+ expect(document.title).toStrictEqual('Home');
11
+ });
12
+
13
+ it('joins multiple breadcrumbs with " - "', () => {
14
+ updatePageTitle('Rancher', 'Clusters', 'my-cluster');
15
+ expect(document.title).toStrictEqual('Rancher - Clusters - my-cluster');
16
+ });
17
+
18
+ it('filters out null values', () => {
19
+ updatePageTitle('Rancher', null, 'Clusters');
20
+ expect(document.title).toStrictEqual('Rancher - Clusters');
21
+ });
22
+
23
+ it('filters out undefined values', () => {
24
+ updatePageTitle('Rancher', undefined, 'Clusters');
25
+ expect(document.title).toStrictEqual('Rancher - Clusters');
26
+ });
27
+
28
+ it('filters out false values', () => {
29
+ updatePageTitle('Rancher', false, 'Clusters');
30
+ expect(document.title).toStrictEqual('Rancher - Clusters');
31
+ });
32
+
33
+ it('filters out empty string values', () => {
34
+ updatePageTitle('Rancher', '', 'Clusters');
35
+ expect(document.title).toStrictEqual('Rancher - Clusters');
36
+ });
37
+
38
+ it('sets document.title to empty string when all breadcrumbs are falsy', () => {
39
+ updatePageTitle(null, undefined, false);
40
+ expect(document.title).toStrictEqual('');
41
+ });
42
+
43
+ it('sets document.title to empty string when called with no arguments', () => {
44
+ updatePageTitle();
45
+ expect(document.title).toStrictEqual('');
46
+ });
47
+ });