@rancher/shell 3.0.12-rc.3 → 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 (258) hide show
  1. package/assets/styles/global/_layout.scss +4 -0
  2. package/assets/translations/en-us.yaml +144 -41
  3. package/assets/translations/zh-hans.yaml +1 -7
  4. package/chart/monitoring/ClusterSelector.vue +0 -21
  5. package/chart/monitoring/prometheus/index.vue +6 -3
  6. package/components/CruResource.vue +161 -14
  7. package/components/ExplorerMembers.vue +8 -4
  8. package/components/ExplorerProjectsNamespaces.vue +10 -6
  9. package/components/GrowlManager.vue +4 -0
  10. package/components/MgmtNodeList.vue +184 -0
  11. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  12. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  13. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  14. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  15. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  16. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  17. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  18. package/components/ResourceDetail/index.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +7 -1
  20. package/components/ResourceList/index.vue +82 -1
  21. package/components/RichTranslation.vue +5 -2
  22. package/components/Setting.vue +1 -0
  23. package/components/SubtleLink.vue +31 -6
  24. package/components/Tabbed/Tab.vue +29 -3
  25. package/components/Tabbed/index.vue +25 -3
  26. package/components/TableOfContents/TableOfContents.vue +109 -0
  27. package/components/TableOfContents/composables.ts +258 -0
  28. package/components/Window/ContainerShell.vue +21 -11
  29. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  30. package/components/Wizard.vue +9 -4
  31. package/components/fleet/AppCoChartGrid.vue +401 -0
  32. package/components/fleet/AppCoEmptyState.vue +127 -0
  33. package/components/fleet/AppCoPageHeader.vue +119 -0
  34. package/components/fleet/AppCoVersionSelect.vue +70 -0
  35. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  36. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  37. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  38. package/components/fleet/FleetIntro.vue +7 -3
  39. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  40. package/components/fleet/FleetSecretSelector.vue +5 -3
  41. package/components/fleet/FleetValuesFrom.vue +8 -2
  42. package/components/fleet/GitRepoTargetTab.vue +0 -2
  43. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  44. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  45. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  46. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  47. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  48. package/components/fleet/HelmOpTargetTab.vue +64 -60
  49. package/components/fleet/HelmOpValuesTab.vue +129 -105
  50. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  51. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  52. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  53. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  54. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  55. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  56. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  57. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  58. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  59. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  60. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  61. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  62. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  63. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  64. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  65. package/components/fleet/dashboard/Empty.vue +8 -4
  66. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  67. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  68. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  69. package/components/form/ArrayList.vue +61 -4
  70. package/components/form/KeyValue.vue +23 -2
  71. package/components/form/LabeledSelect.vue +39 -1
  72. package/components/form/Labels.vue +22 -3
  73. package/components/form/NameNsDescription.vue +13 -5
  74. package/components/form/ResourceTabs/index.vue +1 -0
  75. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  76. package/components/formatter/InternalExternalIP.vue +10 -4
  77. package/components/formatter/ServiceTargets.vue +26 -7
  78. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  79. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  80. package/components/nav/Header.vue +4 -0
  81. package/components/nav/TopLevelMenu.vue +7 -2
  82. package/components/nav/__tests__/Header.test.ts +15 -0
  83. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  84. package/components/templates/default.vue +9 -4
  85. package/components/templates/home.vue +9 -4
  86. package/components/templates/plain.vue +9 -4
  87. package/composables/useHelmOpResources.test.ts +56 -0
  88. package/composables/useHelmOpResources.ts +32 -0
  89. package/composables/useStateColor.test.ts +325 -0
  90. package/composables/useStateColor.ts +128 -0
  91. package/config/home-links.js +1 -1
  92. package/config/labels-annotations.js +1 -0
  93. package/config/product/explorer.js +17 -4
  94. package/config/product/manager.js +2 -0
  95. package/config/router/index.js +16 -0
  96. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  97. package/config/router/navigation-guards/authentication.js +10 -4
  98. package/config/router/routes.js +20 -6
  99. package/config/settings.ts +0 -2
  100. package/config/table-headers.js +3 -4
  101. package/config/types.js +9 -0
  102. package/core/plugin-products-base.ts +3 -3
  103. package/core/plugin-types.ts +83 -30
  104. package/core/plugin.ts +3 -0
  105. package/core/types-provisioning.ts +34 -1
  106. package/core/types.ts +15 -2
  107. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  108. package/detail/__tests__/workload.test.ts +3 -152
  109. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  110. package/detail/provisioning.cattle.io.cluster.vue +30 -4
  111. package/detail/workload/index.vue +12 -55
  112. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  113. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  114. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  115. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  116. package/edit/auth/__tests__/azuread.test.ts +34 -9
  117. package/edit/auth/__tests__/github.test.ts +234 -0
  118. package/edit/auth/__tests__/oidc.test.ts +26 -6
  119. package/edit/auth/__tests__/saml.test.ts +196 -0
  120. package/edit/auth/azuread.vue +128 -95
  121. package/edit/auth/github.vue +72 -13
  122. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  123. package/edit/auth/ldap/config.vue +8 -0
  124. package/edit/auth/ldap/index.vue +75 -1
  125. package/edit/auth/oidc.vue +119 -73
  126. package/edit/auth/saml.vue +76 -12
  127. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  128. package/edit/fleet.cattle.io.helmop.vue +491 -136
  129. package/edit/management.cattle.io.user.vue +5 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
  131. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  132. package/list/group.principal.vue +5 -4
  133. package/list/harvesterhci.io.management.cluster.vue +8 -9
  134. package/list/management.cattle.io.user.vue +12 -9
  135. package/list/provisioning.cattle.io.cluster.vue +16 -10
  136. package/mixins/__tests__/auth-config.test.ts +90 -0
  137. package/mixins/__tests__/chart.test.ts +94 -0
  138. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  139. package/mixins/auth-config.js +7 -0
  140. package/mixins/chart.js +11 -2
  141. package/mixins/child-hook.js +12 -6
  142. package/mixins/create-edit-view/impl.js +5 -3
  143. package/mixins/resource-fetch-api-pagination.js +21 -1
  144. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  145. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  146. package/models/__tests__/fleet-application.test.ts +175 -0
  147. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  148. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  149. package/models/__tests__/management.cattle.io.node.ts +22 -0
  150. package/models/__tests__/namespace.test.ts +36 -0
  151. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
  152. package/models/__tests__/workload.test.ts +401 -26
  153. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  154. package/models/compliance.cattle.io.clusterscan.js +39 -4
  155. package/models/fleet-application.js +4 -0
  156. package/models/fleet.cattle.io.helmop.js +20 -1
  157. package/models/management.cattle.io.cluster.js +18 -2
  158. package/models/management.cattle.io.node.js +44 -3
  159. package/models/namespace.js +1 -1
  160. package/models/pod.js +33 -1
  161. package/models/provisioning.cattle.io.cluster.js +5 -5
  162. package/models/workload.js +108 -13
  163. package/models/workload.service.js +5 -0
  164. package/package.json +14 -13
  165. package/pages/about.vue +5 -6
  166. package/pages/auth/login.vue +0 -35
  167. package/pages/auth/setup.vue +11 -0
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  172. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  173. package/pages/c/_cluster/apps/charts/install.vue +122 -116
  174. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  175. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  176. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  177. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  178. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  179. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  180. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  181. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  182. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  183. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  184. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  185. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  186. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  187. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  188. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  189. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  190. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  191. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  192. package/pages/c/_cluster/fleet/index.vue +2 -2
  193. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  194. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  195. package/pages/fail-whale.vue +16 -11
  196. package/pages/home.vue +16 -46
  197. package/plugins/clean-html.d.ts +9 -0
  198. package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
  199. package/plugins/dashboard-store/resource-class.js +62 -7
  200. package/plugins/steve/__tests__/actions.test.ts +212 -0
  201. package/plugins/steve/actions.js +96 -0
  202. package/plugins/steve/steve-pagination-utils.ts +1 -1
  203. package/rancher-components/Accordion/Accordion.vue +53 -9
  204. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  205. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  206. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  207. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  208. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  209. package/rancher-components/RcButton/RcButton.vue +94 -15
  210. package/rancher-components/RcButton/types.ts +3 -0
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  213. package/rancher-components/RcSection/RcSection.vue +28 -3
  214. package/scripts/extension/helm/package/Dockerfile +1 -1
  215. package/scripts/test-plugins-build.sh +2 -1
  216. package/store/__tests__/notifications.test.ts +434 -0
  217. package/store/catalog.js +57 -0
  218. package/store/plugins.js +7 -4
  219. package/types/components/buttonGroup.ts +5 -0
  220. package/types/shell/index.d.ts +104 -70
  221. package/utils/__tests__/auth.test.ts +273 -0
  222. package/utils/__tests__/computed.test.ts +193 -0
  223. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  224. package/utils/__tests__/dom.test.ts +81 -0
  225. package/utils/__tests__/duration.test.ts +37 -1
  226. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  227. package/utils/__tests__/fleet-appco.test.ts +312 -0
  228. package/utils/__tests__/monitoring.test.ts +130 -0
  229. package/utils/__tests__/object.test.ts +22 -0
  230. package/utils/__tests__/platform.test.ts +91 -0
  231. package/utils/__tests__/position.test.ts +237 -0
  232. package/utils/__tests__/provider.test.ts +51 -1
  233. package/utils/__tests__/queue.test.ts +232 -0
  234. package/utils/__tests__/release-notes.test.ts +221 -0
  235. package/utils/__tests__/router.test.js +254 -1
  236. package/utils/__tests__/select.test.ts +208 -0
  237. package/utils/__tests__/time.test.ts +265 -1
  238. package/utils/__tests__/title.test.ts +47 -0
  239. package/utils/__tests__/width.test.ts +53 -0
  240. package/utils/__tests__/window.test.ts +158 -0
  241. package/utils/__tests__/xccdf.test.ts +126 -6
  242. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  243. package/utils/crypto/__tests__/index.test.ts +144 -0
  244. package/utils/duration.ts +104 -0
  245. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  246. package/utils/dynamic-content/info.ts +2 -1
  247. package/utils/error.js +13 -0
  248. package/utils/fleet-appco.ts +323 -0
  249. package/utils/object.js +22 -2
  250. package/utils/provider.ts +12 -0
  251. package/utils/validators/__tests__/container-images.test.ts +104 -0
  252. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  253. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  254. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  255. package/utils/xccdf.ts +39 -42
  256. package/vue.config.js +1 -1
  257. package/pages/support/index.vue +0 -264
  258. package/utils/duration.js +0 -43
