@rancher/shell 3.0.5-rc.5 → 3.0.5-rc.6

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 (312) hide show
  1. package/assets/data/aws-regions.json +1 -0
  2. package/assets/images/key.svg +17 -0
  3. package/assets/styles/base/_spacing.scss +2 -2
  4. package/assets/styles/global/_form.scss +1 -1
  5. package/assets/styles/global/_labeled-input.scss +1 -1
  6. package/assets/styles/themes/_dark.scss +3 -0
  7. package/assets/styles/themes/_light.scss +3 -0
  8. package/assets/styles/vendor/vue-select.scss +1 -1
  9. package/assets/translations/en-us.yaml +404 -64
  10. package/assets/translations/zh-hans.yaml +3 -4
  11. package/cloud-credential/gcp.vue +9 -1
  12. package/components/AppModal.vue +2 -0
  13. package/components/CodeMirror.vue +1 -1
  14. package/components/ConfigMapSettings/Settings.vue +377 -0
  15. package/components/ConfigMapSettings/index.vue +354 -0
  16. package/components/CruResource.vue +1 -2
  17. package/components/DetailText.vue +61 -11
  18. package/components/Drawer/Chrome.vue +116 -0
  19. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +61 -0
  20. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +48 -0
  21. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +54 -0
  22. package/components/Drawer/ResourceDetailDrawer/__tests__/YamlTab.test.ts +80 -0
  23. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +82 -0
  24. package/components/Drawer/ResourceDetailDrawer/__tests__/helpers.test.ts +42 -0
  25. package/components/Drawer/ResourceDetailDrawer/composables.ts +50 -0
  26. package/components/Drawer/ResourceDetailDrawer/helpers.ts +10 -0
  27. package/components/Drawer/ResourceDetailDrawer/index.vue +110 -0
  28. package/components/GrowlManager.vue +16 -15
  29. package/components/IconOrSvg.vue +5 -0
  30. package/components/KeyValueView.vue +1 -1
  31. package/components/LocaleSelector.vue +9 -1
  32. package/components/ProgressBarMulti.vue +1 -0
  33. package/components/PromptModal.vue +6 -1
  34. package/components/RelatedResources.vue +4 -12
  35. package/components/Resource/Detail/Additional.vue +46 -0
  36. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  37. package/components/Resource/Detail/Metadata/Annotations/index.vue +5 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +223 -0
  39. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +37 -254
  40. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +298 -0
  41. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +27 -5
  42. package/components/Resource/Detail/Metadata/KeyValue.vue +25 -17
  43. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -1
  44. package/components/Resource/Detail/Metadata/Labels/index.vue +4 -0
  45. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +1 -1
  46. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +1 -1
  47. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +75 -0
  48. package/components/Resource/Detail/Metadata/composables.ts +60 -11
  49. package/components/Resource/Detail/Metadata/index.vue +12 -5
  50. package/components/Resource/Detail/Page.vue +15 -0
  51. package/components/Resource/Detail/ResourceRow.vue +37 -18
  52. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/composables.test.ts +29 -0
  53. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/__tests__/index.test.ts +48 -0
  54. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/composables.ts +31 -0
  55. package/components/Resource/Detail/ResourceTabs/ConfigMapDataTab/index.vue +50 -0
  56. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/__tests__/composables.test.ts +66 -0
  57. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/composables.ts +21 -0
  58. package/components/Resource/Detail/ResourceTabs/KnownHostsTab/index.vue +31 -0
  59. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Basic.vue +45 -0
  60. package/components/Resource/Detail/ResourceTabs/SecretDataTab/BasicAuth.vue +31 -0
  61. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Certificate.vue +31 -0
  62. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Registry.vue +22 -0
  63. package/components/Resource/Detail/ResourceTabs/SecretDataTab/ServiceAccountToken.vue +31 -0
  64. package/components/Resource/Detail/ResourceTabs/SecretDataTab/Ssh.vue +32 -0
  65. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Basic.test.ts +40 -0
  66. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/BasicAuth.test.ts +33 -0
  67. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Certificate.test.ts +33 -0
  68. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Registry.test.ts +27 -0
  69. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/ServiceAccountToken.test.ts +33 -0
  70. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/Ssh.test.ts +33 -0
  71. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/auth-types.test.ts +186 -0
  72. package/components/Resource/Detail/ResourceTabs/SecretDataTab/__tests__/composables.test.ts +102 -0
  73. package/components/Resource/Detail/ResourceTabs/SecretDataTab/auth-types.ts +109 -0
  74. package/components/Resource/Detail/ResourceTabs/SecretDataTab/composeables.ts +52 -0
  75. package/components/Resource/Detail/ResourceTabs/SecretDataTab/index.vue +71 -0
  76. package/components/Resource/Detail/TitleBar/Title.vue +2 -1
  77. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +1 -1
  78. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +1 -1
  79. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +63 -0
  80. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  81. package/components/Resource/Detail/TitleBar/composables.ts +44 -0
  82. package/components/Resource/Detail/TitleBar/index.vue +83 -11
  83. package/components/Resource/Detail/composables.ts +45 -0
  84. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +70 -0
  85. package/components/ResourceDetail/{__tests__/Masthead.test.ts → Masthead/__tests__/legacy.test.ts} +3 -3
  86. package/components/ResourceDetail/Masthead/index.vue +65 -0
  87. package/components/ResourceDetail/Masthead/latest.vue +44 -0
  88. package/components/ResourceDetail/__tests__/index.test.ts +26 -5
  89. package/components/ResourceDetail/index.vue +30 -16
  90. package/components/ResourceDetail/legacy.vue +18 -1
  91. package/components/ResourceList/Masthead.vue +6 -0
  92. package/components/ResourceYaml.vue +14 -1
  93. package/components/SlideInPanelManager.vue +46 -7
  94. package/components/StateDot/index.vue +28 -0
  95. package/components/Tabbed/index.vue +11 -15
  96. package/components/Wizard.vue +4 -2
  97. package/components/__tests__/ConfigMapSettings.test.ts +376 -0
  98. package/components/__tests__/GrowlManager.test.ts +0 -25
  99. package/components/auth/login/ldap.vue +1 -1
  100. package/components/fleet/FleetApplications.vue +0 -7
  101. package/components/fleet/FleetClusterTargets/TargetsList.vue +66 -0
  102. package/components/fleet/FleetClusterTargets/index.vue +455 -0
  103. package/components/fleet/FleetClusters.vue +25 -6
  104. package/components/fleet/FleetGitRepoPaths.vue +476 -0
  105. package/components/fleet/FleetHelmOps.vue +8 -0
  106. package/components/fleet/FleetRepos.vue +1 -6
  107. package/components/fleet/FleetResources.vue +4 -5
  108. package/components/fleet/FleetValuesFrom.vue +295 -0
  109. package/components/fleet/__tests__/FleetClusterTargets.test.ts +1224 -0
  110. package/components/fleet/__tests__/FleetGitRepoPaths.test.ts +265 -0
  111. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +13 -13
  112. package/components/fleet/__tests__/FleetValuesFrom.test.ts +300 -0
  113. package/components/fleet/dashboard/ResourceCard.vue +1 -0
  114. package/components/fleet/dashboard/ResourceCardSummary.vue +1 -5
  115. package/components/fleet/dashboard/ResourceDetails.vue +8 -10
  116. package/components/fleet/dashboard/ResourcePanel.vue +15 -8
  117. package/components/form/ArrayList.vue +13 -2
  118. package/components/form/ChangePassword.vue +3 -1
  119. package/components/form/Footer.vue +10 -4
  120. package/components/form/KeyValue.vue +81 -43
  121. package/components/form/LabeledSelect.vue +56 -16
  122. package/components/form/Labels.vue +90 -17
  123. package/components/form/MatchExpressions.vue +46 -5
  124. package/components/form/NameNsDescription.vue +1 -1
  125. package/components/form/ResourceSelector.vue +1 -0
  126. package/components/form/ResourceTabs/index.vue +5 -0
  127. package/components/form/SecretSelector.vue +9 -2
  128. package/components/form/Select.vue +57 -19
  129. package/components/form/SimpleSecretSelector.vue +9 -2
  130. package/components/form/Taints.vue +21 -2
  131. package/components/form/UnitInput.vue +8 -0
  132. package/components/form/ValueFromResource.vue +1 -1
  133. package/components/form/__tests__/LabeledSelect.test.ts +8 -4
  134. package/components/form/__tests__/Labels.test.ts +360 -0
  135. package/components/form/__tests__/MatchExpressions.test.ts +16 -13
  136. package/components/form/__tests__/Select.test.ts +5 -2
  137. package/components/formatter/FleetApplicationSource.vue +1 -1
  138. package/components/formatter/WorkloadHealthScale.vue +1 -1
  139. package/components/google/AccountAccess.vue +211 -0
  140. package/components/google/types/gcp.d.ts +136 -0
  141. package/components/google/types/index.d.ts +101 -0
  142. package/components/google/util/__mocks__/gcp.ts +465 -0
  143. package/components/google/util/formatter.ts +82 -0
  144. package/components/google/util/gcp.ts +134 -0
  145. package/components/google/util/index.d.ts +11 -0
  146. package/components/nav/Favorite.vue +1 -1
  147. package/components/nav/Group.vue +70 -47
  148. package/components/nav/Header.vue +5 -1
  149. package/components/nav/NamespaceFilter.vue +13 -1
  150. package/components/nav/NotificationCenter/Notification.vue +510 -0
  151. package/components/nav/NotificationCenter/NotificationHeader.vue +112 -0
  152. package/components/nav/NotificationCenter/index.vue +148 -0
  153. package/composables/drawer.ts +26 -0
  154. package/composables/resources.test.ts +63 -0
  155. package/composables/resources.ts +38 -0
  156. package/composables/useIsNewDetailPageEnabled.ts +17 -0
  157. package/config/labels-annotations.js +6 -0
  158. package/config/product/auth.js +16 -1
  159. package/config/product/{cis.js → compliance.js} +23 -26
  160. package/config/product/explorer.js +5 -1
  161. package/config/product/fleet.js +7 -0
  162. package/config/product/settings.js +22 -11
  163. package/config/query-params.js +3 -0
  164. package/config/roles.ts +1 -1
  165. package/config/router/navigation-guards/authentication.js +51 -2
  166. package/config/router/routes.js +27 -31
  167. package/config/settings.ts +21 -3
  168. package/config/store.js +2 -0
  169. package/config/system-namespaces.js +1 -1
  170. package/config/table-headers.js +2 -2
  171. package/config/types.js +15 -6
  172. package/core/plugin.ts +32 -7
  173. package/core/types.ts +18 -1
  174. package/detail/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +22 -18
  175. package/detail/management.cattle.io.fleetworkspace.vue +18 -27
  176. package/detail/management.cattle.io.oidcclient.vue +369 -0
  177. package/detail/node.vue +2 -2
  178. package/detail/pod.vue +2 -2
  179. package/detail/service.vue +10 -1
  180. package/detail/workload/index.vue +8 -2
  181. package/dialog/ExtensionCatalogUninstallDialog.vue +7 -4
  182. package/dialog/GenericPrompt.vue +1 -1
  183. package/dialog/ImportDialog.vue +8 -8
  184. package/dialog/OidcClientSecretDialog.vue +117 -0
  185. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +3 -3
  186. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +5 -2
  187. package/edit/autoscaling.horizontalpodautoscaler/index.vue +4 -1
  188. package/edit/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +30 -31
  189. package/edit/{cis.cattle.io.clusterscanbenchmark.vue → compliance.cattle.io.clusterscanbenchmark.vue} +4 -4
  190. package/edit/{cis.cattle.io.clusterscanprofile.vue → compliance.cattle.io.clusterscanprofile.vue} +5 -5
  191. package/edit/configmap.vue +4 -1
  192. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  193. package/edit/fleet.cattle.io.gitrepo.vue +44 -222
  194. package/edit/fleet.cattle.io.helmop.vue +44 -269
  195. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  196. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  197. package/edit/logging-flow/index.vue +1 -0
  198. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  199. package/edit/management.cattle.io.fleetworkspace.vue +1 -0
  200. package/edit/management.cattle.io.oidcclient.vue +162 -0
  201. package/edit/management.cattle.io.project.vue +4 -1
  202. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  203. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +5 -0
  204. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  205. package/edit/monitoring.coreos.com.receiver/auth.vue +30 -30
  206. package/edit/monitoring.coreos.com.receiver/index.vue +1 -0
  207. package/edit/monitoring.coreos.com.receiver/types/email.vue +1 -1
  208. package/edit/monitoring.coreos.com.route.vue +1 -0
  209. package/edit/namespace.vue +1 -0
  210. package/edit/networking.istio.io.destinationrule/index.vue +4 -1
  211. package/edit/networking.k8s.io.ingress/index.vue +4 -1
  212. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +7 -2
  213. package/edit/networking.k8s.io.networkpolicy/index.vue +6 -2
  214. package/edit/node.vue +1 -0
  215. package/edit/persistentvolume/index.vue +4 -1
  216. package/edit/provisioning.cattle.io.cluster/rke2.vue +418 -382
  217. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +27 -27
  218. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +5 -0
  219. package/edit/resources.cattle.io.restore.vue +1 -1
  220. package/edit/secret/index.vue +1 -0
  221. package/edit/service.vue +4 -1
  222. package/edit/serviceaccount.vue +4 -1
  223. package/edit/storage.k8s.io.storageclass/index.vue +4 -1
  224. package/edit/workload/index.vue +5 -0
  225. package/list/{cis.cattle.io.clusterscan.vue → compliance.cattle.io.clusterscan.vue} +2 -2
  226. package/list/management.cattle.io.oidcclient.vue +108 -0
  227. package/list/node.vue +2 -0
  228. package/machine-config/amazonec2.vue +3 -24
  229. package/machine-config/components/GCEImage.vue +374 -0
  230. package/machine-config/google.vue +617 -0
  231. package/mixins/__tests__/brand.spec.ts +170 -0
  232. package/mixins/brand.js +16 -17
  233. package/mixins/create-edit-view/index.js +5 -0
  234. package/mixins/resource-fetch-api-pagination.js +16 -0
  235. package/mixins/vue-select-overrides.js +1 -0
  236. package/models/{cis.cattle.io.clusterscan.js → compliance.cattle.io.clusterscan.js} +8 -8
  237. package/models/{cis.cattle.io.clusterscanbenchmark.js → compliance.cattle.io.clusterscanbenchmark.js} +1 -1
  238. package/models/{cis.cattle.io.clusterscanprofile.js → compliance.cattle.io.clusterscanprofile.js} +5 -5
  239. package/models/{cis.cattle.io.clusterscanreport.js → compliance.cattle.io.clusterscanreport.js} +1 -1
  240. package/models/fleet-application.js +8 -79
  241. package/models/fleet.cattle.io.cluster.js +11 -0
  242. package/models/fleet.cattle.io.gitrepo.js +2 -2
  243. package/models/fleet.cattle.io.helmop.js +9 -39
  244. package/models/management.cattle.io.fleetworkspace.js +2 -1
  245. package/models/management.cattle.io.oidcclient.js +18 -0
  246. package/models/management.cattle.io.registration.js +3 -0
  247. package/models/provisioning.cattle.io.cluster.js +5 -5
  248. package/models/service.js +4 -0
  249. package/models/workload.js +5 -0
  250. package/package.json +1 -1
  251. package/pages/about.vue +4 -58
  252. package/pages/auth/login.vue +1 -1
  253. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +0 -1
  254. package/pages/c/_cluster/apps/charts/index.vue +285 -81
  255. package/pages/c/_cluster/auth/user.retention/index.vue +87 -78
  256. package/pages/c/_cluster/explorer/index.vue +3 -3
  257. package/pages/c/_cluster/explorer/tools/pages/_page.vue +0 -1
  258. package/pages/c/_cluster/fleet/application/create.vue +3 -2
  259. package/pages/c/_cluster/fleet/index.vue +94 -56
  260. package/pages/c/_cluster/fleet/settings/index.vue +229 -0
  261. package/pages/c/_cluster/longhorn/index.vue +5 -2
  262. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +16 -1
  263. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -2
  264. package/pages/explorer/resource/detail/configmap.vue +30 -7
  265. package/pages/explorer/resource/detail/secret.vue +50 -0
  266. package/pages/home.vue +9 -55
  267. package/pages/support/index.vue +4 -6
  268. package/plugins/dashboard-store/actions.js +19 -5
  269. package/plugins/dashboard-store/getters.js +4 -0
  270. package/plugins/dashboard-store/resource-class.js +16 -2
  271. package/plugins/steve/steve-pagination-utils.ts +26 -18
  272. package/plugins/steve/subscribe.js +6 -1
  273. package/rancher-components/Banner/Banner.vue +13 -0
  274. package/rancher-components/Form/Checkbox/Checkbox.vue +9 -4
  275. package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
  276. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -0
  277. package/rancher-components/RcItemCard/RcItemCard.vue +8 -3
  278. package/store/auth.js +2 -0
  279. package/store/catalog.js +23 -1
  280. package/store/growl.js +97 -8
  281. package/store/index.js +6 -0
  282. package/store/notifications.ts +426 -0
  283. package/store/prefs.js +0 -1
  284. package/store/type-map.js +19 -16
  285. package/store/uiplugins.ts +15 -1
  286. package/types/fleet.d.ts +24 -0
  287. package/types/notifications/index.ts +74 -0
  288. package/types/shell/index.d.ts +46 -32
  289. package/types/store/dashboard-store.types.ts +16 -0
  290. package/utils/__tests__/fleet.test.ts +148 -0
  291. package/utils/__tests__/object.test.ts +54 -1
  292. package/utils/__tests__/string.test.ts +273 -1
  293. package/utils/__tests__/time.test.ts +31 -0
  294. package/utils/auth.js +9 -2
  295. package/utils/crypto/encryption.ts +103 -0
  296. package/utils/cspAdaptor.ts +51 -0
  297. package/utils/fleet.ts +54 -65
  298. package/utils/object.js +36 -0
  299. package/utils/pagination-utils.ts +1 -1
  300. package/utils/release-notes.ts +48 -0
  301. package/utils/selector-typed.ts +7 -2
  302. package/utils/string.js +24 -0
  303. package/utils/{time.js → time.ts} +25 -6
  304. package/utils/uiplugins.ts +22 -0
  305. package/utils/validators/formRules/index.ts +3 -0
  306. package/components/Resource/Detail/TitleBar/composable.ts +0 -31
  307. package/config/product/legacy.js +0 -62
  308. package/pages/c/_cluster/legacy/pages/_page.vue +0 -29
  309. package/pages/c/_cluster/legacy/project/_page.vue +0 -57
  310. package/pages/c/_cluster/legacy/project/index.vue +0 -32
  311. package/pages/c/_cluster/legacy/project/pipelines.vue +0 -96
  312. /package/components/ResourceDetail/{Masthead.vue → Masthead/legacy.vue} +0 -0
