@rancher/shell 0.3.0 → 0.3.1

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 (322) hide show
  1. package/assets/styles/global/_button.scss +5 -1
  2. package/assets/styles/global/_columns.scss +4 -0
  3. package/assets/styles/global/_layout.scss +1 -2
  4. package/assets/styles/global/_select.scss +1 -4
  5. package/assets/styles/themes/_dark.scss +4 -4
  6. package/assets/styles/themes/_light.scss +4 -3
  7. package/assets/styles/themes/_suse.scss +1 -1
  8. package/assets/styles/vendor/vue-select.scss +4 -3
  9. package/assets/translations/en-us.yaml +669 -73
  10. package/assets/translations/zh-hans.yaml +547 -165
  11. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  12. package/cloud-credential/azure.vue +23 -0
  13. package/cloud-credential/harvester.vue +25 -62
  14. package/cloud-credential/pnap.vue +80 -0
  15. package/components/.DS_Store +0 -0
  16. package/components/AdvancedSection.vue +9 -2
  17. package/components/Alert.vue +2 -2
  18. package/components/ButtonDropdown.vue +0 -2
  19. package/components/ButtonGroup.vue +1 -0
  20. package/components/CollapsibleCard.vue +0 -1
  21. package/components/CruResource.vue +41 -4
  22. package/components/DetailTop.vue +58 -3
  23. package/components/DisableAuthProviderModal.vue +106 -0
  24. package/{rancher-components/components/Utils/DraggableZone → components}/DraggableZone.vue +0 -0
  25. package/components/ExplorerMembers.vue +253 -30
  26. package/components/ExplorerProjectsNamespaces.vue +77 -33
  27. package/components/GrowlManager.vue +3 -3
  28. package/components/IconOrSvg.vue +149 -0
  29. package/components/LogItem.vue +69 -0
  30. package/components/PodSecurityAdmission.vue +302 -0
  31. package/components/PromptModal.vue +1 -0
  32. package/components/ResourceDetail/Masthead.vue +54 -2
  33. package/components/ResourceDetail/index.vue +12 -5
  34. package/components/ResourceList/Masthead.vue +11 -1
  35. package/components/ResourceList/ResourceLoadingIndicator.vue +12 -2
  36. package/components/ResourceList/index.vue +53 -12
  37. package/components/ResourceList/resource-list.config.js +7 -0
  38. package/components/ResourceTable.vue +31 -6
  39. package/components/SimpleBox.vue +1 -1
  40. package/components/SortableTable/THead.vue +15 -5
  41. package/components/SortableTable/index.vue +21 -10
  42. package/components/Tabbed/index.vue +20 -15
  43. package/components/__tests__/.DS_Store +0 -0
  44. package/components/__tests__/AsyncButton.test.ts +140 -0
  45. package/components/__tests__/BackLink.test.ts +33 -0
  46. package/components/__tests__/ButtonGroup.test.ts +124 -0
  47. package/components/__tests__/ClusterBadge.test.ts +32 -0
  48. package/components/__tests__/CollapsibleCard.test.ts +64 -0
  49. package/components/__tests__/ConsumptionGauge.test.ts +88 -0
  50. package/components/__tests__/CruResource.test.ts +3 -2
  51. package/components/__tests__/FixedBanner.test.ts +129 -0
  52. package/components/__tests__/GrowlManager.test.ts +147 -0
  53. package/components/__tests__/NamespaceFilter.test.ts +33 -25
  54. package/components/__tests__/PercentageBar.test.ts +32 -0
  55. package/components/__tests__/PodSecurityAdmission.test.ts +398 -0
  56. package/components/auth/AuthBanner.vue +20 -10
  57. package/components/auth/RoleDetailEdit.vue +26 -17
  58. package/components/auth/SelectPrincipal.vue +36 -5
  59. package/components/form/ArrayList.vue +3 -35
  60. package/components/form/ArrayListGrouped.vue +13 -4
  61. package/components/form/ArrayListSelect.vue +5 -5
  62. package/components/form/Error.vue +8 -0
  63. package/components/form/KeyValue.vue +39 -7
  64. package/components/form/LabeledSelect.vue +5 -2
  65. package/components/form/Labels.vue +46 -16
  66. package/components/form/Members/ClusterPermissionsEditor.vue +17 -17
  67. package/components/form/Members/MembershipEditor.vue +12 -12
  68. package/components/form/NameNsDescription.vue +1 -1
  69. package/components/form/NodeScheduling.vue +1 -1
  70. package/components/form/Probe.vue +3 -3
  71. package/components/form/ResourceQuota/Project.vue +6 -6
  72. package/components/form/ResourceTabs/index.vue +1 -6
  73. package/components/form/Security.vue +7 -6
  74. package/components/form/Select.vue +3 -2
  75. package/components/form/SelectOrCreateAuthSecret.vue +22 -29
  76. package/components/form/ServicePorts.vue +8 -0
  77. package/components/form/WorkloadPorts.vue +7 -1
  78. package/components/form/__tests__/ArrayList.test.ts +74 -0
  79. package/components/form/__tests__/ArrayListGrouped.test.ts +6 -4
  80. package/components/formatter/Checked.vue +1 -1
  81. package/components/formatter/ClusterLink.vue +5 -0
  82. package/components/formatter/IconIsDefault.vue +2 -2
  83. package/components/formatter/InternalExternalIP.vue +11 -8
  84. package/components/formatter/LiveDuration.vue +78 -0
  85. package/components/formatter/WorkloadHealthScale.vue +5 -3
  86. package/components/nav/Header.vue +6 -3
  87. package/components/nav/NamespaceFilter.vue +146 -63
  88. package/components/nav/TopLevelMenu.vue +22 -19
  89. package/components/nav/WindowManager/ContainerLogs.vue +83 -126
  90. package/components/nav/WindowManager/ContainerShell.vue +9 -7
  91. package/components/nav/WindowManager/Window.vue +2 -0
  92. package/components/nav/WindowManager/index.vue +10 -0
  93. package/config/elemental-types.js +9 -0
  94. package/config/features.js +2 -0
  95. package/config/home-links.js +4 -1
  96. package/config/pod-security-admission.ts +82 -0
  97. package/config/product/apps.js +1 -1
  98. package/config/product/auth.js +6 -5
  99. package/config/product/explorer.js +6 -6
  100. package/config/product/fleet.js +1 -1
  101. package/config/product/manager.js +6 -2
  102. package/config/secret.js +0 -1
  103. package/config/settings.ts +26 -9
  104. package/config/table-headers.js +22 -11
  105. package/config/types.js +4 -1
  106. package/content/docs/zh-hans/getting-started.md +113 -137
  107. package/content/docs/zh-hans/whats-new.md +8 -46
  108. package/creators/pkg/package-lock.json +37 -0
  109. package/creators/pkg/package.json +1 -1
  110. package/detail/catalog.cattle.io.app.vue +1 -1
  111. package/detail/pod.vue +1 -1
  112. package/detail/provisioning.cattle.io.cluster.vue +35 -9
  113. package/detail/service.vue +2 -9
  114. package/detail/workload/index.vue +0 -1
  115. package/dialog/AddClusterMemberDialog.vue +22 -28
  116. package/dialog/AddProjectMemberDialog.vue +53 -9
  117. package/dialog/DiagnosticTimingsDialog.vue +8 -7
  118. package/dialog/DrainNode.vue +44 -48
  119. package/dialog/ForceMachineRemoveDialog.vue +5 -7
  120. package/dialog/GenericPrompt.vue +15 -20
  121. package/dialog/RollbackWorkloadDialog.vue +15 -46
  122. package/dialog/RotateCertificatesDialog.vue +5 -7
  123. package/dialog/RotateEncryptionKeyDialog.vue +5 -9
  124. package/dialog/SaveAsRKETemplateDialog.vue +5 -13
  125. package/dialog/ScaleMachineDownDialog.vue +1 -1
  126. package/dialog/ScalePoolDownDialog.vue +121 -0
  127. package/edit/__tests__/management.cattle.io.setting.test.ts +3 -3
  128. package/edit/auth/azuread.vue +16 -16
  129. package/edit/auth/github.vue +8 -0
  130. package/edit/auth/googleoauth.vue +10 -1
  131. package/edit/auth/ldap/index.vue +10 -0
  132. package/edit/auth/oidc.vue +10 -0
  133. package/edit/auth/saml.vue +10 -0
  134. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -1
  135. package/edit/cloudcredential.vue +3 -7
  136. package/edit/logging-flow/Match.vue +39 -8
  137. package/edit/logging-flow/index.vue +27 -4
  138. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +107 -0
  139. package/edit/management.cattle.io.project.vue +8 -1
  140. package/edit/management.cattle.io.setting.vue +5 -2
  141. package/edit/management.cattle.io.user.vue +7 -1
  142. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +23 -7
  143. package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +2 -2
  144. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +14 -6
  145. package/edit/namespace.vue +18 -4
  146. package/edit/networking.k8s.io.ingress/Certificate.vue +1 -0
  147. package/edit/networking.k8s.io.ingress/IngressClass.vue +8 -6
  148. package/edit/networking.k8s.io.ingress/RulePath.vue +12 -6
  149. package/edit/networking.k8s.io.ingress/index.vue +8 -6
  150. package/edit/persistentvolume/index.vue +30 -27
  151. package/edit/persistentvolume/plugins/cephfs.vue +29 -29
  152. package/edit/persistentvolume/plugins/csi.vue +102 -62
  153. package/edit/persistentvolume/plugins/fc.vue +19 -19
  154. package/edit/persistentvolume/plugins/iscsi.vue +45 -45
  155. package/edit/persistentvolume/plugins/rbd.vue +39 -39
  156. package/edit/persistentvolumeclaim.vue +78 -75
  157. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -7
  158. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +10 -1
  159. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +87 -27
  160. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -6
  161. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +93 -0
  162. package/edit/provisioning.cattle.io.cluster/import.vue +1 -1
  163. package/edit/provisioning.cattle.io.cluster/index.vue +29 -6
  164. package/edit/provisioning.cattle.io.cluster/rke2.vue +440 -152
  165. package/edit/secret/index.vue +3 -7
  166. package/edit/service.vue +3 -1
  167. package/edit/storage.k8s.io.storageclass/index.vue +100 -16
  168. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +114 -0
  169. package/edit/workload/__tests__/index.test.ts +98 -0
  170. package/edit/workload/index.vue +58 -8
  171. package/edit/workload/mixins/workload.js +107 -70
  172. package/edit/workload/storage/ContainerMountPaths.vue +0 -10
  173. package/edit/workload/storage/emptyDir.vue +88 -0
  174. package/edit/workload/storage/ephemeralVolume/index.vue +1 -1
  175. package/edit/workload/storage/index.vue +8 -0
  176. package/edit/workload/storage/persistentVolumeClaim/index.vue +1 -1
  177. package/layouts/default.vue +57 -44
  178. package/list/__tests__/workload.test.ts +5 -2
  179. package/list/catalog.cattle.io.app.vue +1 -0
  180. package/list/cis.cattle.io.clusterscan.vue +1 -0
  181. package/list/fleet.cattle.io.bundle.vue +5 -6
  182. package/list/fleet.cattle.io.cluster.vue +6 -3
  183. package/list/fleet.cattle.io.clusterregistrationtoken.vue +5 -6
  184. package/list/fleet.cattle.io.gitrepo.vue +4 -9
  185. package/list/helm.cattle.io.projecthelmchart.vue +1 -5
  186. package/list/logging.banzaicloud.io.clusterflow.vue +4 -1
  187. package/list/logging.banzaicloud.io.flow.vue +6 -5
  188. package/list/management.cattle.io.cluster.vue +1 -0
  189. package/list/management.cattle.io.feature.vue +3 -4
  190. package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +47 -0
  191. package/list/management.cattle.io.setting.vue +2 -2
  192. package/list/management.cattle.io.user.vue +4 -10
  193. package/list/monitoring.coreos.com.alertmanagerconfig.vue +2 -7
  194. package/list/node.vue +8 -5
  195. package/list/persistentvolume.vue +3 -3
  196. package/list/persistentvolumeclaim.vue +3 -4
  197. package/list/provisioning.cattle.io.cluster.vue +18 -19
  198. package/list/service.vue +6 -14
  199. package/list/workload.vue +43 -38
  200. package/machine-config/azure.vue +429 -60
  201. package/machine-config/pnap.vue +288 -0
  202. package/mixins/auth-config.js +1 -3
  203. package/mixins/browser-tab-visibility.js +8 -14
  204. package/mixins/chart.js +1 -1
  205. package/mixins/create-edit-view/impl.js +4 -0
  206. package/mixins/create-edit-view/index.js +4 -2
  207. package/mixins/resource-fetch-namespaced.js +98 -0
  208. package/mixins/resource-fetch.js +79 -45
  209. package/mixins/resource-manager.js +1 -23
  210. package/models/apps.controllerrevision.js +7 -0
  211. package/models/apps.daemonset.js +18 -0
  212. package/models/apps.deployment.js +44 -0
  213. package/models/apps.replicaset.js +7 -0
  214. package/models/apps.statefulset.js +18 -0
  215. package/models/batch.job.js +7 -14
  216. package/models/cluster/node.js +10 -2
  217. package/models/cluster.x-k8s.io.machine.js +26 -4
  218. package/models/cluster.x-k8s.io.machinedeployment.js +12 -2
  219. package/models/event.js +7 -0
  220. package/models/logging.banzaicloud.io.flow.js +4 -0
  221. package/models/management.cattle.io.cluster.js +1 -1
  222. package/models/management.cattle.io.clusterroletemplatebinding.js +1 -1
  223. package/models/management.cattle.io.globalrole.js +2 -2
  224. package/models/management.cattle.io.node.js +37 -2
  225. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +4 -0
  226. package/models/management.cattle.io.project.js +30 -11
  227. package/models/management.cattle.io.setting.js +1 -1
  228. package/models/management.cattle.io.user.js +37 -1
  229. package/models/namespace.js +42 -5
  230. package/models/persistentvolume.js +14 -2
  231. package/models/pod.js +15 -0
  232. package/models/projectroletemplatebinding.js +7 -0
  233. package/models/provisioning.cattle.io.cluster.js +61 -10
  234. package/models/rke-machine.cattle.io.pnapmachinetemplate.js +15 -0
  235. package/models/service.js +14 -13
  236. package/models/storage.k8s.io.storageclass.js +33 -18
  237. package/models/workload.js +38 -7
  238. package/nuxt.config.js +27 -17
  239. package/package.json +7 -7
  240. package/pages/about.vue +14 -2
  241. package/pages/c/_cluster/apps/charts/index.vue +4 -3
  242. package/pages/c/_cluster/apps/charts/install.vue +59 -22
  243. package/pages/c/_cluster/auth/config/_id.vue +6 -0
  244. package/pages/c/_cluster/auth/config/index.vue +8 -6
  245. package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
  246. package/pages/c/_cluster/auth/roles/index.vue +1 -1
  247. package/pages/c/_cluster/explorer/index.vue +12 -6
  248. package/pages/c/_cluster/longhorn/index.vue +1 -1
  249. package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +15 -4
  250. package/pages/c/_cluster/monitoring/index.vue +1 -1
  251. package/pages/c/_cluster/neuvector/index.vue +1 -1
  252. package/pages/c/_cluster/settings/performance.vue +48 -2
  253. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +34 -1
  254. package/pages/c/_cluster/uiplugins/index.vue +28 -2
  255. package/pages/diagnostic.vue +5 -4
  256. package/pages/home.vue +105 -30
  257. package/pages/prefs.vue +23 -12
  258. package/pages/rio/mesh.vue +1 -1
  259. package/pkg/dynamic-importer.lib.js +8 -0
  260. package/pkg/vue.config.js +4 -0
  261. package/plugins/dashboard-store/__tests__/mutations.spec.js +406 -0
  262. package/plugins/dashboard-store/actions.js +32 -25
  263. package/plugins/dashboard-store/getters.js +50 -33
  264. package/plugins/dashboard-store/mutations.js +134 -28
  265. package/plugins/dashboard-store/resource-class.js +21 -41
  266. package/plugins/steve/actions.js +30 -0
  267. package/plugins/steve/caches/resourceCache.js +60 -0
  268. package/plugins/steve/getters.js +44 -1
  269. package/plugins/steve/mutations.js +97 -36
  270. package/plugins/steve/resourceWatcher.js +277 -0
  271. package/plugins/steve/schema.utils.js +25 -0
  272. package/plugins/steve/subscribe.js +288 -115
  273. package/plugins/steve/worker/index.js +17 -0
  274. package/plugins/steve/worker/web-worker.advanced.js +302 -0
  275. package/plugins/steve/{web-worker.steve-sub-worker.js → worker/web-worker.basic.js} +3 -44
  276. package/rancher-components/Card/Card.vue +3 -3
  277. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -0
  278. package/rancher-components/StringList/StringList.test.ts +45 -420
  279. package/rancher-components/StringList/StringList.vue +1 -10
  280. package/rancher-components/components/Banner/Banner.test.ts +44 -0
  281. package/rancher-components/components/Banner/Banner.vue +129 -61
  282. package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +13 -22
  283. package/rancher-components/components/Form/Checkbox/Checkbox.vue +8 -6
  284. package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +9 -9
  285. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -1
  286. package/rancher-components/components/StringList/StringList.test.ts +7 -7
  287. package/rancher-components/components/StringList/StringList.vue +21 -15
  288. package/scripts/test-plugins-build.sh +8 -0
  289. package/static/loading-indicator.html +1 -1
  290. package/store/index.js +54 -3
  291. package/store/plugins.js +0 -17
  292. package/store/pnap.js +128 -0
  293. package/store/prefs.js +4 -2
  294. package/store/type-map.js +55 -13
  295. package/types/pod-security-admission.ts +36 -0
  296. package/types/shell/index.d.ts +496 -396
  297. package/utils/__tests__/object.test.ts +17 -1
  298. package/utils/__tests__/pod-security-admission.test.ts +61 -0
  299. package/utils/async.ts +36 -0
  300. package/utils/color.js +45 -0
  301. package/utils/crypto/browserHashUtils.js +18 -0
  302. package/utils/dynamic-importer.js +8 -0
  303. package/utils/install-redirect.js +1 -1
  304. package/utils/object.js +24 -0
  305. package/utils/pod-security-admission.ts +39 -0
  306. package/utils/socket.js +61 -24
  307. package/utils/string.js +2 -0
  308. package/utils/svg-filter.js +301 -0
  309. package/utils/time.js +49 -0
  310. package/utils/validators/cidr.js +4 -0
  311. package/utils/validators/formRules/__tests__/index.test.ts +23 -3
  312. package/utils/validators/formRules/index.ts +14 -0
  313. package/config/product/harvester-manager.js +0 -162
  314. package/edit/harvesterhci.io.management.cluster.vue +0 -153
  315. package/list/harvesterhci.io.management.cluster.vue +0 -241
  316. package/machine-config/harvester.vue +0 -693
  317. package/models/harvesterhci.io.management.cluster.js +0 -228
  318. package/pages/c/_cluster/harvesterManager/index.vue +0 -24
  319. package/rancher-components/Card/Card.test.ts +0 -39
  320. package/rancher-components/Utils/DraggableZone/DraggableZone.vue +0 -181
  321. package/rancher-components/Utils/DraggableZone/index.ts +0 -1
  322. package/rancher-components/components/Utils/DraggableZone/index.ts +0 -1