@@ -1835,4 +1835,257 @@ describe('component: FleetClusterTargets', () => {
1835
1835
  expect(wrapper.vm.selectedClusterGroups).toStrictEqual([]);
1836
1836
  });
1837
1837
  });
1838
+
1839
+ describe('resolveClusterDisplayName', () => {
1840
+ it.each([
1841
+ ['c-m-abc123', 'my-production-cluster'],
1842
+ ['c-m-def456', 'my-staging-cluster'],
1843
+ ])('should return nameDisplay for metadata.name "%s"', (metadataName, expectedDisplay) => {
1844
+ const mockClusters = [
1845
+ {
1846
+ metadata: { namespace: 'fleet-default', name: 'c-m-abc123' },
1847
+ nameDisplay: 'my-production-cluster'
1848
+ },
1849
+ {
1850
+ metadata: { namespace: 'fleet-default', name: 'c-m-def456' },
1851
+ nameDisplay: 'my-staging-cluster'
1852
+ }
1853
+ ];
1854
+
1855
+ const wrapper = mount(FleetClusterTargets, {
1856
+ ...requiredSetup(),
1857
+ props: {
1858
+ targets: [],
1859
+ namespace: 'fleet-default',
1860
+ mode: _EDIT
1861
+ }
1862
+ });
1863
+
1864
+ wrapper.setData({ allClusters: mockClusters });
1865
+
1866
+ expect(wrapper.vm.resolveClusterDisplayName(metadataName)).toStrictEqual(expectedDisplay);
1867
+ });
1868
+
1869
+ it('should return the original name when no cluster matches', () => {
1870
+ const mockClusters = [
1871
+ {
1872
+ metadata: { namespace: 'fleet-default', name: 'c-m-abc123' },
1873
+ nameDisplay: 'my-production-cluster'
1874
+ }
1875
+ ];
1876
+
1877
+ const wrapper = mount(FleetClusterTargets, {
1878
+ ...requiredSetup(),
1879
+ props: {
1880
+ targets: [],
1881
+ namespace: 'fleet-default',
1882
+ mode: _EDIT
1883
+ }
1884
+ });
1885
+
1886
+ wrapper.setData({ allClusters: mockClusters });
1887
+
1888
+ expect(wrapper.vm.resolveClusterDisplayName('non-existent')).toStrictEqual('non-existent');
1889
+ });
1890
+
1891
+ it('should not resolve cluster from a different namespace', () => {
1892
+ const mockClusters = [
1893
+ {
1894
+ metadata: { namespace: 'other-namespace', name: 'c-m-abc123' },
1895
+ nameDisplay: 'my-production-cluster'
1896
+ }
1897
+ ];
1898
+
1899
+ const wrapper = mount(FleetClusterTargets, {
1900
+ ...requiredSetup(),
1901
+ props: {
1902
+ targets: [],
1903
+ namespace: 'fleet-default',
1904
+ mode: _EDIT
1905
+ }
1906
+ });
1907
+
1908
+ wrapper.setData({ allClusters: mockClusters });
1909
+
1910
+ expect(wrapper.vm.resolveClusterDisplayName('c-m-abc123')).toStrictEqual('c-m-abc123');
1911
+ });
1912
+ });
1913
+
1914
+ describe('clustersOptions', () => {
1915
+ it('should use nameDisplay for both label and value', () => {
1916
+ const mockClusters = [
1917
+ {
1918
+ metadata: { namespace: 'fleet-default', name: 'c-m-abc123' },
1919
+ nameDisplay: 'my-production-cluster',
1920
+ status: { provider: 'rke2' }
1921
+ },
1922
+ {
1923
+ metadata: { namespace: 'fleet-default', name: 'c-m-def456' },
1924
+ nameDisplay: 'my-staging-cluster',
1925
+ status: { provider: 'rke2' }
1926
+ }
1927
+ ];
1928
+
1929
+ const wrapper = mount(FleetClusterTargets, {
1930
+ ...requiredSetup(),
1931
+ props: {
1932
+ targets: [],
1933
+ namespace: 'fleet-default',
1934
+ mode: _EDIT
1935
+ }
1936
+ });
1937
+
1938
+ wrapper.setData({ allClusters: mockClusters });
1939
+
1940
+ const options = wrapper.vm.clustersOptions;
1941
+
1942
+ expect(options).toStrictEqual([
1943
+ {
1944
+ label: 'my-production-cluster', value: 'my-production-cluster', disabled: false
1945
+ },
1946
+ {
1947
+ label: 'my-staging-cluster', value: 'my-staging-cluster', disabled: false
1948
+ }
1949
+ ]);
1950
+ });
1951
+
1952
+ it('should filter out clusters from other namespaces', () => {
1953
+ const mockClusters = [
1954
+ {
1955
+ metadata: { namespace: 'fleet-default', name: 'c-m-abc123' },
1956
+ nameDisplay: 'my-cluster',
1957
+ status: { provider: 'rke2' }
1958
+ },
1959
+ {
1960
+ metadata: { namespace: 'other-namespace', name: 'c-m-other' },
1961
+ nameDisplay: 'other-cluster',
1962
+ status: { provider: 'rke2' }
1963
+ }
1964
+ ];
1965
+
1966
+ const wrapper = mount(FleetClusterTargets, {
1967
+ ...requiredSetup(),
1968
+ props: {
1969
+ targets: [],
1970
+ namespace: 'fleet-default',
1971
+ mode: _EDIT
1972
+ }
1973
+ });
1974
+
1975
+ wrapper.setData({ allClusters: mockClusters });
1976
+
1977
+ expect(wrapper.vm.clustersOptions).toStrictEqual([
1978
+ {
1979
+ label: 'my-cluster', value: 'my-cluster', disabled: false
1980
+ }
1981
+ ]);
1982
+ });
1983
+ });
1984
+
1985
+ describe('allClusters watcher', () => {
1986
+ it('should resolve selectedClusters metadata.name values to nameDisplay when clusters load', async() => {
1987
+ const mockClusters = [
1988
+ {
1989
+ metadata: { namespace: 'fleet-default', name: 'c-m-abc123' },
1990
+ nameDisplay: 'my-production-cluster'
1991
+ },
1992
+ {
1993
+ metadata: { namespace: 'fleet-default', name: 'c-m-def456' },
1994
+ nameDisplay: 'my-staging-cluster'
1995
+ }
1996
+ ];
1997
+
1998
+ const targets = [
1999
+ { clusterName: 'c-m-abc123' },
2000
+ { clusterName: 'c-m-def456' }
2001
+ ];
2002
+
2003
+ const wrapper = mount(FleetClusterTargets, {
2004
+ ...requiredSetup(),
2005
+ props: {
2006
+ targets,
2007
+ namespace: 'fleet-default',
2008
+ mode: _EDIT
2009
+ }
2010
+ });
2011
+
2012
+ expect(wrapper.vm.selectedClusters).toStrictEqual(['c-m-abc123', 'c-m-def456']);
2013
+
2014
+ wrapper.setData({ allClusters: mockClusters });
2015
+ await flushPromises();
2016
+
2017
+ expect(wrapper.vm.selectedClusters).toStrictEqual(['my-production-cluster', 'my-staging-cluster']);
2018
+ });
2019
+
2020
+ it('should keep names that already match nameDisplay unchanged', async() => {
2021
+ const mockClusters = [
2022
+ {
2023
+ metadata: { namespace: 'fleet-default', name: 'my-cluster' },
2024
+ nameDisplay: 'my-cluster'
2025
+ }
2026
+ ];
2027
+
2028
+ const targets = [{ clusterName: 'my-cluster' }];
2029
+
2030
+ const wrapper = mount(FleetClusterTargets, {
2031
+ ...requiredSetup(),
2032
+ props: {
2033
+ targets,
2034
+ namespace: 'fleet-default',
2035
+ mode: _EDIT
2036
+ }
2037
+ });
2038
+
2039
+ wrapper.setData({ allClusters: mockClusters });
2040
+ await flushPromises();
2041
+
2042
+ expect(wrapper.vm.selectedClusters).toStrictEqual(['my-cluster']);
2043
+ });
2044
+
2045
+ it('should keep unresolvable names as-is', async() => {
2046
+ const mockClusters = [
2047
+ {
2048
+ metadata: { namespace: 'fleet-default', name: 'c-m-abc123' },
2049
+ nameDisplay: 'known-cluster'
2050
+ }
2051
+ ];
2052
+
2053
+ const targets = [
2054
+ { clusterName: 'c-m-abc123' },
2055
+ { clusterName: 'unknown-name' }
2056
+ ];
2057
+
2058
+ const wrapper = mount(FleetClusterTargets, {
2059
+ ...requiredSetup(),
2060
+ props: {
2061
+ targets,
2062
+ namespace: 'fleet-default',
2063
+ mode: _EDIT
2064
+ }
2065
+ });
2066
+
2067
+ wrapper.setData({ allClusters: mockClusters });
2068
+ await flushPromises();
2069
+
2070
+ expect(wrapper.vm.selectedClusters).toStrictEqual(['known-cluster', 'unknown-name']);
2071
+ });
2072
+
2073
+ it('should not resolve when clusters array is empty', async() => {
2074
+ const targets = [{ clusterName: 'c-m-abc123' }];
2075
+
2076
+ const wrapper = mount(FleetClusterTargets, {
2077
+ ...requiredSetup(),
2078
+ props: {
2079
+ targets,
2080
+ namespace: 'fleet-default',
2081
+ mode: _EDIT
2082
+ }
2083
+ });
2084
+
2085
+ wrapper.setData({ allClusters: [] });
2086
+ await flushPromises();
2087
+
2088
+ expect(wrapper.vm.selectedClusters).toStrictEqual(['c-m-abc123']);
2089
+ });
2090
+ });
1838
2091
  });
