@rancher/shell 3.0.2-rc.5 → 3.0.2

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 (219) hide show
  1. package/assets/images/providers/nutanix.svg +12 -1
  2. package/assets/styles/base/_basic.scss +2 -1
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +2 -0
  5. package/assets/styles/global/_labeled-input.scss +5 -13
  6. package/assets/styles/global/_layout.scss +4 -1
  7. package/assets/styles/global/_select.scss +5 -0
  8. package/assets/styles/themes/_dark.scss +1 -3
  9. package/assets/styles/themes/_light.scss +5 -1
  10. package/assets/translations/en-us.yaml +130 -23
  11. package/assets/translations/zh-hans.yaml +0 -3
  12. package/cloud-credential/azure.vue +1 -1
  13. package/components/ActionMenuShell.vue +105 -0
  14. package/components/AppModal.vue +2 -2
  15. package/components/AsyncButton.vue +2 -0
  16. package/components/ButtonGroup.vue +9 -2
  17. package/components/ClusterBadge.vue +1 -0
  18. package/components/ClusterIconMenu.vue +3 -0
  19. package/components/ClusterProviderIcon.vue +14 -1
  20. package/components/CodeMirror.vue +96 -5
  21. package/components/Collapse.vue +16 -3
  22. package/components/CruResource.vue +9 -0
  23. package/components/CruResourceFooter.vue +1 -1
  24. package/components/ExplorerMembers.vue +2 -1
  25. package/components/FixedBanner.vue +19 -12
  26. package/components/Import.vue +14 -1
  27. package/components/LandingPagePreference.vue +4 -2
  28. package/components/PodSecurityAdmission.vue +8 -6
  29. package/components/PromptChangePassword.vue +1 -0
  30. package/components/PromptRemove.vue +23 -21
  31. package/components/ResourceDetail/Masthead.vue +30 -11
  32. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  33. package/components/ResourceDetail/index.vue +6 -0
  34. package/components/ResourceTable.vue +6 -1
  35. package/components/ResourceYaml.vue +1 -0
  36. package/components/Setting.vue +115 -0
  37. package/components/SortableTable/THead.vue +2 -0
  38. package/components/SortableTable/index.vue +7 -12
  39. package/components/StatusBadge.vue +71 -0
  40. package/components/Tabbed/index.vue +16 -15
  41. package/components/Wizard.vue +108 -104
  42. package/components/YamlEditor.vue +12 -2
  43. package/components/__tests__/Collapse.test.ts +2 -2
  44. package/components/__tests__/FixedBanner.test.ts +3 -3
  45. package/components/auth/Principal.vue +29 -17
  46. package/components/auth/__tests__/Principal.test.ts +40 -0
  47. package/components/auth/login/ldap.vue +7 -0
  48. package/components/fleet/FleetBundles.vue +1 -1
  49. package/components/fleet/FleetRepos.vue +1 -1
  50. package/components/fleet/FleetResources.vue +0 -2
  51. package/components/fleet/FleetSummary.vue +60 -65
  52. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  53. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  54. package/components/form/ArrayList.vue +6 -2
  55. package/components/form/ColorInput.vue +1 -0
  56. package/components/form/KeyValue.vue +11 -12
  57. package/components/form/LabeledSelect.vue +15 -3
  58. package/components/form/Labels.vue +8 -1
  59. package/components/form/Members/MembershipEditor.vue +230 -222
  60. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  61. package/components/form/Password.vue +3 -0
  62. package/components/form/ProjectMemberEditor.vue +6 -3
  63. package/components/form/ResourceTabs/index.vue +15 -13
  64. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  65. package/components/form/SchedulingCustomization.vue +85 -0
  66. package/components/form/Select.vue +3 -2
  67. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  68. package/components/form/UnitInput.vue +3 -4
  69. package/components/form/__tests__/ArrayList.test.ts +9 -6
  70. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  71. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  72. package/components/form/__tests__/UnitInput.test.ts +4 -5
  73. package/components/formatter/LiveDate.vue +3 -1
  74. package/components/formatter/ServiceType.vue +12 -4
  75. package/components/formatter/WorkloadHealthScale.vue +2 -1
  76. package/components/nav/Header.vue +35 -2
  77. package/components/nav/HeaderPageActionMenu.vue +11 -40
  78. package/components/nav/Jump.vue +8 -2
  79. package/components/nav/NamespaceFilter.vue +5 -4
  80. package/components/nav/Pinned.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +5 -5
  82. package/components/nav/TopLevelMenu.vue +1 -12
  83. package/components/nav/WindowManager/ContainerLogs.vue +96 -58
  84. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  85. package/components/nav/WindowManager/index.vue +74 -6
  86. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  87. package/components/templates/default.vue +2 -47
  88. package/config/features.js +1 -0
  89. package/config/labels-annotations.js +11 -1
  90. package/config/router/navigation-guards/index.js +2 -1
  91. package/config/router/navigation-guards/record-last-route.js +24 -0
  92. package/config/settings.ts +66 -98
  93. package/config/version.js +1 -1
  94. package/core/types-provisioning.ts +7 -0
  95. package/detail/fleet.cattle.io.bundle.vue +7 -0
  96. package/detail/fleet.cattle.io.cluster.vue +0 -3
  97. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  98. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  99. package/dialog/DeactivateDriverDialog.vue +5 -5
  100. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  101. package/directives/strip-html-aria-label.js +19 -0
  102. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  103. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  104. package/edit/auth/__tests__/oidc.test.ts +60 -12
  105. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  106. package/edit/auth/ldap/config.vue +67 -89
  107. package/edit/auth/oidc.vue +16 -2
  108. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  109. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  110. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  111. package/edit/logging-flow/Match.vue +0 -21
  112. package/edit/management.cattle.io.project.vue +1 -1
  113. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  114. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  115. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  116. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  117. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +0 -2
  119. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  120. package/edit/provisioning.cattle.io.cluster/index.vue +28 -30
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +64 -13
  122. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  124. package/edit/resources.cattle.io.backup.vue +150 -15
  125. package/edit/secret/__tests__/ssh.test.ts +79 -0
  126. package/edit/secret/ssh.vue +7 -1
  127. package/edit/service.vue +0 -3
  128. package/edit/workload/Job.vue +8 -8
  129. package/edit/workload/__tests__/Job.test.ts +0 -1
  130. package/edit/workload/index.vue +3 -1
  131. package/initialize/install-directives.js +2 -0
  132. package/initialize/install-plugins.js +6 -1
  133. package/list/catalog.cattle.io.app.vue +21 -4
  134. package/list/fleet.cattle.io.bundle.vue +1 -1
  135. package/list/management.cattle.io.setting.vue +34 -132
  136. package/list/provisioning.cattle.io.cluster.vue +11 -3
  137. package/machine-config/vmwarevsphere.vue +15 -8
  138. package/mixins/__tests__/auth-config.test.ts +74 -0
  139. package/mixins/__tests__/chart.test.ts +5 -4
  140. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  141. package/mixins/auth-config.js +8 -0
  142. package/mixins/chart.js +2 -2
  143. package/mixins/create-edit-view/impl.js +4 -1
  144. package/mixins/vue-select-overrides.js +10 -0
  145. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  146. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  147. package/models/__tests__/secret.test.ts +56 -13
  148. package/models/catalog.cattle.io.app.js +112 -37
  149. package/models/cluster.js +11 -0
  150. package/models/fleet.cattle.io.bundle.js +40 -2
  151. package/models/fleet.cattle.io.gitrepo.js +169 -109
  152. package/models/management.cattle.io.fleetworkspace.js +4 -0
  153. package/models/management.cattle.io.kontainerdriver.js +7 -0
  154. package/models/nodedriver.js +4 -1
  155. package/models/provisioning.cattle.io.cluster.js +24 -0
  156. package/models/secret.js +1 -1
  157. package/package.json +5 -5
  158. package/pages/auth/login.vue +5 -11
  159. package/pages/auth/verify.vue +11 -1
  160. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  161. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  162. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  163. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  164. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  165. package/pages/c/_cluster/explorer/index.vue +33 -35
  166. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  167. package/pages/c/_cluster/fleet/index.vue +0 -5
  168. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  169. package/pages/c/_cluster/settings/performance.vue +52 -53
  170. package/pages/c/_cluster/uiplugins/index.vue +19 -22
  171. package/pages/home.vue +17 -12
  172. package/pages/prefs.vue +5 -1
  173. package/plugins/shortkey.js +10 -1
  174. package/plugins/steve/steve-pagination-utils.ts +58 -8
  175. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  176. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  177. package/promptRemove/management.cattle.io.project.vue +2 -8
  178. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  179. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  180. package/promptRemove/pod.vue +7 -28
  181. package/rancher-components/Card/Card.vue +9 -1
  182. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  183. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  184. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  185. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  186. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  187. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  188. package/rancher-components/RcButton/RcButton.vue +14 -9
  189. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  190. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -2
  191. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  192. package/rancher-components/RcDropdown/index.ts +1 -0
  193. package/rancher-components/RcDropdown/types.ts +27 -0
  194. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  195. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
  196. package/scripts/typegen.sh +1 -0
  197. package/store/__tests__/auth.test.ts +120 -0
  198. package/store/action-menu.js +13 -3
  199. package/store/auth.js +14 -9
  200. package/store/aws.js +9 -2
  201. package/store/catalog.js +14 -7
  202. package/store/features.js +1 -0
  203. package/store/prefs.js +9 -28
  204. package/store/type-map.utils.ts +4 -0
  205. package/types/resources/settings.d.ts +27 -20
  206. package/types/shell/index.d.ts +18 -12
  207. package/utils/__tests__/array.test.ts +13 -1
  208. package/utils/__tests__/string.test.ts +80 -1
  209. package/utils/array.ts +13 -0
  210. package/utils/auth.js +4 -0
  211. package/utils/banners.js +0 -45
  212. package/utils/cluster.js +1 -1
  213. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  214. package/utils/object.js +0 -3
  215. package/utils/pagination-utils.ts +15 -2
  216. package/utils/string.js +31 -7
  217. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  218. package/utils/validators/formRules/index.ts +16 -0
  219. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -0,0 +1,132 @@