@@ -0,0 +1,124 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import ButtonGroup from '@shell/components/ButtonGroup.vue';
3
+
4
+ describe('component: ButtonGroup', () => {
5
+ it('should render component with the default props correctly', () => {
6
+ const options = [
7
+ {
8
+ label: 'label1',
9
+ value: 'val1'
10
+ },
11
+ {
12
+ label: 'label2',
13
+ value: 'val2'
14
+ },
15
+ {
16
+ label: 'label3',
17
+ value: 'val3'
18
+ },
19
+ ];
20
+
21
+ const wrapper = shallowMount(ButtonGroup, {
22
+ propsData: {
23
+ options,
24
+ value: 'val1'
25
+ },
26
+ directives: { 'trim-whitespace': jest.fn() },
27
+ });
28
+
29
+ const firstBtn = wrapper.find('[data-testid="button-group-child-0"]');
30
+ const firstBtnLabel = wrapper.find('[data-testid="button-group-child-0"] span');
31
+ const secondBtn = wrapper.find('[data-testid="button-group-child-1"]');
32
+ const secondBtnLabel = wrapper.find('[data-testid="button-group-child-1"] span');
33
+ const thirdBtn = wrapper.find('[data-testid="button-group-child-2"]');
34
+
35
+ expect(wrapper.findAll('button')).toHaveLength(3);
36
+ expect(firstBtn.exists()).toBe(true);
37
+ expect(firstBtn.attributes().type).toBe('button');
38
+ expect(firstBtn.classes()).toContain('btn');
39
+ expect(firstBtn.classes()).toContain('bg-primary');
40
+ expect(secondBtn.classes()).toContain('bg-disabled');
41
+ expect(thirdBtn.classes()).toContain('bg-disabled');
42
+
43
+ expect(firstBtnLabel.text()).toBe('label1');
44
+ expect(secondBtnLabel.text()).toBe('label2');
45
+ });
46
+
47
+ it('should render component with icon correctly', () => {
48
+ const options = [
49
+ {
50
+ label: 'label1',
51
+ value: 'val1',
52
+ icon: 'some-icon'
53
+ },
54
+ {
55
+ label: 'label2',
56
+ value: 'val2'
57
+ },
58
+ {
59
+ label: 'label3',
60
+ value: 'val3'
61
+ },
62
+ ];
63
+
64
+ const wrapper = shallowMount(ButtonGroup, {
65
+ propsData: {
66
+ options,
67
+ activeClass: 'bg-another-active-class',
68
+ inactiveClass: 'bg-some-inactive-class',
69
+ iconSize: 'xxxxl',
70
+ value: 'val1'
71
+ },
72
+ directives: { 'trim-whitespace': jest.fn() },
73
+ });
74
+
75
+ const firstBtn = wrapper.find('[data-testid="button-group-child-0"]');
76
+ const firstBtnIcon = wrapper.find('[data-testid="button-group-child-0"] i');
77
+ const firstBtnLabel = wrapper.find('[data-testid="button-group-child-0"] span');
78
+ const secondBtn = wrapper.find('[data-testid="button-group-child-1"]');
79
+
80
+ expect(wrapper.findAll('button')).toHaveLength(3);
81
+
82
+ expect(firstBtn.classes()).toContain('bg-another-active-class');
83
+ expect(secondBtn.classes()).toContain('bg-some-inactive-class');
84
+
85
+ expect(firstBtnLabel.exists()).toBe(true);
86
+ expect(firstBtnIcon.exists()).toBe(true);
87
+
88
+ expect(firstBtnIcon.classes()).toContain('icon');
89
+ expect(firstBtnIcon.classes()).toContain('some-icon');
90
+ expect(firstBtnIcon.classes()).toContain('icon-xxxxl');
91
+ });
92
+
93
+ it('clicking on button should emit event "input"', () => {
94
+ const options = [
95
+ {
96
+ label: 'label1',
97
+ value: 'val1'
98
+ },
99
+ {
100
+ label: 'label2',
101
+ value: 'val2'
102
+ },
103
+ {
104
+ label: 'label3',
105
+ value: 'val3'
106
+ },
107
+ ];
108
+
109
+ const wrapper = shallowMount(ButtonGroup, {
110
+ propsData: {
111
+ options,
112
+ value: 'val1'
113
+ },
114
+ directives: { 'trim-whitespace': jest.fn() },
115
+ });
116
+
117
+ const firstBtn = wrapper.find('[data-testid="button-group-child-0"]');
118
+
119
+ firstBtn.trigger('click');
120
+
121
+ expect(wrapper.emitted('input')).toHaveLength(1);
122
+ expect(wrapper.emitted('input')![0][0]).toBe('val1');
123
+ });
124
+ });
@@ -0,0 +1,32 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import ClusterBadge from '@shell/components/ClusterBadge.vue';
3
+
4
+ describe('component: ClusterBadge', () => {
5
+ it('should render component with the correct data applied', () => {
6
+ const clusterProp = {
7
+ badge: {
8
+ color: 'red',
9
+ textColor: 'blue',
10
+ text: 'some-text',
11
+ }
12
+ };
13
+
14
+ const wrapper = shallowMount(ClusterBadge, { propsData: { cluster: clusterProp } });
15
+
16
+ const elem = wrapper.find('div');
17
+
18
+ expect(elem.classes()).toContain('cluster-badge');
19
+ expect(elem.attributes().style).toBe('background-color: red; color: blue;');
20
+ expect(elem.text()).toBe('some-text');
21
+ });
22
+
23
+ it('should NOT render component if there is no badge property in object', () => {
24
+ const clusterProp = {};
25
+
26
+ const wrapper = shallowMount(ClusterBadge, { propsData: { cluster: clusterProp } });
27
+
28
+ const elem = wrapper.find('div');
29
+
30
+ expect(elem.exists()).toBe(false);
31
+ });
32
+ });
@@ -0,0 +1,64 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import CollapsibleCard from '@shell/components/CollapsibleCard.vue';
3
+
4
+ describe('component: CollapsibleCard', () => {
5
+ it('should render component with the correct data applied', () => {
6
+ const wrapper = shallowMount(CollapsibleCard, {
7
+ propsData: { title: 'some-card-title' },
8
+ slots: {
9
+ 'header-right': '<p>some stuff for header</p>',
10
+ content: '<p>some content</p>'
11
+ }
12
+ });
13
+
14
+ const cardWrapper = wrapper.find('.collapsible-card');
15
+ const cardHeader = wrapper.find('.collapsible-card-header');
16
+ const cardBody = wrapper.find('.collapsible-card-body');
17
+
18
+ expect(cardWrapper.exists()).toBe(true);
19
+
20
+ expect(cardHeader.exists()).toBe(true);
21
+ expect(cardHeader.find('h2 span').text()).toBe('some-card-title');
22
+ expect(cardHeader.find('p').exists()).toBe(true);
23
+ expect(cardHeader.find('p').text()).toBe('some stuff for header');
24
+ expect(cardHeader.find('i').exists()).toBe(true);
25
+ expect(cardHeader.find('i').classes()).toContain('icon-chevron-up');
26
+
27
+ expect(cardBody.exists()).toBe(true);
28
+ expect(cardBody.find('p').exists()).toBe(true);
29
+ expect(cardBody.find('p').text()).toBe('some content');
30
+ });
31
+
32
+ it('clicking on card header should emit event', () => {
33
+ const wrapper = shallowMount(CollapsibleCard, {
34
+ propsData: { title: 'some-card-title' },
35
+ slots: {
36
+ 'header-right': '<p>some stuff for header</p>',
37
+ content: '<p>some content</p>'
38
+ }
39
+ });
40
+
41
+ const cardHeader = wrapper.find('.collapsible-card-header');
42
+
43
+ cardHeader.trigger('click');
44
+
45
+ expect(wrapper.emitted('toggleCollapse')).toHaveLength(1);
46
+ expect(wrapper.emitted('toggleCollapse')![0][0]).toBe(true);
47
+ });
48
+
49
+ it('clicking on card title should emit event', () => {
50
+ const wrapper = shallowMount(CollapsibleCard, {
51
+ propsData: { title: 'some-card-title', isTitleClickable: true },
52
+ slots: {
53
+ 'header-right': '<p>some stuff for header</p>',
54
+ content: '<p>some content</p>'
55
+ }
56
+ });
57
+
58
+ const cardHeaderTitle = wrapper.find('.collapsible-card-header h2 span');
59
+
60
+ cardHeaderTitle.trigger('click');
61
+
62
+ expect(wrapper.emitted('titleClick')).toHaveLength(1);
63
+ });
64
+ });
@@ -0,0 +1,88 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import ConsumptionGauge from '@shell/components/ConsumptionGauge.vue';
3
+ import PercentageBar from '@shell/components/PercentageBar.vue';
4
+
5
+ describe('component: ConsumptionGauge', () => {
6
+ it('should render component with the correct data applied', () => {
7
+ const colorStops = {
8
+ 0: '--success', 30: '--warning', 70: '--error'
9
+ };
10
+
11
+ const wrapper = mount(ConsumptionGauge, {
12
+ propsData: {
13
+ resourceName: 'some-resource-name',
14
+ capacity: 1000,
15
+ used: 200,
16
+ units: 'cores',
17
+ colorStops
18
+ },
19
+ });
20
+
21
+ const mainWrapper = wrapper.find('.consumption-gauge');
22
+ const title = wrapper.find('.consumption-gauge h3');
23
+ const usedSpan = wrapper.find('.consumption-gauge .numbers span:nth-child(1)');
24
+ const percentageSpan = wrapper.find('.consumption-gauge .percentage');
25
+ const percentageBar = wrapper.findComponent(PercentageBar);
26
+
27
+ expect(mainWrapper.exists()).toBe(true);
28
+ expect(title.exists()).toBe(true);
29
+ expect(title.text()).toBe('some-resource-name');
30
+ expect(usedSpan.exists()).toBe(true);
31
+ // check translation key as for translation are not applied
32
+ expect(usedSpan.text()).toBe('%node.detail.glance.consumptionGauge.used%');
33
+
34
+ expect(percentageSpan.exists()).toBe(true);
35
+ expect(percentageSpan.text()).toContain('20%');
36
+
37
+ // checking PercentageBar component render
38
+ expect(percentageBar.exists()).toBe(true);
39
+ expect(percentageBar.props().value).toBe(20);
40
+ expect(percentageBar.props().colorStops).toBe(colorStops);
41
+ });
42
+
43
+ it('usedAsResourceName should render secondary title instead of main h3 title', () => {
44
+ const colorStops = {
45
+ 0: '--success', 30: '--warning', 70: '--error'
46
+ };
47
+
48
+ const wrapper = mount(ConsumptionGauge, {
49
+ propsData: {
50
+ resourceName: 'some-resource-name',
51
+ capacity: 1000,
52
+ used: 200,
53
+ units: 'cores',
54
+ colorStops,
55
+ usedAsResourceName: true
56
+ }
57
+ });
58
+
59
+ const mainTitle = wrapper.find('.consumption-gauge h3');
60
+ const slotTitle = wrapper.find('.consumption-gauge h4');
61
+
62
+ expect(mainTitle.exists()).toBe(false);
63
+ expect(slotTitle.exists()).toBe(true);
64
+ expect(slotTitle.text()).toBe('some-resource-name');
65
+ });
66
+
67
+ it('passing slot TITLE should render correctly', () => {
68
+ const colorStops = {
69
+ 0: '--success', 30: '--warning', 70: '--error'
70
+ };
71
+
72
+ const wrapper = mount(ConsumptionGauge, {
73
+ propsData: {
74
+ resourceName: 'some-resource-name',
75
+ capacity: 1000,
76
+ used: 200,
77
+ units: 'cores',
78
+ colorStops
79
+ },
80
+ slots: { title: '<p class="slot-class">another title</p>' }
81
+ });
82
+
83
+ const slotElem = wrapper.find('.consumption-gauge .slot-class');
84
+
85
+ expect(slotElem.exists()).toBe(true);
86
+ expect(slotElem.text()).toBe('another title');
87
+ });
88
+ });
@@ -30,7 +30,7 @@ describe('component: CruResource', () => {
30
30
  expect(element).toBeDefined();
31
31
  });
