@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.5

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 (315) hide show
  1. package/assets/styles/global/_button.scss +1 -1
  2. package/assets/styles/global/_layout.scss +4 -0
  3. package/assets/translations/en-us.yaml +183 -51
  4. package/assets/translations/zh-hans.yaml +1 -7
  5. package/chart/monitoring/ClusterSelector.vue +0 -21
  6. package/chart/monitoring/prometheus/index.vue +6 -3
  7. package/components/ActionDropdownShell.vue +5 -3
  8. package/components/ButtonGroup.vue +26 -1
  9. package/components/CruResource.vue +212 -16
  10. package/components/ExplorerMembers.vue +8 -4
  11. package/components/ExplorerProjectsNamespaces.vue +10 -6
  12. package/components/GrowlManager.vue +4 -0
  13. package/components/MgmtNodeList.vue +184 -0
  14. package/components/PromptRestore.vue +93 -32
  15. package/components/Questions/index.vue +1 -0
  16. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  17. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  18. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  19. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  20. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  21. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  22. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  23. package/components/ResourceDetail/index.vue +1 -1
  24. package/components/ResourceList/Masthead.vue +7 -1
  25. package/components/ResourceList/index.vue +82 -1
  26. package/components/ResourceTable.vue +1 -0
  27. package/components/RichTranslation.vue +5 -2
  28. package/components/Setting.vue +1 -0
  29. package/components/SortableTable/index.vue +4 -3
  30. package/components/SubtleLink.vue +31 -6
  31. package/components/Tabbed/Tab.vue +29 -3
  32. package/components/Tabbed/index.vue +25 -3
  33. package/components/TableOfContents/TableOfContents.vue +109 -0
  34. package/components/TableOfContents/composables.ts +258 -0
  35. package/components/Window/ContainerShell.vue +21 -11
  36. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  37. package/components/Wizard.vue +23 -5
  38. package/components/__tests__/ButtonGroup.test.ts +56 -0
  39. package/components/__tests__/PromptRestore.test.ts +169 -19
  40. package/components/fleet/AppCoChartGrid.vue +401 -0
  41. package/components/fleet/AppCoEmptyState.vue +127 -0
  42. package/components/fleet/AppCoPageHeader.vue +119 -0
  43. package/components/fleet/AppCoVersionSelect.vue +70 -0
  44. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  45. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  46. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  47. package/components/fleet/FleetIntro.vue +7 -3
  48. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  49. package/components/fleet/FleetSecretSelector.vue +5 -3
  50. package/components/fleet/FleetValuesFrom.vue +8 -2
  51. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  52. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  53. package/components/fleet/GitRepoTargetTab.vue +0 -2
  54. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  55. package/components/fleet/HelmOpAppCoConfigTab.vue +597 -0
  56. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  57. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  58. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  59. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  60. package/components/fleet/HelmOpTargetTab.vue +64 -60
  61. package/components/fleet/HelmOpValuesTab.vue +129 -105
  62. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  63. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  64. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  65. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  66. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  67. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  68. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  69. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  70. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  71. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  72. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  73. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  74. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  75. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  76. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  77. package/components/fleet/dashboard/Empty.vue +8 -4
  78. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  79. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  80. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  81. package/components/form/ArrayList.vue +61 -4
  82. package/components/form/FileSelector.vue +39 -1
  83. package/components/form/KeyValue.vue +23 -2
  84. package/components/form/LabeledSelect.vue +39 -1
  85. package/components/form/Labels.vue +22 -3
  86. package/components/form/NameNsDescription.vue +13 -5
  87. package/components/form/PrivateRegistry.constants.ts +7 -0
  88. package/components/form/PrivateRegistry.vue +253 -18
  89. package/components/form/ResourceTabs/index.vue +1 -0
  90. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  91. package/components/form/__tests__/FileSelector.test.ts +23 -0
  92. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  93. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  94. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  95. package/components/formatter/EtcdSnapshotName.vue +73 -0
  96. package/components/formatter/InternalExternalIP.vue +10 -4
  97. package/components/formatter/ServiceTargets.vue +26 -7
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  99. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  100. package/components/nav/Header.vue +12 -1
  101. package/components/nav/TopLevelMenu.vue +7 -2
  102. package/components/nav/__tests__/Header.test.ts +15 -0
  103. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  104. package/components/templates/default.vue +16 -4
  105. package/components/templates/home.vue +9 -4
  106. package/components/templates/plain.vue +9 -4
  107. package/composables/useHelmOpResources.test.ts +56 -0
  108. package/composables/useHelmOpResources.ts +32 -0
  109. package/composables/useStateColor.test.ts +325 -0
  110. package/composables/useStateColor.ts +128 -0
  111. package/config/features.js +1 -0
  112. package/config/home-links.js +1 -1
  113. package/config/labels-annotations.js +3 -0
  114. package/config/product/explorer.js +17 -4
  115. package/config/product/manager.js +8 -0
  116. package/config/router/index.js +16 -0
  117. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  118. package/config/router/navigation-guards/authentication.js +10 -4
  119. package/config/router/routes.js +20 -6
  120. package/config/secret.ts +10 -0
  121. package/config/settings.ts +6 -4
  122. package/config/table-headers.js +3 -4
  123. package/config/types.js +16 -0
  124. package/core/plugin-products-base.ts +3 -3
  125. package/core/plugin-types.ts +83 -30
  126. package/core/plugin.ts +3 -0
  127. package/core/types-provisioning.ts +34 -1
  128. package/core/types.ts +15 -2
  129. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  130. package/detail/__tests__/workload.test.ts +3 -152
  131. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  132. package/detail/provisioning.cattle.io.cluster.vue +109 -7
  133. package/detail/workload/index.vue +12 -55
  134. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  135. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  136. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  137. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  138. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +206 -0
  139. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  140. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  141. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  142. package/edit/auth/__tests__/azuread.test.ts +34 -9
  143. package/edit/auth/__tests__/github.test.ts +234 -0
  144. package/edit/auth/__tests__/oidc.test.ts +26 -6
  145. package/edit/auth/__tests__/saml.test.ts +196 -0
  146. package/edit/auth/azuread.vue +128 -95
  147. package/edit/auth/github.vue +72 -13
  148. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  149. package/edit/auth/ldap/config.vue +8 -0
  150. package/edit/auth/ldap/index.vue +75 -1
  151. package/edit/auth/oidc.vue +119 -73
  152. package/edit/auth/saml.vue +76 -12
  153. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  154. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  155. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  156. package/edit/fleet.cattle.io.helmop.vue +542 -141
  157. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  158. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  159. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  160. package/edit/management.cattle.io.user.vue +5 -2
  161. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  162. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  163. package/edit/provisioning.cattle.io.cluster/rke2.vue +89 -11
  164. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  165. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  166. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  167. package/list/group.principal.vue +5 -4
  168. package/list/harvesterhci.io.management.cluster.vue +8 -9
  169. package/list/management.cattle.io.user.vue +12 -9
  170. package/list/provisioning.cattle.io.cluster.vue +16 -10
  171. package/mixins/__tests__/auth-config.test.ts +90 -0
  172. package/mixins/__tests__/chart.test.ts +94 -0
  173. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  174. package/mixins/auth-config.js +7 -0
  175. package/mixins/chart.js +11 -2
  176. package/mixins/child-hook.js +12 -6
  177. package/mixins/create-edit-view/impl.js +5 -3
  178. package/mixins/resource-fetch-api-pagination.js +21 -1
  179. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  180. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  181. package/models/__tests__/fleet-application.test.ts +175 -0
  182. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  183. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  184. package/models/__tests__/management.cattle.io.node.ts +22 -0
  185. package/models/__tests__/namespace.test.ts +36 -0
  186. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +205 -0
  187. package/models/__tests__/secret.test.ts +68 -1
  188. package/models/__tests__/workload.test.ts +401 -26
  189. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  190. package/models/compliance.cattle.io.clusterscan.js +39 -4
  191. package/models/fleet-application.js +4 -0
  192. package/models/fleet.cattle.io.helmop.js +20 -1
  193. package/models/management.cattle.io.cluster.js +39 -5
  194. package/models/management.cattle.io.node.js +44 -3
  195. package/models/namespace.js +1 -1
  196. package/models/pod.js +46 -3
  197. package/models/provisioning.cattle.io.cluster.js +64 -14
  198. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  199. package/models/secret.js +19 -0
  200. package/models/workload.js +120 -20
  201. package/models/workload.service.js +5 -0
  202. package/package.json +14 -13
  203. package/pages/about.vue +5 -6
  204. package/pages/auth/login.vue +0 -35
  205. package/pages/auth/setup.vue +11 -0
  206. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  207. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  208. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  209. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +485 -107
  210. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  211. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  212. package/pages/c/_cluster/apps/charts/install.vue +236 -144
  213. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  214. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  215. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  216. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  217. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  218. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  219. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  220. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  221. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  222. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  223. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  224. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  225. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  226. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  227. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  228. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  229. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  230. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  231. package/pages/c/_cluster/fleet/index.vue +2 -2
  232. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  233. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  234. package/pages/fail-whale.vue +16 -11
  235. package/pages/home.vue +16 -46
  236. package/pkg/require-asset.lib.js +25 -0
  237. package/pkg/vue.config.js +7 -0
  238. package/plugins/clean-html.d.ts +9 -0
  239. package/plugins/dashboard-store/__tests__/resource-class.test.ts +177 -0
  240. package/plugins/dashboard-store/getters.js +0 -1
  241. package/plugins/dashboard-store/resource-class.js +114 -19
  242. package/plugins/steve/__tests__/actions.test.ts +212 -0
  243. package/plugins/steve/actions.js +96 -0
  244. package/plugins/steve/steve-pagination-utils.ts +1 -1
  245. package/rancher-components/Accordion/Accordion.vue +53 -9
  246. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  247. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  248. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  249. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  250. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  251. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  252. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  253. package/rancher-components/RcButton/RcButton.vue +94 -15
  254. package/rancher-components/RcButton/index.ts +1 -1
  255. package/rancher-components/RcButton/types.ts +3 -0
  256. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  257. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  258. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  259. package/rancher-components/RcSection/RcSection.vue +28 -3
  260. package/scripts/extension/helm/package/Dockerfile +1 -1
  261. package/scripts/test-plugins-build.sh +2 -1
  262. package/store/__tests__/features.test.ts +131 -0
  263. package/store/__tests__/growl.test.ts +374 -0
  264. package/store/__tests__/modal.test.ts +131 -0
  265. package/store/__tests__/notifications.test.ts +434 -0
  266. package/store/__tests__/slideInPanel.test.ts +88 -0
  267. package/store/__tests__/type-map.utils.test.ts +433 -0
  268. package/store/catalog.js +57 -0
  269. package/store/features.js +4 -0
  270. package/store/plugins.js +7 -4
  271. package/types/components/buttonGroup.ts +5 -0
  272. package/types/shell/index.d.ts +166 -70
  273. package/utils/__tests__/auth.test.ts +273 -0
  274. package/utils/__tests__/computed.test.ts +193 -0
  275. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  276. package/utils/__tests__/dom.test.ts +81 -0
  277. package/utils/__tests__/duration.test.ts +37 -1
  278. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  279. package/utils/__tests__/fleet-appco.test.ts +312 -0
  280. package/utils/__tests__/monitoring.test.ts +130 -0
  281. package/utils/__tests__/object.test.ts +22 -0
  282. package/utils/__tests__/operation-cr.test.ts +34 -0
  283. package/utils/__tests__/platform.test.ts +91 -0
  284. package/utils/__tests__/position.test.ts +237 -0
  285. package/utils/__tests__/provider.test.ts +51 -1
  286. package/utils/__tests__/queue.test.ts +232 -0
  287. package/utils/__tests__/release-notes.test.ts +221 -0
  288. package/utils/__tests__/router.test.js +254 -1
  289. package/utils/__tests__/select.test.ts +208 -0
  290. package/utils/__tests__/time.test.ts +265 -1
  291. package/utils/__tests__/title.test.ts +47 -0
  292. package/utils/__tests__/width.test.ts +53 -0
  293. package/utils/__tests__/window.test.ts +158 -0
  294. package/utils/__tests__/xccdf.test.ts +126 -6
  295. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  296. package/utils/crypto/__tests__/index.test.ts +144 -0
  297. package/utils/duration.ts +104 -0
  298. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  299. package/utils/dynamic-content/info.ts +2 -1
  300. package/utils/error.js +13 -0
  301. package/utils/fleet-appco.ts +323 -0
  302. package/utils/object.js +22 -2
  303. package/utils/operation-cr.js +19 -0
  304. package/utils/provider.ts +12 -0
  305. package/utils/require-asset.ts +7 -0
  306. package/utils/validators/__tests__/container-images.test.ts +104 -0
  307. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  308. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  309. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  310. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  311. package/utils/validators/private-registry.ts +15 -4
  312. package/utils/xccdf.ts +39 -42
  313. package/vue.config.js +1 -1
  314. package/pages/support/index.vue +0 -264
  315. package/utils/duration.js +0 -43