1
+ <script>
2
+ import { mapGetters, mapState } from 'vuex';
3
+ import { resourceNames } from '@shell/utils/string';
4
+ import { exceptionToErrorsArray } from '@shell/utils/error';
5
+ import { Banner } from '@components/Banner';
6
+ import { Card } from '@components/Card';
7
+ import AsyncButton from '@shell/components/AsyncButton';
8
+
9
+ export default {
10
+ name: 'GitRepoForceUpdateDialog',
11
+
12
+ emits: ['close'],
13
+
14
+ components: {
15
+ AsyncButton,
16
+ Banner,
17
+ Card,
18
+ },
19
+
20
+ props: {
21
+ repositories: {
22
+ type: Array,
23
+ required: true,
24
+ },
25
+ },
26
+
27
+ data() {
28
+ return { errors: [] };
29
+ },
30
+
31
+ computed: {
32
+ ...mapState('action-menu', ['toRemove']),
33
+ ...mapGetters({ t: 'i18n/t' }),
34
+
35
+ names() {
36
+ return this.repositories.map((repository) => repository.nameDisplay);
37
+ }
38
+ },
39
+
40
+ methods: {
41
+ resourceNames,
42
+
43
+ close(buttonDone) {
44
+ if (buttonDone && typeof buttonDone === 'function') {
45
+ buttonDone(true);
46
+ }
47
+ this.$emit('close');
48
+ },
49
+
50
+ async apply(buttonDone) {
51
+ try {
52
+ await Promise.all(this.repositories.map((repository) => this.updateGitRepo(repository)));
53
+
54
+ this.close();
55
+ } catch (err) {
56
+ this.errors = exceptionToErrorsArray(err);
57
+ buttonDone(false);
58
+ }
59
+ },
60
+
61
+ updateGitRepo(repository) {
62
+ const now = repository.spec.forceSyncGeneration || 1;
63
+
64
+ repository.spec.forceSyncGeneration = now + 1;
65
+
66
+ return repository.save();
67
+ },
68
+ }
69
+ };
70
+ </script>
71
+
72
+ <template>
73
+ <Card
74
+ class="prompt-force-update"
75
+ :show-highlight-border="false"
76
+ >
77
+ <template #title>
78
+ <h4
79
+ v-clean-html="t('fleet.gitRepo.actions.forceUpdate.promptTitle')"
80
+ class="text-default-text"
81
+ />
82
+ </template>
83
+
84
+ <template #body>
85
+ <div class="mb-20">
86
+ {{ t('fleet.gitRepo.actions.forceUpdate.promptNames') }} <span
87
+ v-clean-html="resourceNames(names, t)"
88
+ class="body"
89
+ />
90
+ </div>
91
+ <Banner
92
+ color="info"
93
+ label-key="fleet.gitRepo.actions.forceUpdate.promptWarning"
94
+ >
95
+ <span v-clean-html="t('fleet.gitRepo.actions.forceUpdate.promptWarning', { count: names.length}, true)" />
96
+ </Banner>
97
+ <Banner
98
+ v-for="(err, i) in errors"
99
+ :key="i"
100
+ color="error"
101
+ :label="err"
102
+ />
103
+ </template>
104
+
105
+ <template #actions>
106
+ <button
107
+ class="btn role-secondary"
108
+ @click="close"
109
+ >
110
+ {{ t('generic.cancel') }}
111
+ </button>
112
+ <div class="spacer" />
113
+ <AsyncButton
114
+ mode="update"
115
+ class="btn bg-info ml-10"
116
+ :data-testid="'deactivate-driver-confirm'"
117
+ @click="apply"
118
+ />
119
+ </template>
120
+ </Card>
121
+ </template>
122
+
123
+ <style lang='scss' scoped>
124
+ .card-container {
125
+ box-shadow: none;
126
+ }
127
+
128
+ :deep() .card-actions {
129
+ display: flex;
130
+ justify-content: end;
131
+ }
132
+ </style>
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Vue directive that strips HTML tags from a string and uses the output as an "aria-label" HTML attribute
3
+ */
4
+ export default {
5
+ mounted(el, binding) {
6
+ if (typeof binding.value === 'string') {
7
+ const htmlStrippedAriaLabelString = binding.value.replace(/<\/?[^>]+(>|$)/g, '');
8
+
9
+ el.setAttribute('aria-label', htmlStrippedAriaLabelString);
10
+ }
11
+ },
12
+ updated(el, binding) {
13
+ if (typeof binding.value === 'string') {
14
+ const htmlStrippedAriaLabelString = binding.value.replace(/<\/?[^>]+(>|$)/g, '');
15
+
16
+ el.setAttribute('aria-label', htmlStrippedAriaLabelString);
17
+ }
18
+ }
19
+ };
@@ -0,0 +1,87 @@
1
+ import { mount, type VueWrapper } from '@vue/test-utils';
2
+ import cisEditor from '@shell/edit/cis.cattle.io.clusterscan.vue';
3
+
4
+ describe('view: cisEditor', () => {
5
+ let wrapper: VueWrapper<any, any>;
6
+
7
+ beforeEach(() => {
8
+ wrapper = mount(cisEditor, {
9
+ props: {
10
+ value: {
11
+ spec: { scanProfileName: null, canBeScheduled: () => true },
12
+ canBeScheduled: () => true,
13
+ }
14
+ },
15
+ global: {
16
+ mocks: {
17
+ $fetchState: { pending: false },
18
+ $route: {
19
+ name: 'whatever',
20
+ query: { AS: 'whatever' },
21
+ },
22
+ $store: {
23
+ getters: {
24
+ 'i18n/t': (text: string) => text,
25
+ currentStore: () => 'whatever',
26
+ 'whatever/schemaFor': jest.fn(),
27
+ },
28
+ dispatch: jest.fn(),
29
+ },
30
+ },
31
+ stubs: { AsyncButton: true }
32
+ }
33
+ });
34
+ });
35
+
36
+ it('renders the component', () => {
37
+ expect(wrapper.exists()).toBe(true);
38
+ });
39
+
40
+ describe('should prevent to save', () => {
41
+ it('given no Profile name', () => {
42
+ const saveButton = wrapper.find('[data-testid="form-save"]');
43
+
44
+ expect(saveButton.attributes().disabled).toStrictEqual('true');
45
+ });
46
+
47
+ it('given a wrong schedule format', async() => {
48
+ wrapper.vm.value.spec.scanProfileName = 'this is valid';
49
+ wrapper.vm.value.spec.scheduledScanConfig = { cronSchedule: 'this is not', scanAlertRule: { } };
50
+ wrapper.vm.isScheduled = true; // Not assigned by fetch()
51
+ wrapper.vm.scheduledScanConfig = { cronSchedule: 'this is not', scanAlertRule: { } }; // Not assigned by fetch()
52
+ wrapper.vm.scanAlertRule = {}; // Not assigned by fetch()
53
+ await wrapper.vm.$nextTick();
54
+
55
+ const saveButton = wrapper.find('[data-testid="form-save"]');
56
+
57
+ expect(saveButton.attributes().disabled).toStrictEqual('true');
58
+ });
59
+ });
60
+
61
+ describe('should allow to save', () => {
62
+ it('given a scanProfileName', async() => {
63
+ wrapper.vm.value.spec.scanProfileName = 'this is valid';
64
+ await wrapper.vm.$nextTick();
65
+
66
+ const saveButton = wrapper.find('[data-testid="form-save"]');
67
+
68
+ expect(saveButton.attributes().disabled).toStrictEqual('false');
69
+ });
70
+
71
+ it.each([
72
+ '0 * * * *',
73
+ '@daily'
74
+ ])('given a scanProfileName and a schedule %p', async(cronSchedule) => {
75
+ wrapper.vm.value.spec.scanProfileName = 'this is valid';
76
+ wrapper.vm.value.spec.scheduledScanConfig = { cronSchedule, scanAlertRule: { } };
77
+ wrapper.vm.scheduledScanConfig = { cronSchedule, scanAlertRule: {} }; // Not assigned by fetch()
78
+ wrapper.vm.scanAlertRule = {}; // Not assigned by fetch()
79
+ wrapper.vm.isScheduled = true; // Not assigned by fetch()
80
+ await wrapper.vm.$nextTick();
81
+
82
+ const saveButton = wrapper.find('[data-testid="form-save"]');
83
+
84
+ expect(saveButton.attributes().disabled).toStrictEqual('false');
85
+ });
86
+ });
87
+ });
@@ -1,59 +1,104 @@
1
1
  import { mount } from '@vue/test-utils';