32
32
 
33
- it('should display errors', () => {
33
+ it('should display multiple errors', () => {
34
34
  const errors = ['mistake!', 'BiG MiStAke11'];
35
35
  const wrapper = mount(CruResource, {
36
36
  propsData: {
@@ -61,6 +61,7 @@ describe('component: CruResource', () => {
61
61
  const node = wrapper.find('#cru-errors');
62
62
 
63
63
  expect(node.element.childElementCount).toBe(errors.length);
64
- expect(node.text()).toBe(`${ errors[0] } ${ errors[1] }`);
64
+ expect(node.text()).toContain(errors[0]);
65
+ expect(node.text()).toContain(errors[1]);
65
66
  });
66
67
  });
@@ -0,0 +1,129 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import FixedBanner from '@shell/components/FixedBanner.vue';
3
+ import { ExtendedVue, Vue } from 'vue/types/vue';
4
+ import { DefaultProps } from 'vue/types/options';
5
+
6
+ const SETTING_NO_CONSENT = { value: '{"loginError":{"message":"","showMessage":"false"},"bannerHeader":{"background":" #EEEFF4","color":" #141419","textAlignment":"center","fontWeight":null,"fontStyle":null,"fontSize":"14px","textDecoration":null,"text":"SOME HEADER TEXT"},"bannerFooter":{"background":" #EEEFF4","color":" #141419","textAlignment":"center","fontWeight":null,"fontStyle":null,"fontSize":"14px","textDecoration":null,"text":"SOME FOOTER TEXT"},"bannerConsent":{"background":" #EEEFF4","color":" #141419","textAlignment":"center","fontWeight":null,"fontStyle":null,"fontSize":"14px","textDecoration":null,"text":null,"button":null},"showHeader":"true","showFooter":"true","showConsent":"false"}' };
7
+ const SETTING_WITH_CONSENT = { value: '{"loginError":{"message":"","showMessage":"false"},"bannerHeader":{"background":" #EEEFF4","color":" #141419","textAlignment":"center","fontWeight":null,"fontStyle":null,"fontSize":"14px","textDecoration":null,"text":"SOME HEADER TEXT"},"bannerFooter":{"background":" #EEEFF4","color":" #141419","textAlignment":"center","fontWeight":null,"fontStyle":null,"fontSize":"14px","textDecoration":null,"text":"SOME FOOTER TEXT"},"bannerConsent":{"background":" #EEEFF4","color":" #141419","textAlignment":"center","fontWeight":null,"fontStyle":null,"fontSize":"14px","textDecoration":null,"text":"SOME CONSENT TEXT" ,"button": "some-button-label"},"showHeader":"true","showFooter":"true","showConsent":"true"}' };
8
+
9
+ const parsedBannerStyle = {
10
+ 'background-color': ' #EEEFF4',
11
+ color: ' #141419',
12
+ 'font-size': '14px',
13
+ 'font-style': '',
14
+ 'font-weight': '',
15
+ 'text-align': 'center',
16
+ 'text-decoration': '',
17
+ };
18
+
19
+ const parsedBannerContent = {
20
+ background: ' #EEEFF4',
21
+ color: ' #141419',
22
+ fontSize: '14px',
23
+ fontStyle: null,
24
+ fontWeight: null,
25
+ textAlignment: 'center',
26
+ textDecoration: null,
27
+ };
28
+
29
+ describe('component: FixedBanner', () => {
30
+ it('should render HEADER fixed banner correctly', async() => {
31
+ const wrapper = shallowMount(FixedBanner as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
32
+ propsData: { header: true },
33
+ mocks: { $store: { getters: { 'management/byId': jest.fn() } } }
34
+ });
35
+
36
+ wrapper.setData({ bannerSetting: SETTING_NO_CONSENT });
37
+
38
+ await wrapper.vm.$nextTick();
39
+
40
+ expect(wrapper.vm.bannerStyle).toStrictEqual(parsedBannerStyle);
41
+
42
+ expect(wrapper.vm.banner).toStrictEqual(expect.objectContaining(parsedBannerContent));
43
+ expect(wrapper.vm.showHeader).toStrictEqual(true);
44
+
45
+ const bannerElem = wrapper.find('.banner');
46
+ const noArrayTextElem = wrapper.find('.banner > p');
47
+
48
+ expect(bannerElem.exists()).toBe(true);
49
+ expect(bannerElem.classes()).not.toContain('banner-consent');
50
+ expect(noArrayTextElem.exists()).toBe(true);
51
+ expect(noArrayTextElem.text()).toBe('SOME HEADER TEXT');
52
+ });
53
+
54
+ it('should render FOOTER fixed banner, as text array (newline char), correctly', async() => {
55
+ const wrapper = shallowMount(FixedBanner as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
56
+ propsData: { footer: true },
57
+ mocks: { $store: { getters: { 'management/byId': jest.fn() } } }
58
+ });
59
+
60
+ wrapper.setData({ bannerSetting: SETTING_NO_CONSENT });
61
+
62
+ await wrapper.vm.$nextTick();
63
+
64
+ expect(wrapper.vm.bannerStyle).toStrictEqual(parsedBannerStyle);
65
+
66
+ expect(wrapper.vm.banner).toStrictEqual(expect.objectContaining(parsedBannerContent));
67
+ expect(wrapper.vm.showFooter).toStrictEqual(true);
68
+
69
+ const bannerElem = wrapper.find('.banner');
70
+ const noArrayTextElem = wrapper.find('.banner > p');
71
+
72
+ expect(bannerElem.exists()).toBe(true);
73
+ expect(bannerElem.classes()).not.toContain('banner-consent');
74
+ expect(noArrayTextElem.exists()).toBe(true);
75
+ expect(noArrayTextElem.text()).toBe('SOME FOOTER TEXT');
76
+ });
77
+
78
+ it('should render CONSENT as a DIALOG correctly', async() => {
79
+ const wrapper = shallowMount(FixedBanner as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
80
+ propsData: { consent: true },
81
+ mocks: { $store: { getters: { 'management/byId': jest.fn() } } }
82
+ });
83
+
84
+ wrapper.setData({ bannerSetting: SETTING_WITH_CONSENT });
85
+
86
+ await wrapper.vm.$nextTick();
87
+
88
+ expect(wrapper.vm.bannerStyle).toStrictEqual(parsedBannerStyle);
89
+
90
+ expect(wrapper.vm.banner).toStrictEqual(expect.objectContaining(parsedBannerContent));
91
+ expect(wrapper.vm.showConsent).toStrictEqual(true);
92
+
93
+ const bannerElem = wrapper.find('.banner');
94
+ const bannerDialogGlassElem = wrapper.find('.banner-dialog-glass');
95
+ const bannerDialogElem = wrapper.find('.banner-dialog');
96
+ const bannerDialogFrameElem = wrapper.find('.banner-dialog-frame');
97
+ const buttonDialog = wrapper.find('.banner-dialog button');
98
+ const noArrayTextElem = wrapper.find('.banner > p');
99
+
100
+ expect(bannerElem.exists()).toBe(true);
101
+ expect(bannerDialogGlassElem.exists()).toBe(true);
102
+ expect(bannerDialogElem.exists()).toBe(true);
103
+ expect(bannerDialogFrameElem.exists()).toBe(true);
104
+ expect(buttonDialog.exists()).toBe(true);
105
+ expect(noArrayTextElem.exists()).toBe(true);
106
+ expect(noArrayTextElem.text()).toBe('SOME CONSENT TEXT');
107
+ });
108
+
109
+ it('clicking dialog button should hide dialog', async() => {
110
+ const wrapper = shallowMount(FixedBanner as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
111
+ propsData: { consent: true },
112
+ mocks: { $store: { getters: { 'management/byId': jest.fn() } } }
113
+ });
114
+
115
+ wrapper.setData({ bannerSetting: SETTING_WITH_CONSENT });
116
+
117
+ await wrapper.vm.$nextTick();
118
+
119
+ const buttonDialog = wrapper.find('.banner-dialog button');
120
+ const spy = jest.spyOn(wrapper.vm, 'hideDialog');
121
+
122
+ expect(wrapper.vm.showDialog).toBe(true);
123
+
124
+ buttonDialog.trigger('click');
125
+
126
+ expect(spy).toHaveBeenCalledTimes(1);
127
+ expect(wrapper.vm.showDialog).toBe(false);
128
+ });
129
+ });
@@ -0,0 +1,147 @@
1
+ import { shallowMount, createLocalVue } from '@vue/test-utils';
2
+ import GrowlManager from '@shell/components/GrowlManager.vue';
3
+ import Vuex from 'vuex';
4
+ import { ExtendedVue, Vue } from 'vue/types/vue';
5
+ import { DefaultProps } from 'vue/types/options';
6
+
7
+ const stackMock = [
8
+ {
9
+ title: 'growl1-title',
10
+ message: 'growl1-message',
11
+ icon: 'growl1-icon',
12
+ timeout: 5000,
13
+ id: 'growl1-id',
14
+ color: 'red'
15
+ },
16
+ {
17
+ title: 'growl2-title',
18
+ message: 'growl2-message',
19
+ icon: 'growl2-icon',
20
+ timeout: 5000,
21
+ id: 'growl2-id',
22
+ color: 'red'
23
+ }
24
+ ];
25
+
26
+ describe('component: GrowlManager', () => {
27
+ it('should render component with the correct data applied', async() => {
28
+ const mockStore = {
29
+ getters: { 'i18n/t': jest.fn() },
30
+ dispatch: () => jest.fn()
31
+ };
32
+
33
+ const wrapper = shallowMount(GrowlManager as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
34
+ computed: { stack: () => stackMock },
35
+ mocks: { $store: mockStore }
36
+ });
37
+
38
+ await wrapper.vm.$nextTick();
39
+
40
+ const growlMainContainer = wrapper.find('.growl-container');
41
+ const growlListContainer = wrapper.find('.growl-list');
42
+ const growlListItems = wrapper.findAll('.growl-list .growl');
43
+ const growlFirstItemIcon = wrapper.find('.growl-list [data-testid="growl-list-item-0"] .icon-container .icon');
44
+ const growlFirstItemTitle = wrapper.find('.growl-list [data-testid="growl-list-item-0"] .growl-text div');
45
+ const growlFirstItemMessage = wrapper.find('.growl-list [data-testid="growl-list-item-0"] .growl-text p');
46
+ const growlFirstItemClose = wrapper.find('.growl-list [data-testid="growl-list-item-0"] .close.icon');
47
+ const clearAllButton = wrapper.find('button[type="button"]');
48
+
49
+ expect(growlMainContainer.exists()).toBe(true);
50
+ expect(growlListContainer.exists()).toBe(true);
51
+ expect(growlListItems).toHaveLength(2);
52
+ expect(growlFirstItemIcon.classes()).toContain('icon-growl1-icon');
53
+ expect(growlFirstItemTitle.text()).toBe('growl1-title');
54
+ expect(growlFirstItemMessage.text()).toBe('growl1-message');
55
+ expect(growlFirstItemClose.exists()).toBe(true);
56
+ expect(clearAllButton.exists()).toBe(true);
57
+ });
58
+
59
+ it('clicking on individual growl close should dismiss the individual growl', async() => {
60
+ const mockStore = {
61
+ getters: { 'i18n/t': jest.fn() },
62
+ dispatch: () => jest.fn()
63
+ };
64
+
65
+ const wrapper = shallowMount(GrowlManager as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
66
+ computed: { stack: () => stackMock },
67
+ mocks: { $store: mockStore }
68
+ });
69
+
70
+ await wrapper.vm.$nextTick();
71
+
72
+ const growlFirstItemClose = wrapper.find('.growl-list [data-testid="growl-list-item-0"] .close.icon');
73
+ const spyClose = jest.spyOn(wrapper.vm, 'close');
74
+ const spyDispatch = jest.spyOn(mockStore, 'dispatch');
75
+
76
+ growlFirstItemClose.trigger('click');
77
+
78
+ expect(spyClose).toHaveBeenCalledTimes(1);
79
+ expect(spyClose).toHaveBeenCalledWith(stackMock[0]);
80
+ expect(spyDispatch).toHaveBeenCalledTimes(1);
81
+ });
82
+
83
+ it('clicking on clear all growls should dismiss all growls', async() => {
84
+ const mockStore = {
85
+ getters: { 'i18n/t': jest.fn() },
86
+ dispatch: () => jest.fn()
87
+ };
88
+
89
+ const wrapper = shallowMount(GrowlManager as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
90
+ computed: { stack: () => stackMock },
91
+ mocks: { $store: mockStore }
92
+ });
93
+
94
+ const clearAllButton = wrapper.find('button[type="button"]');
95
+ const spyCloseAll = jest.spyOn(wrapper.vm, 'closeAll');
96
+ const spyDispatch = jest.spyOn(mockStore, 'dispatch');
97
+
98
+ clearAllButton.trigger('click');
99
+
100
+ await wrapper.vm.$nextTick();
101
+
102
+ expect(spyCloseAll).toHaveBeenCalledTimes(1);
103
+ expect(spyDispatch).toHaveBeenCalledTimes(1);
104
+ });
105
+
106
+ it('growl should auto remove itself after set interval of 1 second', async() => {
107
+ const localVue = createLocalVue();
108
+
109
+ localVue.use(Vuex);
110
+
111
+ const store = new Vuex.Store({
112
+ modules: {
113
+ growl: {
114
+ namespaced: true,
115
+ state: { stack: [] },
116
+ actions: { clear: jest.fn() },
117
+ mutations: {
118
+ updateStack: (state) => {
119
+ state.stack = stackMock;
120
+ }
121
+ }
122
+ },
123
+ },
124
+ getters: { 'i18n/t': () => jest.fn() }
125
+ });
126
+
127
+ jest.useFakeTimers();
128
+
129
+ const wrapper = shallowMount(GrowlManager as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
130
+ store,
131
+ localVue
132
+ });
133
+
134
+ const spyCloseExpired = jest.spyOn(wrapper.vm, 'closeExpired');
135
+
136
+ expect(spyCloseExpired).not.toHaveBeenCalled();
137
+
138
+ // this is to trigger the watch so that autoRemove can do its part
139
+ store.commit('growl/updateStack');
140
+
141
+ await wrapper.vm.$nextTick();
142
+
143
+ jest.advanceTimersByTime(1001);
144
+
145
+ expect(spyCloseExpired).toHaveBeenCalledTimes(1);
146
+ });
147
+ });