@@ -0,0 +1,248 @@
1
+ import { shallowMount, VueWrapper } from '@vue/test-utils';
2
+ import CruCatalogRepo from '@shell/edit/catalog.cattle.io.clusterrepo.vue';
3
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
4
+
5
+ const createEditViewMock = {
6
+ props: {
7
+ value: {
8
+ type: Object,
9
+ default: () => ({}),
10
+ },
11
+ realMode: {
12
+ type: String,
13
+ default: _CREATE,
14
+ },
15
+ },
16
+ data() {
17
+ return { errors: [] };
18
+ },
19
+ computed: {
20
+ isCreate: () => false,
21
+ isEdit: () => true,
22
+ isView: () => false,
23
+ schema: () => ({}),
24
+ isNamespaced: () => false,
25
+ labels: {
26
+ get: jest.fn(() => ({})),
27
+ set: jest.fn(),
28
+ },
29
+ annotations: {
30
+ get: jest.fn(() => ({})),
31
+ set: jest.fn(),
32
+ },
33
+ doneRoute: () => 'mockedRoute',
34
+ doneParams: () => ({}),
35
+ },
36
+ methods: {
37
+ done: jest.fn(),
38
+ conflict: jest.fn(() => Promise.resolve([])),
39
+ save: jest.fn(() => Promise.resolve()),
40
+ actuallySave: jest.fn(() => Promise.resolve()),
41
+ setErrors: jest.fn(),
42
+ registerBeforeHook: jest.fn(),
43
+ }
44
+ };
45
+
46
+ jest.mock('@shell/config/version', () => ({ getVersionData: () => ({ RancherPrime: 'false' }) }));
47
+ jest.mock('@shell/utils/require-asset', () => ({ requireAsset: (path: string) => path }));
48
+
49
+ const defaultGlobalMocks = {
50
+ $store: {
51
+ getters: {
52
+ 'i18n/t': (key: string) => key,
53
+ currentProduct: { inStore: 'cluster' },
54
+ 'cluster/byId': () => null,
55
+ 'cluster/all': () => [],
56
+ },
57
+ dispatch: jest.fn(),
58
+ },
59
+ $route: {
60
+ name: 'test-route',
61
+ query: {},
62
+ },
63
+ $fetchState: { pending: false },
64
+ };
65
+
66
+ const createWrapper = (specOverrides = {}, mode = _EDIT): VueWrapper<any> => {
67
+ return shallowMount(CruCatalogRepo, {
68
+ props: {
69
+ value: {
70
+ spec: { url: 'https://test.com', ...specOverrides },
71
+ isOciType: false,
72
+ isSuseAppCollectionFromUI: false,
73
+ metadata: { name: 'test-repo', annotations: {} },
74
+ },
75
+ realMode: mode,
76
+ },
77
+ mixins: [createEditViewMock],
78
+ global: {
79
+ mocks: defaultGlobalMocks,
80
+ stubs: {
81
+ AsyncButton: true,
82
+ Footer: true,
83
+ NameNsDescription: true,
84
+ Labels: true,
85
+ SelectOrCreateAuthSecret: true,
86
+ Banner: true,
87
+ RcItemCard: true,
88
+ UnitInput: true,
89
+ },
90
+ },
91
+ });
92
+ };
93
+
94
+ describe('CruCatalogRepo - refresh interval', () => {
95
+ describe('initial state', () => {
96
+ it('defaults to enabled with null display value when no refreshInterval is set', () => {
97
+ const wrapper = createWrapper();
98
+
99
+ expect(wrapper.vm.refreshEnabled).toStrictEqual(true);
100
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(null);
101
+ expect(wrapper.vm.refreshUnit).toStrictEqual(3600);
102
+ });
103
+
104
+ it('parses existing refreshInterval into display value and unit', () => {
105
+ const wrapper = createWrapper({ refreshInterval: 7200 });
106
+
107
+ expect(wrapper.vm.refreshEnabled).toStrictEqual(true);
108
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(2);
109
+ expect(wrapper.vm.refreshUnit).toStrictEqual(3600);
110
+ });
111
+
112
+ it('parses refreshInterval that maps to minutes', () => {
113
+ const wrapper = createWrapper({ refreshInterval: 300 });
114
+
115
+ expect(wrapper.vm.refreshEnabled).toStrictEqual(true);
116
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(5);
117
+ expect(wrapper.vm.refreshUnit).toStrictEqual(60);
118
+ });
119
+
120
+ it('parses refreshInterval that maps to days', () => {
121
+ const wrapper = createWrapper({ refreshInterval: 172800 });
122
+
123
+ expect(wrapper.vm.refreshEnabled).toStrictEqual(true);
124
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(2);
125
+ expect(wrapper.vm.refreshUnit).toStrictEqual(86400);
126
+ });
127
+
128
+ it('parses refreshInterval that maps to seconds', () => {
129
+ const wrapper = createWrapper({ refreshInterval: 45 });
130
+
131
+ expect(wrapper.vm.refreshEnabled).toStrictEqual(true);
132
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(45);
133
+ expect(wrapper.vm.refreshUnit).toStrictEqual(1);
134
+ });
135
+
136
+ it('sets refreshEnabled to false when refreshInterval is -1', () => {
137
+ const wrapper = createWrapper({ refreshInterval: -1 });
138
+
139
+ expect(wrapper.vm.refreshEnabled).toStrictEqual(false);
140
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(null);
141
+ });
142
+ });
143
+
144
+ describe('syncRefreshIntervalToSpec', () => {
145
+ it('sets spec.refreshInterval to -1 when disabled', () => {
146
+ const wrapper = createWrapper();
147
+
148
+ wrapper.vm.onRefreshEnabledChange(false);
149
+
150
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(-1);
151
+ });
152
+
153
+ it('deletes spec.refreshInterval when input is empty', () => {
154
+ const wrapper = createWrapper({ refreshInterval: 3600 });
155
+
156
+ wrapper.vm.onRefreshValueChange('');
157
+
158
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(undefined);
159
+ });
160
+
161
+ it('deletes spec.refreshInterval when input is 0', () => {
162
+ const wrapper = createWrapper({ refreshInterval: 3600 });
163
+
164
+ wrapper.vm.onRefreshValueChange('0');
165
+
166
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(undefined);
167
+ });
168
+
169
+ it('converts display value and unit to seconds on spec', () => {
170
+ const wrapper = createWrapper();
171
+
172
+ wrapper.vm.onRefreshValueChange('5');
173
+
174
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(5 * 3600);
175
+ });
176
+
177
+ it('recalculates when unit changes', () => {
178
+ const wrapper = createWrapper();
179
+
180
+ wrapper.vm.onRefreshValueChange('2');
181
+ wrapper.vm.onRefreshUnitChange(60);
182
+
183
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(120);
184
+ });
185
+
186
+ it('recalculates when re-enabled', () => {
187
+ const wrapper = createWrapper();
188
+
189
+ wrapper.vm.onRefreshValueChange('10');
190
+ wrapper.vm.onRefreshEnabledChange(false);
191
+
192
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(-1);
193
+
194
+ wrapper.vm.onRefreshEnabledChange(true);
195
+
196
+ expect(wrapper.vm.value.spec.refreshInterval).toStrictEqual(10 * 3600);
197
+ });
198
+ });
199
+
200
+ describe('onRefreshValueChange', () => {
201
+ it('normalizes empty string to null', () => {
202
+ const wrapper = createWrapper();
203
+
204
+ wrapper.vm.onRefreshValueChange('');
205
+
206
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(null);
207
+ });
208
+
209
+ it('normalizes null to null', () => {
210
+ const wrapper = createWrapper();
211
+
212
+ wrapper.vm.onRefreshValueChange(null);
213
+
214
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(null);
215
+ });
216
+
217
+ it('coerces string value to number', () => {
218
+ const wrapper = createWrapper();
219
+
220
+ wrapper.vm.onRefreshValueChange('42');
221
+
222
+ expect(wrapper.vm.refreshDisplayValue).toStrictEqual(42);
223
+ });
224
+ });
225
+
226
+ describe('validation', () => {
227
+ it('returns error for negative values', () => {
228
+ const wrapper = createWrapper();
229
+ const rule = wrapper.vm.refreshIntervalRules[0];
230
+
231
+ expect(rule(-1)).toBeDefined();
232
+ });
233
+
234
+ it('returns undefined for zero', () => {
235
+ const wrapper = createWrapper();
236
+ const rule = wrapper.vm.refreshIntervalRules[0];
237
+
238
+ expect(rule(0)).toStrictEqual(undefined);
239
+ });
240
+
241
+ it('returns undefined for positive values', () => {
242
+ const wrapper = createWrapper();
243
+ const rule = wrapper.vm.refreshIntervalRules[0];
244
+
245
+ expect(rule(5)).toStrictEqual(undefined);
246
+ });
247
+ });
248
+ });
@@ -1,6 +1,7 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
3
3
  import { AUTH_TYPE } from '@shell/config/types';
