@rancher/shell 3.0.2-rc.4 → 3.0.2-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 (212) 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 +1 -0
  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 +142 -22
  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 +15 -1
  17. package/components/ButtonMultiAction.vue +5 -1
  18. package/components/ClusterBadge.vue +1 -0
  19. package/components/ClusterIconMenu.vue +3 -0
  20. package/components/ClusterProviderIcon.vue +14 -1
  21. package/components/CodeMirror.vue +96 -5
  22. package/components/Collapse.vue +16 -3
  23. package/components/CopyToClipboardText.vue +3 -1
  24. package/components/CruResource.vue +9 -0
  25. package/components/CruResourceFooter.vue +1 -1
  26. package/components/ExplorerMembers.vue +2 -1
  27. package/components/ExplorerProjectsNamespaces.vue +7 -0
  28. package/components/Import.vue +14 -1
  29. package/components/LandingPagePreference.vue +4 -2
  30. package/components/PodSecurityAdmission.vue +8 -6
  31. package/components/PromptChangePassword.vue +1 -0
  32. package/components/PromptRemove.vue +23 -21
  33. package/components/ResourceDetail/Masthead.vue +30 -11
  34. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  35. package/components/ResourceDetail/index.vue +6 -0
  36. package/components/ResourceTable.vue +6 -14
  37. package/components/ResourceYaml.vue +1 -0
  38. package/components/SelectIconGrid.vue +2 -0
  39. package/components/Setting.vue +115 -0
  40. package/components/SortableTable/THead.vue +2 -0
  41. package/components/SortableTable/index.vue +38 -14
  42. package/components/Tabbed/index.vue +16 -15
  43. package/components/Wizard.vue +108 -104
  44. package/components/YamlEditor.vue +12 -2
  45. package/components/__tests__/Collapse.test.ts +2 -2
  46. package/components/auth/Principal.vue +29 -17
  47. package/components/auth/__tests__/Principal.test.ts +40 -0
  48. package/components/auth/login/ldap.vue +7 -0
  49. package/components/fleet/FleetBundles.vue +1 -1
  50. package/components/fleet/FleetRepos.vue +1 -1
  51. package/components/fleet/FleetResources.vue +0 -2
  52. package/components/fleet/FleetSummary.vue +60 -65
  53. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  54. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  55. package/components/form/ArrayList.vue +6 -2
  56. package/components/form/ColorInput.vue +1 -0
  57. package/components/form/KeyValue.vue +11 -12
  58. package/components/form/LabeledSelect.vue +16 -3
  59. package/components/form/Labels.vue +8 -1
  60. package/components/form/Members/MembershipEditor.vue +230 -222
  61. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  62. package/components/form/Password.vue +3 -0
  63. package/components/form/ProjectMemberEditor.vue +6 -3
  64. package/components/form/ResourceTabs/index.vue +15 -13
  65. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  66. package/components/form/SchedulingCustomization.vue +85 -0
  67. package/components/form/Select.vue +4 -2
  68. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  69. package/components/form/UnitInput.vue +1 -2
  70. package/components/form/__tests__/ArrayList.test.ts +9 -6
  71. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  72. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  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/WindowManager/ContainerLogs.vue +97 -49
  83. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  84. package/components/nav/WindowManager/index.vue +85 -6
  85. package/components/templates/default.vue +2 -47
  86. package/config/features.js +1 -0
  87. package/config/home-links.js +1 -1
  88. package/config/labels-annotations.js +11 -1
  89. package/config/router/navigation-guards/index.js +2 -1
  90. package/config/router/navigation-guards/record-last-route.js +24 -0
  91. package/config/settings.ts +66 -98
  92. package/config/version.js +1 -1
  93. package/core/types-provisioning.ts +7 -0
  94. package/detail/fleet.cattle.io.bundle.vue +7 -0
  95. package/detail/fleet.cattle.io.cluster.vue +0 -3
  96. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  97. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  98. package/dialog/DeactivateDriverDialog.vue +5 -5
  99. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  100. package/directives/strip-html-aria-label.js +19 -0
  101. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  102. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  103. package/edit/auth/__tests__/oidc.test.ts +26 -9
  104. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  105. package/edit/auth/ldap/config.vue +67 -89
  106. package/edit/auth/oidc.vue +15 -1
  107. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  108. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  109. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  110. package/edit/logging-flow/Match.vue +0 -21
  111. package/edit/management.cattle.io.project.vue +1 -1
  112. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  113. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  114. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  115. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  116. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  117. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  118. package/edit/provisioning.cattle.io.cluster/index.vue +39 -39
  119. package/edit/provisioning.cattle.io.cluster/rke2.vue +63 -12
  120. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  121. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  122. package/edit/resources.cattle.io.backup.vue +150 -15
  123. package/edit/secret/__tests__/ssh.test.ts +79 -0
  124. package/edit/secret/ssh.vue +7 -1
  125. package/edit/workload/Job.vue +2 -2
  126. package/edit/workload/index.vue +3 -1
  127. package/initialize/install-directives.js +2 -0
  128. package/initialize/install-plugins.js +6 -1
  129. package/list/catalog.cattle.io.app.vue +21 -4
  130. package/list/fleet.cattle.io.bundle.vue +1 -1
  131. package/list/management.cattle.io.setting.vue +34 -129
  132. package/list/provisioning.cattle.io.cluster.vue +11 -3
  133. package/machine-config/vmwarevsphere.vue +15 -8
  134. package/mixins/__tests__/auth-config.test.ts +74 -0
  135. package/mixins/__tests__/chart.test.ts +5 -4
  136. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  137. package/mixins/auth-config.js +9 -1
  138. package/mixins/chart.js +2 -2
  139. package/mixins/create-edit-view/impl.js +4 -1
  140. package/mixins/vue-select-overrides.js +10 -0
  141. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  142. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  143. package/models/__tests__/secret.test.ts +56 -13
  144. package/models/catalog.cattle.io.app.js +112 -37
  145. package/models/cluster.js +11 -0
  146. package/models/fleet.cattle.io.bundle.js +40 -2
  147. package/models/fleet.cattle.io.gitrepo.js +169 -109
  148. package/models/management.cattle.io.fleetworkspace.js +4 -0
  149. package/models/management.cattle.io.kontainerdriver.js +7 -0
  150. package/models/nodedriver.js +4 -1
  151. package/models/provisioning.cattle.io.cluster.js +24 -0
  152. package/models/secret.js +1 -1
  153. package/package.json +4 -4
  154. package/pages/auth/login.vue +4 -2
  155. package/pages/auth/verify.vue +11 -1
  156. package/pages/c/_cluster/apps/charts/chart.vue +1 -0
  157. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  158. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  159. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  160. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  161. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  162. package/pages/c/_cluster/explorer/index.vue +33 -35
  163. package/pages/c/_cluster/explorer/tools/index.vue +17 -4
  164. package/pages/c/_cluster/fleet/index.vue +0 -5
  165. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  166. package/pages/c/_cluster/settings/performance.vue +52 -53
  167. package/pages/c/_cluster/uiplugins/index.vue +21 -22
  168. package/pages/home.vue +17 -12
  169. package/pages/prefs.vue +5 -1
  170. package/plugins/shortkey.js +10 -1
  171. package/plugins/steve/steve-pagination-utils.ts +58 -8
  172. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  173. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  174. package/promptRemove/management.cattle.io.project.vue +2 -8
  175. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  176. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  177. package/promptRemove/pod.vue +7 -28
  178. package/rancher-components/Card/Card.vue +9 -1
  179. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  180. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  181. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  182. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  183. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  184. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  185. package/rancher-components/RcButton/RcButton.vue +14 -9
  186. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  187. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -14
  188. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  189. package/rancher-components/RcDropdown/index.ts +1 -0
  190. package/rancher-components/RcDropdown/types.ts +27 -0
  191. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  192. package/scripts/typegen.sh +1 -0
  193. package/store/__tests__/auth.test.ts +120 -0
  194. package/store/action-menu.js +13 -3
  195. package/store/auth.js +14 -9
  196. package/store/catalog.js +14 -7
  197. package/store/features.js +1 -0
  198. package/store/prefs.js +9 -28
  199. package/store/type-map.utils.ts +4 -0
  200. package/types/resources/settings.d.ts +27 -20
  201. package/types/shell/index.d.ts +18 -2
  202. package/utils/__tests__/array.test.ts +13 -1
  203. package/utils/__tests__/string.test.ts +80 -1
  204. package/utils/array.ts +13 -0
  205. package/utils/auth.js +4 -0
  206. package/utils/cluster.js +1 -1
  207. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  208. package/utils/pagination-utils.ts +15 -2
  209. package/utils/string.js +31 -7
  210. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  211. package/utils/validators/formRules/index.ts +16 -0
  212. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -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
  });