@@ -63,6 +63,22 @@ describe('component: FleetSecretSelector.vue', () => {
63
63
  ]);
64
64
  });
65
65
 
66
+ it('should accept lockedOptions prop', () => {
67
+ const lockedOptions = ['locked-secret-1', 'locked-secret-2'];
68
+ const wrapper = shallowMount(FleetSecretSelector, {
69
+ props: { ...props, lockedOptions },
70
+ global
71
+ });
72
+
73
+ expect((wrapper.vm as any).lockedOptions).toStrictEqual(lockedOptions);
74
+ });
75
+
76
+ it('should default lockedOptions to empty array', () => {
77
+ const wrapper = shallowMount(FleetSecretSelector, { props, global });
78
+
79
+ expect((wrapper.vm as any).lockedOptions).toStrictEqual([]);
80
+ });
81
+
66
82
  it('should return correct filter structure from paginatePageOptions', () => {
67
83
  const wrapper = shallowMount(FleetSecretSelector, { props, global });
68
84
 
@@ -50,6 +50,50 @@ const allSecrets = [
50
50
  ];
51
51
 
52
52
  describe('component: FleetValuesFrom', () => {
53
+ describe('compact prop', () => {
54
+ it('should render h2 heading when compact is false', async() => {
55
+ const wrapper = mount(FleetValuesFrom, {
56
+ ...requiredSetup(),
57
+ props: {
58
+ value: [],
59
+ namespace: 'fleet-default',
60
+ mode: _CREATE,
61
+ compact: false,
62
+ },
63
+ data: () => ({ allSecrets, allConfigMaps })
64
+ });
65
+
66
+ await flushPromises();
67
+
68
+ const h2 = wrapper.find('[data-testid="fleet-values-from-list"] h2');
69
+ const h4 = wrapper.find('[data-testid="fleet-values-from-list"] h4');
70
+
71
+ expect(h2.exists()).toBe(true);
72
+ expect(h4.exists()).toBe(false);
73
+ });
74
+
75
+ it('should render h4 heading when compact is true', async() => {
76
+ const wrapper = mount(FleetValuesFrom, {
77
+ ...requiredSetup(),
78
+ props: {
79
+ value: [],
80
+ namespace: 'fleet-default',
81
+ mode: _CREATE,
82
+ compact: true,
83
+ },
84
+ data: () => ({ allSecrets, allConfigMaps })
85
+ });
86
+
87
+ await flushPromises();
88
+
89
+ const h4 = wrapper.find('[data-testid="fleet-values-from-list"] h4');
90
+ const h2 = wrapper.find('[data-testid="fleet-values-from-list"] h2');
91
+
92
+ expect(h4.exists()).toBe(true);
93
+ expect(h2.exists()).toBe(false);
94
+ });
95
+ });
96
+
53
97
  describe.each([
54
98
  _CREATE,
55
99
  _EDIT
@@ -0,0 +1,59 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import HelmOpAppCoConfigTab from '@shell/components/fleet/HelmOpAppCoConfigTab.vue';
3
+ import HelmOpAppCoResourcesSection from '@shell/components/fleet/HelmOpAppCoResourcesSection.vue';
4
+ import { _EDIT } from '@shell/config/query-params';
5
+
6
+ describe('component: HelmOpAppCoConfigTab', () => {
7
+ const defaultProps = {
8
+ value: {
9
+ metadata: { namespace: 'fleet-default' },
10
+ spec: { helm: { valuesFrom: [] } },
11
+ },
12
+ mode: _EDIT,
13
+ realMode: _EDIT,
14
+ registerBeforeHook: jest.fn(),
15
+ };
16
+
17
+ const mountWithSecrets = (downstreamSecretsList: string[]) => {
18
+ return shallowMount(HelmOpAppCoConfigTab, {
19
+ props: {
20
+ ...defaultProps,
21
+ downstreamSecretsList,
22
+ },
23
+ global: {
24
+ stubs: {
25
+ RcSection: { template: '<div><slot /></div>' },
26
+ RcIcon: true,
27
+ Tab: { template: '<div><slot /></div>' },
28
+ Tabbed: { template: '<div><slot /></div>' },
29
+ }
30
+ }
31
+ });
32
+ };
33
+
34
+ describe('appCoLockedSecrets', () => {
35
+ it('should only lock image pull secrets, not auth secrets', () => {
36
+ const wrapper = mountWithSecrets([
37
+ 'fleet-appco-auth-2n9px-image-pull-secret',
38
+ 'fleet-appco-auth-2n9px',
39
+ ]);
40
+
41
+ const resourcesSection = wrapper.findComponent(HelmOpAppCoResourcesSection);
42
+
43
+ expect(resourcesSection.props('lockedSecrets')).toStrictEqual([
44
+ 'fleet-appco-auth-2n9px-image-pull-secret',
45
+ ]);
46
+ });
47
+
48
+ it('should return empty array when no image pull secrets exist', () => {
49
+ const wrapper = mountWithSecrets([
50
+ 'fleet-appco-auth-abc123',
51
+ 'some-other-secret',
52
+ ]);
53
+
54
+ const resourcesSection = wrapper.findComponent(HelmOpAppCoResourcesSection);
55
+
56
+ expect(resourcesSection.props('lockedSecrets')).toStrictEqual([]);
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,62 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import HelmOpAppCoResourcesSection from '@shell/components/fleet/HelmOpAppCoResourcesSection.vue';
3
+ import FleetSecretSelector from '@shell/components/fleet/FleetSecretSelector.vue';
4
+ import { _EDIT } from '@shell/config/query-params';
5
+
6
+ describe('component: HelmOpAppCoResourcesSection', () => {
7
+ const props = {
8
+ value: { metadata: { namespace: 'fleet-default' }, spec: { keepResources: false } },
9
+ mode: _EDIT,
10
+ correctDriftEnabled: false,
11
+ downstreamSecretsList: ['secret-1'],
12
+ downstreamConfigMapsList: ['cm-1'],
13
+ };
14
+
15
+ const shallowMountWithSlots = (overrides = {}) => {
16
+ return shallowMount(HelmOpAppCoResourcesSection, {
17
+ props: { ...props, ...overrides },
18
+ global: {
19
+ stubs: {
20
+ RcSection: { template: '<div><slot /></div>' },
21
+ RcIcon: true,
22
+ }
23
+ }
24
+ });
25
+ };
26
+
27
+ it('should render the section container', () => {
28
+ const wrapper = shallowMountWithSlots();
29
+
30
+ expect(wrapper.find('[data-testid="helmop-appco-resources-section"]').exists()).toBe(true);
31
+ });
32
+
33
+ it('should show locked secret banner when lockedSecrets is non-empty', () => {
34
+ const wrapper = shallowMountWithSlots({ lockedSecrets: ['locked-1'] });
35
+
36
+ expect(wrapper.find('[data-testid="helmop-appco-resources-locked-secret-banner"]').exists()).toBe(true);
37
+ });
38
+
39
+ it('should not show locked secret banner when lockedSecrets is empty', () => {
40
+ const wrapper = shallowMountWithSlots({ lockedSecrets: [] });
41
+
42
+ expect(wrapper.find('[data-testid="helmop-appco-resources-locked-secret-banner"]').exists()).toBe(false);
43
+ });
44
+
45
+ it('should pass lockedSecrets to FleetSecretSelector as locked-options', () => {
46
+ const lockedSecrets = ['locked-a', 'locked-b'];
47
+ const wrapper = shallowMountWithSlots({ lockedSecrets });
48
+
49
+ const selector = wrapper.findComponent(FleetSecretSelector);
50
+
51
+ expect(selector.props('lockedOptions')).toStrictEqual(lockedSecrets);
52
+ });
53
+
54
+ it('should emit update:correct-drift', () => {
55
+ const wrapper = shallowMountWithSlots();
56
+ const checkbox = wrapper.findComponent({ name: 'Checkbox' });
57
+
58
+ checkbox.vm.$emit('update:value', true);
59
+
60
+ expect(wrapper.emitted('update:correct-drift')?.[0]).toStrictEqual([true]);
61
+ });
62
+ });
@@ -0,0 +1,43 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import HelmOpResourcesSection from '@shell/components/fleet/HelmOpResourcesSection.vue';
3
+ import { _EDIT } from '@shell/config/query-params';
4
+
5
+ describe('component: HelmOpResourcesSection', () => {
6
+ const props = {
7
+ value: { metadata: { namespace: 'fleet-default' }, spec: { keepResources: false } },
8
+ mode: _EDIT,
9
+ correctDriftEnabled: false,
10
+ downstreamSecretsList: ['secret-1'],
11
+ downstreamConfigMapsList: ['cm-1'],
12
+ };
13
+
14
+ it('should render the section container', () => {
15
+ const wrapper = shallowMount(HelmOpResourcesSection, { props });
16
+
17
+ expect(wrapper.find('[data-testid="helmop-resources-section"]').exists()).toBe(true);
18
+ });
19
+
20
+ it('should emit update:correct-drift when checkbox changes', () => {
21
+ const wrapper = shallowMount(HelmOpResourcesSection, { props });
22
+
23
+ wrapper.findComponent({ name: 'Checkbox' }).vm.$emit('update:value', true);
24
+
25
+ expect(wrapper.emitted('update:correct-drift')?.[0]).toStrictEqual([true]);
26
+ });
27
+
28
+ it('should emit update:downstream-resources when FleetSecretSelector emits', () => {
29
+ const wrapper = shallowMount(HelmOpResourcesSection, { props });
30
+
31
+ wrapper.findComponent({ name: 'FleetSecretSelector' }).vm.$emit('update:value', ['s1', 's2']);
32
+
33
+ expect(wrapper.emitted('update:downstream-resources')?.[0]).toStrictEqual([{ kind: 'Secret', list: ['s1', 's2'] }]);
34
+ });
35
+
36
+ it('should emit update:downstream-resources for ConfigMap', () => {
37
+ const wrapper = shallowMount(HelmOpResourcesSection, { props });
38
+
39
+ wrapper.findComponent({ name: 'FleetConfigMapSelector' }).vm.$emit('update:value', ['cm-a']);
40
+
41
+ expect(wrapper.emitted('update:downstream-resources')?.[0]).toStrictEqual([{ kind: 'ConfigMap', list: ['cm-a'] }]);
42
+ });
43
+ });
@@ -0,0 +1,34 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import HelmOpTargetOptionsSection from '@shell/components/fleet/HelmOpTargetOptionsSection.vue';
3
+ import { _EDIT } from '@shell/config/query-params';
4
+
5
+ describe('component: HelmOpTargetOptionsSection', () => {
6
+ const props = {
7
+ value: { spec: { serviceAccount: 'sa-1', namespace: 'ns-1' } },
8
+ mode: _EDIT,
9
+ };
10
+
11
+ it('should match snapshot with default props', () => {
12
+ const wrapper = shallowMount(HelmOpTargetOptionsSection, { props });
13
+
14
+ expect(wrapper.element).toMatchSnapshot();
15
+ });
16
+
17
+ it('should match snapshot in compact mode', () => {
18
+ const wrapper = shallowMount(HelmOpTargetOptionsSection, { props: { ...props, compact: true } });
19
+
20
+ expect(wrapper.element).toMatchSnapshot();
21
+ });
22
+
23
+ it('should show description text when compact is true', () => {
24
+ const wrapper = shallowMount(HelmOpTargetOptionsSection, { props: { ...props, compact: true } });
25
+
26
+ expect(wrapper.find('p').exists()).toBe(true);
27
+ });
28
+
29
+ it('should not show description text when compact is false', () => {
30
+ const wrapper = shallowMount(HelmOpTargetOptionsSection, { props: { ...props, compact: false } });
31
+
32
+ expect(wrapper.find('p').exists()).toBe(false);
33
+ });
34
+ });
@@ -0,0 +1,39 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import HelmOpValuesTab from '@shell/components/fleet/HelmOpValuesTab.vue';
3
+ import { _EDIT } from '@shell/config/query-params';
4
+
5
+ describe('component: HelmOpValuesTab', () => {
6
+ const defaultProps = {
7
+ value: {
8
+ metadata: { namespace: 'fleet-default' },
9
+ spec: { helm: { valuesFrom: [] } },
10
+ },
11
+ mode: _EDIT,
12
+ realMode: _EDIT,
13
+ chartValues: '',
14
+ chartValuesInit: '',
15
+ yamlForm: 'yaml',
16
+ yamlFormOptions: [],
17
+ yamlDiffModeOptions: [],
18
+ isYamlDiff: false,
19
+ editorMode: 'edit',
20
+ diffMode: 'diff',
21
+ isRealModeEdit: true,
22
+ };
23
+
24
+ it('should update value.spec.helm.valuesFrom when FleetValuesFrom emits update:value', () => {
25
+ const wrapper = shallowMount(HelmOpValuesTab, { props: defaultProps });
26
+
27
+ const newValuesFrom = [
28
+ {
29
+ configMapKeyRef: {
30
+ key: 'foo', name: 'cm-1', namespace: 'fleet-default'
31
+ }
32
+ },
33
+ ];
34
+
35
+ wrapper.findComponent({ name: 'FleetValuesFrom' }).vm.$emit('update:value', newValuesFrom);
36
+
37
+ expect(defaultProps.value.spec.helm.valuesFrom).toStrictEqual(newValuesFrom);
38
+ });
39
+ });
@@ -0,0 +1,97 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`component: AppCoEmptyState should match snapshot with error badge state 1`] = `
4
+ <div
5
+ class="appco-transitioning-error-state"
6
+ >
7
+ <h2
8
+ class="appco-empty-state-title m-0"
9
+ >
10
+ Error
11
+ </h2>
12
+ <div
13
+ class="appco-empty-state-body has-badge direction-column"
14
+ >
15
+ <div
16
+ class="appco-badge-container"
17
+ role="status"
18
+ >
19
+ <rc-icon-stub
20
+ ariahidden="true"
21
+ size="large"
22
+ status="error"
23
+ type="alert-alt"
24
+ />
25
+ <anonymous-stub
26
+ color="bg-error"
27
+ label="Failed"
28
+ />
29
+ </div>
30
+ <p>
31
+
32
+ Something went wrong
33
+
34
+ </p>
35
+ </div>
36
+ </div>
37
+ `;
38
+
39
+ exports[`component: AppCoEmptyState should match snapshot with no badge state 1`] = `
40
+ <div
41
+ class="appco-empty-state"
42
+ >
43
+ <h1
44
+ class="appco-empty-state-title m-0"
45
+ >
46
+ No charts found
47
+ </h1>
48
+ <div
49
+ class="appco-empty-state-body"
50
+ >
51
+ <!--v-if-->
52
+ <p>
53
+
54
+ Try a different search
55
+
56
+ </p>
57
+ </div>
58
+ </div>
59
+ `;
60
+
61
+ exports[`component: AppCoEmptyState should match snapshot with transitioning badge state 1`] = `
62
+ <div
63
+ class="appco-transitioning-error-state"
64
+ >
65
+ <h2
66
+ class="appco-empty-state-title m-0"
67
+ >
68
+ Loading charts
69
+ </h2>
70
+ <div
71
+ class="appco-empty-state-body has-badge"
72
+ >
73
+ <div
74
+ aria-live="polite"
75
+ class="appco-badge-container"
76
+ role="status"
77
+ >
78
+ <rc-icon-stub
79
+ ariahidden="true"
80
+ class="icon-spin"
81
+ size="large"
82
+ status="inherit"
83
+ type="spinner"
84
+ />
85
+ <anonymous-stub
86
+ color="bg-info"
87
+ label="In Progress"
88
+ />
89
+ </div>
90
+ <p>
91
+
92
+ Please wait...
93
+
94
+ </p>
95
+ </div>
96
+ </div>
97
+ `;