4
+ import { base64Encode } from '@shell/utils/crypto';
4
5
  import GitRepo from '@shell/models/fleet.cattle.io.gitrepo';
5
6
  import GitRepoComponent from '@shell/edit/fleet.cattle.io.gitrepo.vue';
6
7
 
@@ -327,3 +328,94 @@ describe('view: fleet.cattle.io.gitrepo, GitHub password banner - should', () =>
327
328
  expect(githubBanner.exists()).toBe(shouldShowBanner);
328
329
  });
329
330
  });
331
+
332
+ describe('view: fleet.cattle.io.gitrepo, GitHub App auth - should', () => {
333
+ const originalDispatch = mockStore.dispatch;
334
+
335
+ afterEach(() => {
336
+ mockStore.dispatch = originalDispatch;
337
+ });
338
+
339
+ it('create an Opaque secret with the GitHub App data keys on doCreate', async() => {
340
+ const fakeSecret: any = {
341
+ metadata: { name: 'gitrepo-auth-abc' },
342
+ save: jest.fn().mockResolvedValue(undefined),
343
+ };
344
+
345
+ mockStore.dispatch = jest.fn().mockResolvedValue(fakeSecret);
346
+
347
+ const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
348
+
349
+ await (wrapper.vm as any).doCreate('clientSecretName', {
350
+ selected: AUTH_TYPE._GITHUB_APP,
351
+ githubAppId: 'app-id',
352
+ githubAppInstallationId: 'install-id',
353
+ githubAppPrivateKey: 'private-key',
354
+ });
355
+
356
+ expect(fakeSecret._type).toBe('Opaque');
357
+ expect(fakeSecret.data).toStrictEqual({
358
+ github_app_id: base64Encode('app-id'),
359
+ github_app_installation_id: base64Encode('install-id'),
360
+ github_app_private_key: base64Encode('private-key'),
361
+ });
362
+ expect(fakeSecret.save).toHaveBeenCalledWith();
363
+ });
364
+ });
365
+
366
+ describe('view: fleet.cattle.io.gitrepo, beforeNext dryRun validation', () => {
367
+ it('should call dryRunCreate when leaving stepMetadata in create mode', async() => {
368
+ const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
369
+ const vm = wrapper.vm as any;
370
+
371
+ vm.value.dryRunCreate = jest.fn().mockResolvedValue({});
372
+
373
+ await vm.beforeNext({ name: 'stepMetadata' });
374
+
375
+ expect(vm.value.dryRunCreate).toHaveBeenCalledWith(expect.objectContaining({
376
+ type: 'fleet.cattle.io.gitrepo',
377
+ metadata: expect.objectContaining({
378
+ name: 'test',
379
+ namespace: 'test',
380
+ }),
381
+ spec: expect.objectContaining({ repo: 'https://example.com/placeholder' }),
382
+ }));
383
+ });
384
+
385
+ it('should not call dryRunCreate for non-metadata steps', async() => {
386
+ const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
387
+ const vm = wrapper.vm as any;
388
+
389
+ vm.value.dryRunCreate = jest.fn();
390
+
391
+ await vm.beforeNext({ name: 'stepRepo' });
392
+
393
+ expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
394
+ });
395
+
396
+ it('should not call dryRunCreate in edit mode', async() => {
397
+ const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _EDIT }));
398
+ const vm = wrapper.vm as any;
399
+
400
+ vm.value.dryRunCreate = jest.fn();
401
+
402
+ await vm.beforeNext({ name: 'stepMetadata' });
403
+
404
+ expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
405
+ });
406
+
407
+ it('should reject with API errors when dryRunCreate fails', async() => {
408
+ const apiError = {
409
+ _status: 409,
410
+ message: 'gitrepos.fleet.cattle.io "test" already exists',
411
+ statusText: 'Conflict',
412
+ };
413
+
414
+ const wrapper = mount(GitRepoComponent, initGitRepo({ mode: _CREATE }));
415
+ const vm = wrapper.vm as any;
416
+
417
+ vm.value.dryRunCreate = jest.fn().mockRejectedValue(apiError);
418
+
419
+ await expect(vm.beforeNext({ name: 'stepMetadata' })).rejects.toStrictEqual(apiError);
420
+ });
421
+ });
@@ -28,12 +28,17 @@ const mocks = {
28
28
  $fetchState: { pending: false },
29
29
  $route: {
30
30
  query: { AS: '' },
31
+ hash: '',
31
32
  name: {
32
33
  endsWith: () => {
33
34
  return false;
34
35
  }
35
36
  }
36
37
  },
38
+ $router: {
39
+ currentRoute: { _value: { hash: '' } },
40
+ replace: jest.fn(),
41
+ },
37
42
  };