2
+ import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
2
3
  import GitRepo from '@shell/edit/fleet.cattle.io.gitrepo.vue';
3
4
 
4
- describe('view: fleet.cattle.io.gitrepo should', () => {
5
- const mockStore = {
6
- dispatch: jest.fn(),
7
- getters: {
8
- 'i18n/t': (text: string) => text,
9
- t: (text: string) => text,
10
- currentStore: () => 'current_store',
11
- 'current_store/schemaFor': jest.fn(),
12
- 'current_store/all': jest.fn(),
13
- workspace: jest.fn(),
14
- }
15
- };
16
- const mocks = {
17
- $store: mockStore,
18
- $fetchState: { pending: false },
19
- $route: {
20
- query: { AS: '' },
21
- name: {
22
- endsWith: () => {
23
- return false;
24
- }
5
+ const mockStore = {
6
+ dispatch: jest.fn(),
7
+ getters: {
8
+ 'i18n/t': (text: string) => text,
9
+ 'i18n/exists': jest.fn(),
10
+ t: (text: string) => text,
11
+ currentStore: () => 'current_store',
12
+ 'current_store/schemaFor': jest.fn(),
13
+ 'current_store/all': jest.fn(),
14
+ workspace: jest.fn(),
15
+ }
16
+ };
17
+ const mocks = {
18
+ $store: mockStore,
19
+ $fetchState: { pending: false },
20
+ $route: {
21
+ query: { AS: '' },
22
+ name: {
23
+ endsWith: () => {
24
+ return false;
25
25
  }
26
- },
27
- };
28
- const values = {
29
- metadata: { namespace: 'test' }, spec: { template: {}, correctDrift: { enabled: false } }, targetInfo: { mode: 'all' },
30
- };
26
+ }
27
+ },
28
+ };
29
+ const mockComputed = {
30
+ ...GitRepo.computed,
31
+ steps: () => [{
32
+ name: 'stepAdvanced',
33
+ title: 'title',
34
+ label: 'label',
35
+ subtext: 'subtext',
36
+ descriptionKey: 'description',
37
+ ready: true,
38
+ weight: 1,
39
+ }],
40
+ };
41
+
42
+ const values = {
43
+ metadata: { namespace: 'test' },
44
+ spec: {
45
+ template: {},
46
+ correctDrift: { enabled: false },
47
+ },
48
+ targetInfo: { mode: 'all' },
49
+ };
50
+
51
+ describe('view: fleet.cattle.io.gitrepo, mode: view - should', () => {
52
+ it('hide advanced options banner', () => {
53
+ const wrapper = mount(GitRepo, {
54
+ props: { value: values, mode: _VIEW },
55
+ computed: mockComputed,
56
+ global: { mocks }
57
+ });
58
+
59
+ const advancedInfoBanner = wrapper.find('[data-testid="gitrepo-advanced-info"]');
60
+
61
+ expect(advancedInfoBanner.exists()).toBeFalsy();
62
+ });
63
+ });
64
+
65
+ describe.each([
66
+ _CREATE,
67
+ _EDIT,
68
+ ])('view: fleet.cattle.io.gitrepo, mode: %p - should', (mode) => {
31
69
  const wrapper = mount(GitRepo, {
32
- props: { value: values },
33
- global: { mocks }
70
+ props: { value: values, mode },
71
+ computed: mockComputed,
72
+ global: { mocks }
73
+ });
74
+
75
+ it('show advanced options banner', () => {
76
+ const advancedInfoBanner = wrapper.find('[data-testid="gitrepo-advanced-info"]');
77
+
78
+ expect(advancedInfoBanner.exists()).toBeTruthy();
34
79
  });
35
80
 
36
- it('should have self-healing checkbox and tooltip', () => {
37
- const correctDriftCheckbox = wrapper.find('[data-testid="GitRepo-correctDrift-checkbox"]');
38
- const tooltip = wrapper.find('[data-testid="GitRepo-correctDrift-checkbox"]');
81
+ it('have self-healing checkbox and tooltip', () => {
82
+ const correctDriftCheckbox = wrapper.find('[data-testid="gitRepo-correctDrift-checkbox"]');
83
+ const tooltip = wrapper.find('[data-testid="gitRepo-correctDrift-checkbox"]');
39
84
 
40
85
  expect(tooltip.element.classList).toContain('v-popper--has-tooltip');
41
86
  expect(correctDriftCheckbox.exists()).toBeTruthy();
42
87
  expect(correctDriftCheckbox.attributes().value).toBeFalsy();
43
88
  });
44
89
 
45
- it('should have keep-resources checkbox and tooltip', () => {
46
- const correctDriftCheckbox = wrapper.find('[data-testid="GitRepo-keepResources-checkbox"]');
47
- const tooltip = wrapper.find('[data-testid="GitRepo-keepResources-checkbox"]');
90
+ it('have keep-resources checkbox and tooltip', () => {
91
+ const correctDriftCheckbox = wrapper.find('[data-testid="gitRepo-keepResources-checkbox"]');
92
+ const tooltip = wrapper.find('[data-testid="gitRepo-keepResources-checkbox"]');
48
93
 
49
94
  expect(tooltip.element.classList).toContain('v-popper--has-tooltip');
50
95
  expect(correctDriftCheckbox.exists()).toBeTruthy();
51
96
  expect(correctDriftCheckbox.attributes().value).toBeFalsy();
52
97
  });
53
98
 
54
- it('should enable drift if self-healing is checked', async() => {
55
- const correctDriftCheckbox = wrapper.findComponent('[data-testid="GitRepo-correctDrift-checkbox"]');
56
- const correctDriftContainer = wrapper.find('[data-testid="GitRepo-correctDrift-checkbox"] .checkbox-container');
99
+ it('enable drift if self-healing is checked', async() => {
100
+ const correctDriftCheckbox = wrapper.findComponent('[data-testid="gitRepo-correctDrift-checkbox"]') as any;
101
+ const correctDriftContainer = wrapper.find('[data-testid="gitRepo-correctDrift-checkbox"] .checkbox-container');
57
102
 
58
103
  expect(correctDriftContainer.exists()).toBeTruthy();
59
104
 
@@ -63,4 +108,139 @@ describe('view: fleet.cattle.io.gitrepo should', () => {
63
108
  expect(correctDriftCheckbox.emitted('update:value')![0][0]).toBe(true);
64
109
  expect(correctDriftCheckbox.props().value).toBeTruthy();
65
110
  });
111
+
112
+ it.each([
113
+ ['show Polling Interval and warnings', 'enabled', undefined, true],
114
+ ['show Polling Interval and warnings', 'enabled', false, true],
115
+ ['hide Polling Interval and warnings', 'disabled', true, false],
116
+ ])('show Enable Polling checkbox and %p if %p, with spec.disablePolling: %p', (
117
+ descr1,
118
+ descr2,
119
+ disablePolling,
120
+ enabled
121
+ ) => {
122
+ const wrapper = mount(GitRepo, {
123
+ props: {
124
+ value: {
125
+ ...values,
126
+ spec: {
127
+ disablePolling,
128
+ pollingInterval: 10
129
+ },
130
+ status: { webhookCommit: 'sha' },
131
+ },
132
+ realMode: mode
133
+ },
134
+ computed: mockComputed,
135
+ global: { mocks },
136
+ });
137
+
138
+ const pollingCheckbox = wrapper.findComponent('[data-testid="gitRepo-enablePolling-checkbox"]') as any;
139
+ const pollingIntervalInput = wrapper.find('[data-testid="gitRepo-pollingInterval-input"]');
140
+ const pollingIntervalMinimumValueWarning = wrapper.find('[data-testid="gitRepo-pollingInterval-minimumValueWarning"]');
141
+ const pollingIntervalWebhookWarning = wrapper.find('[data-testid="gitRepo-pollingInterval-webhookWarning"]');
142
+
143
+ expect(pollingIntervalMinimumValueWarning.exists()).toBe(enabled);
144
+ expect(pollingIntervalWebhookWarning.exists()).toBe(enabled);
145
+ expect(pollingCheckbox.exists()).toBeTruthy();
146
+ expect(pollingCheckbox.vm.value).toBe(enabled);
147
+ expect(pollingIntervalInput.exists()).toBe(enabled);
148
+ });
149
+
150
+ const defaultPollingInterval = mode === _CREATE ? '60' : '15';
151
+
152
+ it.each([
153
+ ['null', `default ${ defaultPollingInterval } seconds`, null, defaultPollingInterval],
154
+ ['0', `default ${ defaultPollingInterval } seconds`, 0, defaultPollingInterval],
155
+ ['1', 'custom 1 second', 1, '1'],
156
+ ['60', 'custom 60 seconds', 60, '60'],
157
+ ['15', 'custom 15 seconds', 15, '15'],
158
+ ['0s', `default ${ defaultPollingInterval } seconds`, 0, defaultPollingInterval],
159
+ ['1s', 'custom 1 second', '1s', '1'],
160
+ ['60s', 'custom 60 seconds', '1m', '60'],
161
+ ['1m3s', 'custom 63 seconds', '1m3s', '63'],
162
+ ['1h2m3s', 'custom 3723 seconds', '1h2m3s', '3723'],
163
+ ['15', 'custom 15 seconds', '15s', '15'],
164
+ ])('show Polling Interval input with source: %p, value: %p', async(
165
+ descr1,
166
+ descr2,
167
+ pollingInterval,
168
+ unitValue,
169
+ ) => {
170
+ const wrapper = mount(GitRepo, {
171
+ props: {
172
+ value: {
173
+ ...values,
174
+ spec: { pollingInterval }
175
+ },
176
+ realMode: mode
177
+ },
178
+ computed: mockComputed,
179
+ global: { mocks },
180
+ });
181
+
182
+ const pollingIntervalInput = wrapper.find('[data-testid="gitRepo-pollingInterval-input"]').element as any;
183
+
184
+ expect(pollingIntervalInput).toBeDefined();
185
+ expect(pollingIntervalInput.value).toBe(unitValue);
186
+ });
187
+
188
+ it.each([
189
+ ['hide', 'source: null, value: equal to 60', null, false],
190
+ ['hide', 'source: 0, value: equal to 60', 0, false],
191
+ ['hide', 'source: 15, value: equal to 15', 15, false],
192
+ ['hide', 'source: 60, value: equal to 60', 60, false],
193
+ ['hide', 'source: 16, value: higher than 15', 16, false],
194
+ ['show', 'source: 1, value: lower than 15', 1, true],
195
+ ])('%p Polling Interval warning if %p', async(
196
+ descr1,
197
+ descr2,
198
+ pollingInterval,
199
+ visible,
200
+ ) => {
201
+ const wrapper = mount(GitRepo, {
202
+ props: {
203
+ value: {
204
+ ...values,
205
+ spec: { pollingInterval }
206
+ },
207
+ realMode: mode
208
+ },
209
+ computed: mockComputed,
210
+ global: { mocks },
211
+ });
212
+
213
+ const pollingIntervalMinimumValueWarning = wrapper.find('[data-testid="gitRepo-pollingInterval-minimumValueWarning"]');
214
+
215
+ expect(pollingIntervalMinimumValueWarning.exists()).toBe(visible);
216
+ });
217
+
218
+ it.each([
219
+ ['hide', 'disabled', null, false],
220
+ ['hide', 'disabled', false, false],
221
+ ['hide', 'disabled', '', false],
222
+ ['show', 'enabled', 'sha', true],
223
+ ])('%p Webhook configured warning if webhook is %p', (
224
+ descr1,
225
+ descr2,
226
+ webhookCommit,
227
+ visible
228
+ ) => {
229
+ const wrapper = mount(GitRepo, {
230
+ props: {
231
+ value: {
232
+ ...values,
233
+ spec: { pollingInterval: 60 },
234
+ status: { webhookCommit },
235
+ },
236
+ realMode: mode
237
+ },
238
+ computed: mockComputed,
239
+ global: { mocks },
240
+ });
241
+
242
+ const pollingIntervalWebhookWarning = wrapper.find('[data-testid="gitRepo-pollingInterval-webhookWarning"]');
243
+
244
+ expect(pollingIntervalWebhookWarning.exists()).toBe(visible);
245
+ });
66
246
  });
@@ -1,6 +1,6 @@
1
1
  import { nextTick } from 'vue';
2
2
  /* eslint-disable jest/no-hooks */
3
- import { mount } from '@vue/test-utils';
3
+ import { mount, type VueWrapper } from '@vue/test-utils';
4
4
  import { _EDIT } from '@shell/config/query-params';
5
5
 
6
6
  import oidc from '@shell/edit/auth/oidc.vue';
@@ -19,19 +19,20 @@ const validAuthEndpoint = 'http://localhost:8080/realms/rancherrealm/protocol/op
19
19
  const validScope = 'openid profile email';
20
20
 
21
21
  const mockModel = {
22
- enabled: false,
23
- id: 'genericoidc',
24
- rancherUrl: validRancherUrl,
25
- issuer: validIssuer,
26
- authEndpoint: validAuthEndpoint,
27
- scope: validScope,
28
- clientId: validClientId,
29
- clientSecret: validClientSecret,
30
- type: 'genericOIDCConfig',
22
+ enabled: false,
23
+ id: 'genericoidc',
24
+ rancherUrl: validRancherUrl,
25
+ issuer: validIssuer,
26
+ authEndpoint: validAuthEndpoint,
27
+ scope: validScope,
28
+ clientId: validClientId,
29
+ clientSecret: validClientSecret,
30
+ type: 'genericOIDCConfig',
31
+ groupSearchEnabled: false,
31
32
  };
32
33
 
33
34
  describe('oidc.vue', () => {
34
- let wrapper: any;
35
+ let wrapper: VueWrapper<any, any>;
35
36
  const requiredSetup = () => ({
36
37
  data() {
37
38
  return {
@@ -43,7 +44,7 @@ describe('oidc.vue', () => {
43
44
  originalModel: null,
44
45
  principals: [],
45
46
  authConfigName: 'oidc',
46
- };
47
+ } as any; // any is necessary as in pre-existing tests we are including inherited mixins values
47
48
  },
48
49
  global: {
49
50
  mocks: {
@@ -131,4 +132,51 @@ describe('oidc.vue', () => {
131
132
 
132
133
  expect(wrapper.vm.model.issuer).toBe(`${ validUrl }/realms/${ validRealm }`);
133
134
  });
135
+
136
+ it('`groupSearchEnabled` defaults to false', async() => {
137
+ const groupSearchCheckbox = wrapper.getComponent('[data-testid="input-group-search"]');
138
+
139
+ expect(groupSearchCheckbox.isVisible()).toBe(true);
140
+ expect(wrapper.vm.model.groupSearchEnabled).toBe(false);
141
+ });
142
+
143
+ it('`groupSearchEnabled` updates when checkbox is clicked', async() => {
144
+ const groupSearchCheckbox = wrapper.getComponent('[data-testid="input-group-search"]');
145
+
146
+ await groupSearchCheckbox.find('[role="checkbox"]').trigger('click');
147
+
148
+ expect(groupSearchCheckbox.isVisible()).toBe(true);
149
+ expect(wrapper.vm.model.groupSearchEnabled).toBe(true);
150
+ });
151
+
152
+ it('changing URL should update issuer and auth-endpoint if Keycloak', async() => {
153
+ wrapper.vm.model.id = 'keycloakoidc';
154
+ const newUrl = 'whatever';
155
+
156
+ await wrapper.find(`[data-testid="oidc-url"]`).setValue(newUrl);
157
+ await wrapper.vm.$nextTick();
158
+
159
+ const issuer = (wrapper.find('[data-testid="oidc-issuer"]').element as HTMLInputElement).value;
160
+ const endpoint = (wrapper.find('[data-testid="oidc-auth-endpoint"]').element as HTMLInputElement).value;
161
+
162
+ expect(issuer).toBe(`${ newUrl }/realms/`);
163
+ expect(endpoint).toBe(`${ newUrl }/realms//protocol/openid-connect/auth`);
164
+ });
165
+
166
+ it('changing realm should update issuer and auth-endpoint if Keycloak', async() => {
167
+ const newRealm = 'newRealm';
168
+ const oldUrl = 'oldUrl';
169
+
170
+ wrapper.vm.model.id = 'keycloakoidc';
171
+ wrapper.vm.oidcUrls.url = oldUrl;
172
+
173
+ await wrapper.find(`[data-testid="oidc-realm"]`).setValue(newRealm);
174
+ await wrapper.vm.$nextTick();
175
+
176
+ const issuer = (wrapper.find('[data-testid="oidc-issuer"]').element as HTMLInputElement).value;
177
+ const endpoint = (wrapper.find('[data-testid="oidc-auth-endpoint"]').element as HTMLInputElement).value;
178
+
179
+ expect(issuer).toBe(`${ oldUrl }/realms/${ newRealm }`);
180
+ expect(endpoint).toBe(`${ oldUrl }/realms/${ newRealm }/protocol/openid-connect/auth`);
181
+ });
134
182
  });
@@ -15,4 +15,44 @@ describe('lDAP config', () => {
15
15
 
16
16
  expect(checkbox).toBeDefined();
17
17
  });
18
+
19
+ it('updates user login filter when value is entered', async() => {
20
+ const wrapper = mount(
21
+ LDAPConfig,
22
+ {
23
+ props: {
24
+ value: {},
25
+ type: 'openldap',
26
+ }
27
+ });
28
+
29
+ const userLoginFilter = wrapper.find('[data-testid="user-login-filter"]');
30
+
31
+ await userLoginFilter.setValue('Test Filter');
32
+
33
+ const expectedValue = 'Test Filter';
34
+
35
+ expect(userLoginFilter.exists()).toBe(true);
36
+ expect(userLoginFilter.element.value).toBe(expectedValue);
37
+ expect(wrapper.vm.model.userLoginFilter).toBe(expectedValue);
38
+ });
39
+
40
+ it('defaults to undefined for user login filter', () => {
41
+ const wrapper = mount(
42
+ LDAPConfig,
43
+ {
44
+ props: {
45
+ value: {},
46
+ type: 'openldap',
47
+ }
48
+ });
49
+
50
+ const userLoginFilter = wrapper.find('[data-testid="user-login-filter"]');
51
+
52
+ const expectedValue = '';
53
+
54
+ expect(userLoginFilter.exists()).toBe(true);
55
+ expect(userLoginFilter.element.value).toBe(expectedValue);
56
+ expect(wrapper.vm.model.userLoginFilter).toBeUndefined();
57
+ });
18
58
  });