@@ -19,15 +19,16 @@ 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', () => {
@@ -131,4 +132,20 @@ 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
+ });
134
151
  });
@@ -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
  });
@@ -279,147 +279,125 @@ export default {
279
279
  color="info"
280
280
  label-key="authConfig.ldap.oktaSchema"
281
281
  />
282
- <div class="row">
283
- <div class="col span-6">
282
+ <div class="schema-container">
283
+ <div class="schema-column">
284
284
  <h4>{{ t('authConfig.ldap.users') }}</h4>
285
- </div>
286
- <div class="col span-6">
287
- <h4>{{ t('authConfig.ldap.groups') }}</h4>
288
- </div>
289
- </div>
290
- <div class="row mb-20">
291
- <div class="col span-6">
292
285
  <LabeledInput
293
286
  v-model:value="model.userObjectClass"
294
287
  :mode="mode"
295
288
  :label="t('authConfig.ldap.objectClass')"
296
289
  />
297
- </div>
298
- <div class="col span-6">
299
- <LabeledInput
300
- v-model:value="model.groupObjectClass"
301
- :mode="mode"
302
- :label="t('authConfig.ldap.objectClass')"
303
- />
304
- </div>
305
- </div>
306
- <div class="row mb-20">
307
- <div class="col span-6">
308
290
  <LabeledInput
309
291
  v-model:value="model.userNameAttribute"
310
292
  :mode="mode"
311
293
  :label="t('authConfig.ldap.usernameAttribute')"
312
294
  />
313
- </div>
314
- <div class="col span-6">
315
- <LabeledInput
316
- v-model:value="model.groupNameAttribute"
317
- :mode="mode"
318
- :label="t('authConfig.ldap.nameAttribute')"
319
- />
320
- </div>
321
- </div>
322
- <div class="row mb-20">
323
- <div class="col span-6">
324
295
  <LabeledInput
325
296
  v-model:value="model.userLoginAttribute"
326
297
  :mode="mode"
327
298
  :label="t('authConfig.ldap.loginAttribute')"
328
299
  />
329
- </div>
330
- <div class="col span-6">
331
- <LabeledInput
332
- v-model:value="model.groupMemberUserAttribute"
333
- :mode="mode"
334
- :label="t('authConfig.ldap.groupMemberUserAttribute')"
335
- />
336
- </div>
337
- </div>
338
- <div class="row mb-20">
339
- <div class="col span-6">
340
300
  <LabeledInput
341
301
  v-model:value="model.userMemberAttribute"
342
302
  :mode="mode"
343
303
  :label="t('authConfig.ldap.userMemberAttribute')"
344
304
  />
345
- </div>
346
- <div class="col span-6">
347
305
  <LabeledInput
348
- v-model:value="model.groupSearchAttribute"
306
+ v-model:value="model.userLoginFilter"
307
+ data-testid="user-login-filter"
349
308
  :mode="mode"
350
- :label="t('authConfig.ldap.searchAttribute')"
309
+ :label="t('authConfig.ldap.userLoginFilter')"
351
310
  />
352
- </div>
353
- </div>
354
- <div class="row mb-20">
355
- <div class="col span-6">
356
311
  <LabeledInput
357
312
  v-model:value="model.userSearchAttribute"
358
313
  :mode="mode"
359
314
  :label="t('authConfig.ldap.searchAttribute')"
360
315
  />
361
- </div>
362
- <div class="col span-6">
363
316
  <LabeledInput
364
- v-model:value="model.groupSearchFilter"
317
+ v-model:value="model.userSearchFilter"
365
318
  :mode="mode"
366
319
  :label="t('authConfig.ldap.searchFilter')"
367
320
  />
368
- </div>
369
- </div>
370
- <div class="row mb-20">
371
- <div class="col span-6">
372
321
  <LabeledInput
373
- v-model:value="model.userSearchFilter"
322
+ v-model:value="model.userEnabledAttribute"
374
323
  :mode="mode"
375
- :label="t('authConfig.ldap.searchFilter')"
324
+ :label="t('authConfig.ldap.userEnabledAttribute')"
376
325
  />
377
- </div>
378
- <div class="col span-6">
379
326
  <LabeledInput
380
- v-model:value="model.groupMemberMappingAttribute"
327
+ v-model:value="model.disabledStatusBitmask"
381
328
  :mode="mode"
382
- :label="t('authConfig.ldap.groupMemberMappingAttribute')"
329
+ :label="t('authConfig.ldap.disabledStatusBitmask')"
383
330
  />
384
331
  </div>
385
- </div>
386
- <div class="row mb-20">
387
- <div class="col span-6">
332
+ <div class="schema-column">
333
+ <h4>{{ t('authConfig.ldap.groups') }}</h4>
388
334
  <LabeledInput
389
- v-model:value="model.userEnabledAttribute"
335
+ v-model:value="model.groupObjectClass"
390
336
  :mode="mode"
391
- :label="t('authConfig.ldap.userEnabledAttribute')"
337
+ :label="t('authConfig.ldap.objectClass')"
392
338
  />
393
- </div>
394
- <div class="col span-6">
395
339
  <LabeledInput
396
- v-model:value="model.groupDNAttribute"
340
+ v-model:value="model.groupNameAttribute"
397
341
  :mode="mode"
398
- :label="t('authConfig.ldap.groupDNAttribute')"
342
+ :label="t('authConfig.ldap.nameAttribute')"
399
343
  />
400
- </div>
401
- </div>
402
- <div class="row mb-20">
403
- <div class="col span-6">
404
344
  <LabeledInput
405
- v-model:value="model.disabledStatusBitmask"
345
+ v-model:value="model.groupMemberUserAttribute"
406
346
  :mode="mode"
407
- :label="t('authConfig.ldap.disabledStatusBitmask')"
347
+ :label="t('authConfig.ldap.groupMemberUserAttribute')"
408
348
  />
409
- </div>
410
- <div
411
- v-if="!isSamlProvider"
412
- class=" col span-6"
413
- >
414
- <RadioGroup
415
- v-model:value="model.nestedGroupMembershipEnabled"
349
+ <LabeledInput
350
+ v-model:value="model.groupSearchAttribute"
416
351
  :mode="mode"
417
- name="nested"
418
- class="full-height"
419
- :options="[true, false]"
420
- :labels="[t('authConfig.ldap.nestedGroupMembership.options.nested'), t('authConfig.ldap.nestedGroupMembership.options.direct')]"
352
+ :label="t('authConfig.ldap.searchAttribute')"
353
+ />
354
+ <LabeledInput
355
+ v-model:value="model.groupSearchFilter"
356
+ :mode="mode"
357
+ :label="t('authConfig.ldap.searchFilter')"
421
358
  />
359
+ <LabeledInput
360
+ v-model:value="model.groupMemberMappingAttribute"
361
+ :mode="mode"
362
+ :label="t('authConfig.ldap.groupMemberMappingAttribute')"
363
+ />
364
+ <LabeledInput
365
+ v-model:value="model.groupDNAttribute"
366
+ :mode="mode"
367
+ :label="t('authConfig.ldap.groupDNAttribute')"
368
+ />
369
+ <template
370
+ v-if="!isSamlProvider"
371
+ >
372
+ <RadioGroup
373
+ v-model:value="model.nestedGroupMembershipEnabled"
374
+ :mode="mode"
375
+ name="nested"
376
+ class="full-height"
377
+ :options="[true, false]"
378
+ :labels="[t('authConfig.ldap.nestedGroupMembership.options.nested'), t('authConfig.ldap.nestedGroupMembership.options.direct')]"
379
+ />
380
+ </template>
422
381
  </div>
423
382
  </div>
424
383
  </div>
425
384
  </template>
385
+
386
+ <style lang="scss" scoped>
387
+ .schema-container {
388
+ display: flex;
389
+ gap: 1.75%;
390
+ flex-wrap: wrap;
391
+ }
392
+
393
+ .schema-column {
394
+ flex: 1;
395
+ display: flex;
396
+ flex-direction: column;
397
+ min-width: 16rem;
398
+
399
+ > :not(:first-child) {
400
+ margin-bottom: 20px;
401
+ }
402
+ }
403
+ </style>