38
43
 
39
44
  const mockComputed = {
@@ -152,6 +157,106 @@ describe('helmOp component lifecycle', () => {
152
157
  });
153
158
  });
154
159
 
160
+ describe('onCancel', () => {
161
+ it('should navigate back to the AppCo chart page with version when isSuseAppCollection is true and mode is CREATE', () => {
162
+ const routerPush = jest.fn();
163
+ const appCoMocks = {
164
+ ...mocks,
165
+ $route: {
166
+ ...mocks.$route,
167
+ query: {
168
+ AS: '',
169
+ type: 'suse-application-collection',
170
+ chart: 'alertmanager',
171
+ version: '1.37.0',
172
+ secret: 'fleet-appco-auth-2n9px',
173
+ },
174
+ params: { cluster: 'local' },
175
+ },
176
+ $router: {
177
+ ...mocks.$router,
178
+ push: routerPush,
179
+ },
180
+ };
181
+
182
+ const wrapper = mount(HelmOpComponent, {
183
+ ...initHelmOp({ mode: _CREATE, realMode: _CREATE }),
184
+ computed: {
185
+ ...mockComputed,
186
+ isSuseAppCollection: () => true,
187
+ },
188
+ global: { mocks: appCoMocks },
189
+ });
190
+
191
+ wrapper.vm.onCancel();
192
+
193
+ expect(routerPush).toHaveBeenCalledWith({
194
+ name: 'c-cluster-fleet-application-appco-chart',
195
+ params: { cluster: 'local' },
196
+ query: {
197
+ 'repo-type': 'cluster',
198
+ repo: 'fleet-appco-repo-2n9px',
199
+ chart: 'alertmanager',
200
+ version: '1.37.0',
201
+ secret: 'fleet-appco-auth-2n9px',
202
+ },
203
+ });
204
+ });
205
+
206
+ it('should navigate back to the AppCo chart page without version when version is not in query', () => {
207
+ const routerPush = jest.fn();
208
+ const appCoMocks = {
209
+ ...mocks,
210
+ $route: {
211
+ ...mocks.$route,
212
+ query: {
213
+ AS: '',
214
+ type: 'suse-application-collection',
215
+ chart: 'alertmanager',
216
+ secret: 'fleet-appco-auth-2n9px',
217
+ },
218
+ params: { cluster: 'local' },
219
+ },
220
+ $router: {
221
+ ...mocks.$router,
222
+ push: routerPush,
223
+ },
224
+ };
225
+
226
+ const wrapper = mount(HelmOpComponent, {
227
+ ...initHelmOp({ mode: _CREATE, realMode: _CREATE }),
228
+ computed: {
229
+ ...mockComputed,
230
+ isSuseAppCollection: () => true,
231
+ },
232
+ global: { mocks: appCoMocks },
233
+ });
234
+
235
+ wrapper.vm.onCancel();
236
+
237
+ expect(routerPush).toHaveBeenCalledWith({
238
+ name: 'c-cluster-fleet-application-appco-chart',
239
+ params: { cluster: 'local' },
240
+ query: {
241
+ 'repo-type': 'cluster',
242
+ repo: 'fleet-appco-repo-2n9px',
243
+ chart: 'alertmanager',
244
+ version: undefined,
245
+ secret: 'fleet-appco-auth-2n9px',
246
+ },
247
+ });
248
+ });
249
+
250
+ it('should call done() when not a SuseAppCollection', () => {
251
+ const wrapper = mount(HelmOpComponent, initHelmOp({ mode: _CREATE, realMode: _CREATE }));
252
+
253
+ jest.spyOn(wrapper.vm, 'done').mockImplementation(jest.fn());
254
+ wrapper.vm.onCancel();
255
+
256
+ expect(wrapper.vm.done).toHaveBeenCalledWith();
257
+ });
258
+ });
259
+
155
260
  describe.each([
156
261
  _CREATE,
157
262
  _EDIT,
@@ -316,6 +421,12 @@ describe.each([
316
421
  expect(wrapper.vm.value.spec.downstreamResources).toStrictEqual([{ name: 'configMap2', kind: 'ConfigMap' }, { name: 'configMap3', kind: 'ConfigMap' }]);
317
422
  });
318
423
 
424
+ it('should have a beforeNext method', () => {
425
+ const wrapper = mount(HelmOpComponent, initHelmOp({ mode }));
426
+
427
+ expect(typeof wrapper.vm.beforeNext).toBe('function');
428
+ });
429
+
319
430
  if (mode === _CREATE) {
320
431
  it('should set created-by-user-id label when updateBeforeSave is called in CREATE mode', () => {
321
432
  const mockCurrentUser = { id: 'user-123' };
@@ -370,3 +481,98 @@ describe.each([
370
481
  });
371
482
  }
372
483
  });
484
+
485
+ describe('view: fleet.cattle.io.helmop, beforeNext dryRun validation', () => {
486
+ const initHelmOpWithMetadata = (props: any) => {
487
+ const value = new HelmOp({
488
+ ...mockHelmOp,
489
+ metadata: {
490
+ name: 'test-helmop',
491
+ namespace: 'fleet-default',
492
+ },
493
+ }, {
494
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
495
+ dispatch: jest.fn(),
496
+ rootGetters: { 'i18n/t': jest.fn() },
497
+ });
498
+
499
+ value.applyDefaults = () => {};
500
+
501
+ return {
502
+ props: {
503
+ value,
504
+ ...props
505
+ },
506
+ provide: {
507
+ store: createStore({
508
+ getters: {
509
+ currentStore: () => 'current_store',
510
+ 'management/paginationEnabled': () => () => false
511
+ }
512
+ })
513
+ },
514
+ computed: mockComputed,
515
+ global: { mocks },
516
+ };
517
+ };
518
+
519
+ it('should call dryRunCreate when leaving basics step in create mode', async() => {
520
+ const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _CREATE }));
521
+ const vm = wrapper.vm as any;
522
+
523
+ vm.value.dryRunCreate = jest.fn().mockResolvedValue({});
524
+
525
+ await vm.beforeNext({ name: 'basics' });
526
+
527
+ expect(vm.value.dryRunCreate).toHaveBeenCalledWith(expect.objectContaining({
528
+ type: 'fleet.cattle.io.helmop',
529
+ metadata: expect.objectContaining({
530
+ name: 'test-helmop',
531
+ namespace: 'fleet-default',
532
+ }),
533
+ spec: expect.objectContaining({
534
+ helm: expect.objectContaining({
535
+ chart: 'placeholder',
536
+ repo: 'https://example.com',
537
+ }),
538
+ }),
539
+ }));
540
+ });
541
+
542
+ it('should not call dryRunCreate for non-basics steps', async() => {
543
+ const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _CREATE }));
544
+ const vm = wrapper.vm as any;
545
+
546
+ vm.value.dryRunCreate = jest.fn();
547
+
548
+ await vm.beforeNext({ name: 'chart' });
549
+
550
+ expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
551
+ });
552
+
553
+ it('should not call dryRunCreate in edit mode', async() => {
554
+ const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _EDIT }));
555
+ const vm = wrapper.vm as any;
556
+
557
+ vm.value.dryRunCreate = jest.fn();
558
+
559
+ await vm.beforeNext({ name: 'basics' });
560
+
561
+ expect(vm.value.dryRunCreate).not.toHaveBeenCalled();
562
+ });
563
+
564
+ it('should reject with API errors when dryRunCreate fails', async() => {
565
+ const apiError = {
566
+ _status: 409,
567
+ message: 'helmops.fleet.cattle.io "test-helmop" already exists',
568
+ statusText: 'Conflict',
569
+ };
570
+
571
+ const wrapper = mount(HelmOpComponent, initHelmOpWithMetadata({ mode: _CREATE }));
572
+ const vm = wrapper.vm as any;
573
+
574
+ vm.value.dryRunCreate = jest.fn().mockRejectedValue(apiError);
575
+
576
+ await expect(vm.beforeNext({ name: 'basics' })).rejects.toStrictEqual(apiError);
577
+ });
578
+ });
@@ -1,5 +1,5 @@
1
1
  import { mount } from '@vue/test-utils';