@@ -0,0 +1,360 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Labels, { Factory } from '@shell/components/form/Labels.vue';
3
+ import KeyValue from '@shell/components/form/KeyValue.vue';
4
+ import { ToggleSwitch } from '@components/Form/ToggleSwitch';
5
+ import { _EDIT, _VIEW } from '@shell/config/query-params';
6
+
7
+ const mockT = jest.fn((key: string) => key);
8
+
9
+ describe('class: Factory', () => {
10
+ const mockProtectedWarning = 'Protected Key';
11
+
12
+ it('should correctly initialize with no protected keys or regexes', () => {
13
+ const factory = new Factory([], [], mockProtectedWarning, { a: '1', b: '2' });
14
+
15
+ expect((factory as any).protectedKeys).toStrictEqual([]);
16
+ expect((factory as any).protectedRegexes).toStrictEqual([]);
17
+ expect((factory as any).protectedWarning).toBe(mockProtectedWarning);
18
+ expect(factory.initValue).toStrictEqual({ a: '1', b: '2' });
19
+ expect(factory.value).toStrictEqual({ a: '1', b: '2' });
20
+ expect(factory.keyErrors).toStrictEqual({});
21
+ expect(factory.hasProtectedKeys).toBe(false);
22
+ });
23
+
24
+ it('should correctly initialize with protected keys', () => {
25
+ const factory = new Factory(['key1'], [], mockProtectedWarning, { key1: 'value1', key2: 'value2' });
26
+
27
+ expect((factory as any).protectedKeys).toStrictEqual(['key1']);
28
+ expect(factory.value).toStrictEqual({ key2: 'value2' });
29
+ expect(factory.hasProtectedKeys).toBe(true);
30
+ });
31
+
32
+ it('should correctly initialize with protected regexes', () => {
33
+ const factory = new Factory([], [/^key/], mockProtectedWarning, { key1: 'value1', other: 'value2' });
34
+
35
+ expect((factory as any).protectedRegexes.map((r: RegExp) => r.source)).toStrictEqual(['^key']);
36
+ expect(factory.value).toStrictEqual({ other: 'value2' });
37
+ expect(factory.hasProtectedKeys).toBe(true);
38
+ });
39
+
40
+ it('should omit protected keys from value but keep them in initValue', () => {
41
+ const factory = new Factory(['protected'], [], mockProtectedWarning, { protected: 'pVal', normal: 'nVal' });
42
+
43
+ expect(factory.initValue).toStrictEqual({ protected: 'pVal', normal: 'nVal' });
44
+ expect(factory.value).toStrictEqual({ normal: 'nVal' });
45
+ });
46
+
47
+ it('should update value and call callbackFn, preserving initial protected keys', () => {
48
+ const initialValue = {
49
+ system: 'sysVal', user: 'userVal', system2: 'sysVal'
50
+ };
51
+ const factory = new Factory(['system', 'system2'], [], mockProtectedWarning, initialValue);
52
+ const callback = jest.fn();
53
+
54
+ const newValue = {
55
+ newUser: 'newUVal', user: 'updatedUserVal', newProtected: 'newPVal', system: 'foo'
56
+ };
57
+
58
+ factory.update(newValue, callback);
59
+
60
+ // The internal factory.value is just the new set
61
+ expect(factory.value).toStrictEqual(newValue);
62
+
63
+ // The callback should receive the mix of new unprotected values and initial protected values
64
+ expect(callback).toHaveBeenCalledWith({
65
+ newProtected: 'newPVal',
66
+ newUser: 'newUVal',
67
+ user: 'updatedUserVal',
68
+ system: 'sysVal', // 'system' is the original protected from initialValue, sysVal is the original value
69
+ system2: 'sysVal' // 'system2' is the original protected from initialValue
70
+ });
71
+ });
72
+
73
+ it('should handle null/undefined update value gracefully', () => {
74
+ const initialValue = { system: 'sysVal', user: 'userVal' };
75
+ const factory = new Factory(['system'], [], mockProtectedWarning, initialValue);
76
+ const callback = jest.fn();
77
+
78
+ factory.update({}, callback);
79
+
80
+ expect(factory.value).toStrictEqual({});
81
+ expect(factory.keyErrors).toStrictEqual({});
82
+ expect(callback).toHaveBeenCalledWith({ system: 'sysVal' }); // Only the original protected values
83
+ });
84
+
85
+ it('should correctly identify protected keys based on both array and regex', () => {
86
+ const factory = new Factory(['staticKey'], [/^dynamic/], mockProtectedWarning, {});
87
+
88
+ expect((factory as any).isProtected('staticKey')).toBe(true);
89
+ expect((factory as any).isProtected('dynamicKey')).toBe(true);
90
+ expect((factory as any).isProtected('otherKey')).toBe(false);
91
+ });
92
+ });
93
+
94
+ describe('component: Labels', () => {
95
+ const mockValue = {
96
+ labels: {
97
+ 'kubernetes.io/hostname': 'test-app',
98
+ 'my-label': 'my-value'
99
+ },
100
+ systemLabels: ['kubernetes.io/hostname'],
101
+ annotations: {
102
+ 'cattle.io/system': 'user',
103
+ 'my-annotation': 'my-annotation'
104
+ },
105
+ systemAnnotations: ['cattle.io/system'],
106
+ setLabels: jest.fn(),
107
+ setAnnotations: jest.fn(),
108
+ };
109
+
110
+ it('should initialize with correct data properties', () => {
111
+ const wrapper = mount(Labels, {
112
+ props: {
113
+ value: mockValue,
114
+ mode: 'create',
115
+ },
116
+ global: { mocks: { t: mockT } },
117
+ });
118
+
119
+ const data = (wrapper.vm as any).$data;
120
+
121
+ expect(data.labels).toBeDefined();
122
+ expect(data.labels.initValue).toStrictEqual(mockValue.labels);
123
+ expect(data.labels.protectedKeys).toStrictEqual(mockValue.systemLabels);
124
+ expect(data.labels.value).toStrictEqual({ 'my-label': 'my-value' });
125
+ expect(data.labels.hasProtectedKeys).toBe(true);
126
+ expect(data.labels.keyErrors).toStrictEqual({}); // only when manually update
127
+
128
+ expect(data.annotations).toBeDefined();
129
+ expect(data.annotations.initValue).toStrictEqual(mockValue.annotations);
130
+ expect(data.annotations.protectedKeys).toStrictEqual(mockValue.systemAnnotations);
131
+ expect(data.annotations.value).toStrictEqual({ 'my-annotation': 'my-annotation' });
132
+ expect(data.annotations.hasProtectedKeys).toBe(true);
133
+ expect(data.annotations.keyErrors).toStrictEqual({}); // only when manually update
134
+
135
+ expect(data.toggler).toBe(false);
136
+ });
137
+
138
+ it('should render KeyValue for labels', () => {
139
+ const wrapper = mount(Labels, {
140
+ props: {
141
+ value: mockValue,
142
+ mode: 'create',
143
+ },
144
+ global: { mocks: { t: mockT } },
145
+ });
146
+
147
+ const labelsKeyValue = wrapper.findComponent(KeyValue);
148
+
149
+ expect(labelsKeyValue.exists()).toBe(true);
150
+ expect(labelsKeyValue.props('value')).toStrictEqual({ 'my-label': 'my-value' });
151
+ expect(labelsKeyValue.props('addLabel')).toBe('labels.addLabel');
152
+ expect(labelsKeyValue.props('mode')).toBe('create');
153
+ expect(labelsKeyValue.props('keyErrors')).toStrictEqual({});
154
+ });
155
+
156
+ it('should render KeyValue for annotations if showAnnotations is true', () => {
157
+ const wrapper = mount(Labels, {
158
+ props: {
159
+ value: mockValue,
160
+ mode: 'create',
161
+ showAnnotations: true,
162
+ },
163
+ global: { mocks: { t: mockT } },
164
+ });
165
+
166
+ const keyValueComponents = wrapper.findAllComponents(KeyValue);
167
+
168
+ expect(keyValueComponents).toHaveLength(2);
169
+ const annotationsKeyValue = keyValueComponents[1]; // The second KeyValue is for annotations
170
+
171
+ expect(annotationsKeyValue.exists()).toBe(true);
172
+ expect(annotationsKeyValue.props('value')).toStrictEqual({ 'my-annotation': 'my-annotation' });
173
+ expect(annotationsKeyValue.props('addLabel')).toBe('labels.addAnnotation');
174
+ expect(annotationsKeyValue.props('mode')).toBe('create');
175
+ expect(annotationsKeyValue.props('keyErrors')).toStrictEqual({});
176
+ });
177
+
178
+ it('should not render KeyValue for annotations if showAnnotations is false', () => {
179
+ const wrapper = mount(Labels, {
180
+ props: {
181
+ value: mockValue,
182
+ mode: 'create',
183
+ showAnnotations: false,
184
+ },
185
+ global: { mocks: { t: mockT } },
186
+ });
187
+
188
+ const keyValueComponents = wrapper.findAllComponents(KeyValue);
189
+
190
+ expect(keyValueComponents).toHaveLength(1); // Only the KeyValue for labels
191
+ });
192
+
193
+ it('should show ToggleSwitch in VIEW mode if there are protected keys', async() => {
194
+ const wrapper = mount(Labels, {
195
+ props: {
196
+ value: mockValue,
197
+ mode: _VIEW,
198
+ },
199
+ global: { mocks: { t: mockT } },
200
+ });
201
+
202
+ expect((wrapper.vm as any).showToggler).toBe(true);
203
+ const toggleSwitch = wrapper.findComponent(ToggleSwitch);
204
+
205
+ expect(toggleSwitch.exists()).toBe(true);
206
+ expect(toggleSwitch.props('onLabel')).toBe('labels.labels.show');
207
+ });
208
+
209
+ it('should not show ToggleSwitch in non-VIEW mode', () => {
210
+ const wrapper = mount(Labels, {
211
+ props: {
212
+ value: mockValue,
213
+ mode: _EDIT,
214
+ },
215
+ global: { mocks: { t: mockT } },
216
+ });
217
+
218
+ expect((wrapper.vm as any).showToggler).toBe(false);
219
+ expect(wrapper.findComponent(ToggleSwitch).exists()).toBe(false);
220
+ });
221
+
222
+ it('should not show ToggleSwitch in VIEW mode if no protected keys', () => {
223
+ const mockValueNoProtected = {
224
+ labels: { 'my-label': 'my-value' },
225
+ systemLabels: [],
226
+ annotations: { 'my-annotation': 'my-annotation' },
227
+ systemAnnotations: [],
228
+ setLabels: jest.fn(),
229
+ setAnnotations: jest.fn(),
230
+ };
231
+
232
+ const wrapper = mount(Labels, {
233
+ props: {
234
+ value: mockValueNoProtected,
235
+ mode: _VIEW,
236
+ },
237
+ global: { mocks: { t: mockT } },
238
+ });
239
+
240
+ expect((wrapper.vm as any).showToggler).toBe(false);
241
+ expect(wrapper.findComponent(ToggleSwitch).exists()).toBe(false);
242
+ });
243
+
244
+ it('should toggle label values when ToggleSwitch is activated', async() => {
245
+ const wrapper = mount(Labels, {
246
+ props: {
247
+ value: mockValue,
248
+ mode: _VIEW,
249
+ },
250
+ global: { mocks: { t: mockT } },
251
+ });
252
+
253
+ const labelsKeyValue = wrapper.findComponent(KeyValue);
254
+
255
+ expect(labelsKeyValue.props('value')).toStrictEqual({ 'my-label': 'my-value' }); // Initial: hidden protected
256
+
257
+ // Activate the toggle
258
+ await wrapper.findComponent(ToggleSwitch).vm.$emit('update:value', true);
259
+ await wrapper.vm.$nextTick();
260
+
261
+ expect((wrapper.vm as any).toggler).toBe(true);
262
+ expect(labelsKeyValue.props('value')).toStrictEqual(mockValue.labels); // Should show all labels including protected
263
+ });
264
+
265
+ it('should toggle annotation values when ToggleSwitch is activated', async() => {
266
+ const wrapper = mount(Labels, {
267
+ props: {
268
+ value: mockValue,
269
+ mode: _VIEW,
270
+ },
271
+ global: { mocks: { t: mockT } },
272
+ });
273
+
274
+ const annotationsKeyValue = wrapper.findAllComponents(KeyValue)[1];
275
+
276
+ expect(annotationsKeyValue.props('value')).toStrictEqual({ 'my-annotation': 'my-annotation' }); // Initial: hidden protected
277
+
278
+ // Activate the toggle
279
+ await wrapper.findComponent(ToggleSwitch).vm.$emit('update:value', true);
280
+ await wrapper.vm.$nextTick();
281
+
282
+ expect((wrapper.vm as any).toggler).toBe(true);
283
+ expect(annotationsKeyValue.props('value')).toStrictEqual(mockValue.annotations); // Should show all annotations including protected
284
+ });
285
+
286
+ it('should call setLabels when labels KeyValue emits update:value', async() => {
287
+ const wrapper = mount(Labels, {
288
+ props: {
289
+ value: mockValue,
290
+ mode: 'create',
291
+ },
292
+ global: { mocks: { t: mockT } },
293
+ });
294
+
295
+ const labelsKeyValue = wrapper.findComponent(KeyValue);
296
+ const newLabels = { 'new-label': 'new-value', 'my-label': 'my-value' };
297
+
298
+ await labelsKeyValue.vm.$emit('update:value', newLabels);
299
+
300
+ // We expect setLabels to be called with the new values, preserving initial protected ones
301
+ expect(mockValue.setLabels).toHaveBeenCalledWith({
302
+ 'new-label': 'new-value',
303
+ 'my-label': 'my-value',
304
+ 'kubernetes.io/hostname': 'test-app'
305
+ });
306
+ });
307
+
308
+ it('should call setAnnotations when annotations KeyValue emits update:value', async() => {
309
+ const wrapper = mount(Labels, {
310
+ props: {
311
+ value: mockValue,
312
+ mode: 'create',
313
+ showAnnotations: true,
314
+ },
315
+ global: { mocks: { t: mockT } },
316
+ });
317
+
318
+ const annotationsKeyValue = wrapper.findAllComponents(KeyValue)[1];
319
+ const newAnnotations = { 'new-anno': 'new-value', 'my-annotation': 'my-annotation' };
320
+
321
+ await annotationsKeyValue.vm.$emit('update:value', newAnnotations);
322
+
323
+ // We expect setAnnotations to be called with the new values, preserving initial protected ones
324
+ expect(mockValue.setAnnotations).toHaveBeenCalledWith({
325
+ 'new-anno': 'new-value',
326
+ 'my-annotation': 'my-annotation',
327
+ 'cattle.io/system': 'user'
328
+ });
329
+ });
330
+
331
+ it('should add newly entered protected keys to keyErrors', async() => {
332
+ const wrapper = mount(Labels, {
333
+ props: {
334
+ value: mockValue,
335
+ mode: 'create',
336
+ },
337
+ global: { mocks: { t: mockT } },
338
+ });
339
+
340
+ // Insert new protected key
341
+ const newLabels = {
342
+ 'my-label': 'my-value',
343
+ 'kubernetes.io/hostname': 'new-protected-value',
344
+ 'non-protected': 'value'
345
+ };
346
+
347
+ await wrapper.vm.labels.update(newLabels, () => {});
348
+ await wrapper.vm.$nextTick();
349
+
350
+ // Expect to add protected labels in keyErrors
351
+ expect((wrapper.vm as any).labels.keyErrors).toStrictEqual({ 'kubernetes.io/hostname': 'labels.protectedWarning' });
352
+
353
+ // On edit, should key the protected label in the list
354
+ expect((wrapper.vm as any).labels.value).toStrictEqual({
355
+ 'my-label': 'my-value',
356
+ 'kubernetes.io/hostname': 'new-protected-value',
357
+ 'non-protected': 'value'
358
+ });
359
+ });
360
+ });
@@ -53,19 +53,22 @@ describe('component: MatchExpressions', () => {
53
53
  it.each([
54
54
  'operator',
55
55
  ])('should emit an update on %p selection change', async(field) => {
56
- const wrapper = mount(MatchExpressions, {
57
- props: {
58
- mode: _CREATE,
59
- value: [
60
- {
61
- id: '123',
62
- key: '123',
63
- operator: 'anything',
64
- values: '123'
65
- }
66
- ]
67
- },
68
- });
56
+ const wrapper = mount(
57
+ MatchExpressions,
58
+ {
59
+ attachTo: document.body,
60
+ props: {
61
+ mode: _CREATE,
62
+ value: [
63
+ {
64
+ id: '123',
65
+ key: '123',
66
+ operator: 'anything',
67
+ values: '123'
68
+ }
69
+ ]
70
+ },
71
+ });
69
72
 
70
73
  const select = wrapper.find(`[data-testid="input-match-expression-${ field }-0"]`);
71
74
 
@@ -77,7 +77,8 @@ describe('select.vue', () => {
77
77
  value,
78
78
  label: 'some-label',
79
79
  options,
80
- searchable: true
80
+ searchable: true,
81
+ loading: false,
81
82
  }
82
83
  });
83
84
 
@@ -91,7 +92,9 @@ describe('select.vue', () => {
91
92
  await input.trigger('keydown.enter');
92
93
 
93
94
  // mimic pressing space on search box inside v-select
94
- await input.trigger('keydown.space', mockEvent);
95
+ const search = input.find('input');
96
+
97
+ await search.trigger('keydown.space', mockEvent);
95
98
 
96
99
  // eslint-disable-next-line
97
100
  expect(spyFocus).toHaveBeenCalled();
@@ -50,7 +50,7 @@ export default {
50
50
  :value="row.source.value || ''"
51
51
  />
52
52
  <template v-if="row.source.value && row.sourceSub.value">
53
- <div class="text-muted">
53
+ <div class="text-label">
54
54
  <Shortened
55
55
  long-value-key="sourceSub.value"
56
56
  :row="row"
@@ -182,7 +182,7 @@ export default {
182
182
  <div
183
183
  id="trigger"
184
184
  class="hs-popover__trigger"
185
- aria-role="button"
185
+ role="button"
186
186
  tabindex="0"
187
187
  :class="{expanded}"
188
188
  :aria-roledescription="t('workload.scaleWorkloads')"
@@ -0,0 +1,211 @@
1
+ <script lang="ts">
2
+ import { defineComponent } from 'vue';
3
+ import { _CREATE, _VIEW } from '@shell/config/query-params';
4
+ import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
5
+ import SelectCredential from '@shell/edit/provisioning.cattle.io.cluster/SelectCredential.vue';
6
+ import AsyncButton from '@shell/components/AsyncButton.vue';
7
+ import { mapGetters, Store } from 'vuex';
8
+ import { getGKEZones } from '@shell/components/google/util/gcp';
9
+
10
+ export default defineComponent({
11
+ name: 'GKEAccountAccess',
12
+
13
+ emits: ['update:isAuthenticated', 'error', 'update:project', 'cancel-credential', 'update:credential'],
14
+
15
+ components: {
16
+ LabeledInput,
17
+ SelectCredential,
18
+ AsyncButton
19
+ },
20
+
21
+ props: {
22
+ mode: {
23
+ type: String,
24
+ default: _CREATE
25
+ },
26
+
27
+ credential: {
28
+ type: String,
29
+ default: null
30
+ },
31
+
32
+ project: {
33
+ type: String,
34
+ default: ''
35
+ },
36
+
37
+ isAuthenticated: {
38
+ type: Boolean,
39
+ default: false
40
+ },
41
+ },
42
+
43
+ created() {
44
+ if (this.mode === _VIEW) {
45
+ this.$emit('update:isAuthenticated', true);
46
+ }
47
+ },
48
+
49
+ computed: {
50
+ ...mapGetters({ t: 'i18n/t' }),
51
+
52
+ CREATE(): string {
53
+ return _CREATE;
54
+ },
55
+
56
+ VIEW(): string {
57
+ return _VIEW;
58
+ },
59
+
60
+ isView():boolean {
61
+ return this.mode === _VIEW;
62
+ }
63
+
64
+ },
65
+
66
+ methods: {
67
+ async testProjectId(cb: (success: Boolean)=>{}) {
68
+ const store = this.$store as Store<any>;
69
+
70
+ try {
71
+ await getGKEZones(store, this.credential, this.project, {});
72
+
73
+ this.$emit('update:isAuthenticated', true);
74
+
75
+ // eslint-disable-next-line node/no-callback-literal
76
+ return cb(true);
77
+ } catch (e) {
78
+ this.$emit('error', e?.data || e);
79
+ this.$emit('update:isAuthenticated', false);
80
+
81
+ // eslint-disable-next-line node/no-callback-literal
82
+ return cb(false);
83
+ }
84
+ },
85
+
86
+ // gcp credentials include a project id - we can grab that and auto-fill to save users having to manually enter it
87
+ // this only applies to new credentials because of the way credential data is stored
88
+ parseNewCredential(e) {
89
+ const authJson = e?.googlecredentialConfig?.authEncodedJson;
90
+
91
+ if (authJson) {
92
+ try {
93
+ // eslint-disable-next-line camelcase
94
+ const { project_id:projectId } = JSON.parse(authJson);
95
+
96
+ if (projectId) {
97
+ this.$emit('update:project', projectId);
98
+ }
99
+ } catch (e) {
100
+
101
+ }
102
+ }
103
+ }
104
+ },
105
+ });
106
+ </script>
107
+
108
+ <template>
109
+ <div>
110
+ <div
111
+ :class="{'showing-form': !credential}"
112
+ class="credential-project"
113
+ >
114
+ <div
115
+ v-show="!!credential"
116
+ class="project mb-10"
117
+ >
118
+ <LabeledInput
119
+ :disabled="isAuthenticated"
120
+ :value="project"
121
+ label-key="gke.project.label"
122
+ required
123
+ @update:value="(val) => $emit('update:project', val.trim())"
124
+ />
125
+ </div>
126
+ <div
127
+ :class="{'view': isView}"
128
+ class="select-credential-container mb-10"
129
+ >
130
+ <SelectCredential
131
+ :value="credential"
132
+ data-testid="crugke-select-credential"
133
+ :mode="(isView|| isAuthenticated) ? VIEW : CREATE"
134
+ provider="gcp"
135
+ :default-on-cancel="true"
136
+ :showing-form="!credential"
137
+ class="select-credential"
138
+ :cancel="()=>$emit('cancel-credential')"
139
+ @update:value="$emit('update:credential', $event)"
140
+ @credential-created="parseNewCredential"
141
+ />
142
+ </div>
143
+ </div>
144
+ <div
145
+ v-if="!isView && !isAuthenticated"
146
+ class="row mt-10"
147
+ >
148
+ <div
149
+ v-show="!!credential"
150
+ class="auth-button-container mb-10"
151
+ >
152
+ <AsyncButton
153
+ :disabled="!credential || !project || isAuthenticated"
154
+ type="button"
155
+ class="btn"
156
+ mode="authenticate"
157
+ @click="testProjectId"
158
+ />
159
+ </div>
160
+ </div>
161
+ </div>
162
+ </template>
163
+
164
+ <style scoped lang="scss">
165
+ .credential-project {
166
+ display: flex;
167
+ min-width: 150px;
168
+ .project {
169
+ flex-basis: 49.25%;
170
+ flex-grow: 0;
171
+ margin: 0 1.75% 0 0;
172
+ }
173
+
174
+ &.showing-form {
175
+ flex-grow:1;
176
+ flex-direction: column;
177
+
178
+ &>.project {
179
+ margin: 0;
180
+ }
181
+
182
+ &>.select-credential-container{
183
+ display:flex;
184
+ flex-direction: column;
185
+ flex-grow: 1;
186
+ }
187
+ }
188
+
189
+ &>.select-credential-container{
190
+ flex-basis: 49.25%;
191
+
192
+ &.view{
193
+ margin: 0;
194
+ }
195
+
196
+ &>.select-credential{
197
+ flex-grow: 1;
198
+ }
199
+
200
+ }
201
+ }
202
+
203
+ .auth-button-container {
204
+ align-content: center;
205
+ width: 100%;
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: flex-start;
209
+ }
210
+
211
+ </style>