2
- import Settings from '@shell/edit/management.cattle.io.setting.vue';
2
+ import Settings from '@shell/edit/management.cattle.io.setting/index.vue';
3
3
  import { SETTING } from '@shell/config/settings';
4
4
 
5
5
  const requiredSetup = () => ({
@@ -18,6 +18,7 @@ const requiredSetup = () => ({
18
18
  },
19
19
  $route: { query: { AS: '' } },
20
20
  $router: { applyQuery: jest.fn() },
21
+ t: (key: string) => key,
21
22
  }
22
23
  }
23
24
  });
@@ -21,6 +21,7 @@ exports[`component: General rendering & initial state should render with default
21
21
  labelkey="auditPolicy.general.enabled.checkbox"
22
22
  mode="create"
23
23
  primary="false"
24
+ usebodytextcolor="false"
24
25
  value="false"
25
26
  valuewhentrue="true"
26
27
  />
@@ -71,6 +72,7 @@ exports[`component: General rendering & initial state should render with default
71
72
  label="auditPolicy.general.verbosity.level.label"
72
73
  loading="false"
73
74
  localizedlabel="false"
75
+ lockedoptions=""
74
76
  mode="create"
75
77
  nooptionslabelkey="labelSelect.noOptions.empty"
76
78
  optionlabel="label"
@@ -112,6 +114,7 @@ exports[`component: General rendering & initial state should render with default
112
114
  label="auditPolicy.general.verbosity.request.requestHeaders"
113
115
  mode="create"
114
116
  primary="false"
117
+ usebodytextcolor="false"
115
118
  value="false"
116
119
  valuewhentrue="true"
117
120
  />
@@ -127,6 +130,7 @@ exports[`component: General rendering & initial state should render with default
127
130
  label="auditPolicy.general.verbosity.request.requestBody"
128
131
  mode="create"
129
132
  primary="false"
133
+ usebodytextcolor="false"
130
134
  value="false"
131
135
  valuewhentrue="true"
132
136
  />
@@ -152,6 +156,7 @@ exports[`component: General rendering & initial state should render with default
152
156
  label="auditPolicy.general.verbosity.response.responseHeaders"
153
157
  mode="create"
154
158
  primary="false"
159
+ usebodytextcolor="false"
155
160
  value="false"
156
161
  valuewhentrue="true"
157
162
  />
@@ -167,6 +172,7 @@ exports[`component: General rendering & initial state should render with default
167
172
  label="auditPolicy.general.verbosity.response.responseBody"
168
173
  mode="create"
169
174
  primary="false"
175
+ usebodytextcolor="false"
170
176
  value="false"
171
177
  valuewhentrue="true"
172
178
  />
@@ -19,6 +19,7 @@ exports[`component: CRUAuditPolicy (index) rendering & initial state should rend
19
19
  prevententersubmit="false"
20
20
  resource="[object Object]"
21
21
  showcancel="true"
22
+ showtoc="false"
22
23
  steps=""
23
24
  stepsoptions="[object Object]"
24
25
  subtypes=""