@rancher/shell 3.0.6 → 3.0.8-rc.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 (146) hide show
  1. package/assets/images/pl/dark/rancher-logo.svg +131 -44
  2. package/assets/images/pl/rancher-logo.svg +120 -44
  3. package/assets/images/vendor/githubapp.svg +13 -0
  4. package/assets/styles/base/_basic.scss +2 -2
  5. package/assets/styles/base/_color-classic.scss +51 -0
  6. package/assets/styles/base/_color.scss +3 -3
  7. package/assets/styles/base/_mixins.scss +1 -1
  8. package/assets/styles/base/_typography.scss +1 -1
  9. package/assets/styles/base/_variables-classic.scss +47 -0
  10. package/assets/styles/global/_button.scss +49 -17
  11. package/assets/styles/global/_form.scss +1 -1
  12. package/assets/styles/themes/_dark.scss +4 -0
  13. package/assets/styles/themes/_light.scss +3 -69
  14. package/assets/styles/themes/_modern.scss +194 -50
  15. package/assets/styles/vendor/vue-select.scss +1 -2
  16. package/assets/translations/en-us.yaml +124 -32
  17. package/assets/translations/zh-hans.yaml +0 -4
  18. package/components/ClusterIconMenu.vue +1 -1
  19. package/components/ClusterProviderIcon.vue +1 -1
  20. package/components/CodeMirror.vue +1 -1
  21. package/components/IconOrSvg.vue +40 -29
  22. package/components/Inactivity.vue +222 -106
  23. package/components/InstallHelmCharts.vue +2 -2
  24. package/components/ResourceDetail/index.vue +2 -1
  25. package/components/SortableTable/index.vue +17 -2
  26. package/components/SortableTable/sorting.js +3 -1
  27. package/components/Tabbed/index.vue +5 -5
  28. package/components/fleet/FleetConfigMapSelector.vue +117 -0
  29. package/components/fleet/FleetSecretSelector.vue +127 -0
  30. package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
  31. package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
  32. package/components/form/FileImageSelector.vue +13 -4
  33. package/components/form/FileSelector.vue +11 -2
  34. package/components/form/ResourceLabeledSelect.vue +1 -0
  35. package/components/form/ResourceTabs/index.vue +37 -18
  36. package/components/form/SecretSelector.vue +6 -2
  37. package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
  38. package/components/nav/Group.vue +29 -9
  39. package/components/nav/Header.vue +7 -8
  40. package/components/nav/NamespaceFilter.vue +1 -1
  41. package/components/nav/TopLevelMenu.helper.ts +47 -20
  42. package/components/nav/TopLevelMenu.vue +44 -14
  43. package/components/nav/Type.vue +0 -5
  44. package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
  45. package/config/pagination-table-headers.js +10 -2
  46. package/config/product/auth.js +1 -0
  47. package/config/product/explorer.js +4 -3
  48. package/config/query-params.js +1 -0
  49. package/config/settings.ts +8 -1
  50. package/config/table-headers.js +9 -0
  51. package/config/types.js +2 -0
  52. package/core/plugin.ts +18 -6
  53. package/core/types.ts +8 -0
  54. package/detail/provisioning.cattle.io.cluster.vue +1 -0
  55. package/dialog/AddonConfigConfirmationDialog.vue +45 -1
  56. package/dialog/InstallExtensionDialog.vue +71 -45
  57. package/dialog/UninstallExtensionDialog.vue +2 -1
  58. package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
  59. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
  60. package/edit/auth/AuthProviderWarningBanners.vue +14 -1
  61. package/edit/auth/github-app-steps.vue +97 -0
  62. package/edit/auth/github-steps.vue +75 -0
  63. package/edit/auth/github.vue +94 -65
  64. package/edit/auth/oidc.vue +86 -16
  65. package/edit/fleet.cattle.io.helmop.vue +51 -2
  66. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
  67. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
  68. package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
  69. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
  70. package/list/projectsecret.vue +1 -1
  71. package/machine-config/azure.vue +1 -1
  72. package/mixins/__tests__/chart.test.ts +1 -1
  73. package/mixins/chart.js +2 -2
  74. package/models/__tests__/chart.test.ts +17 -9
  75. package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
  76. package/models/catalog.cattle.io.app.js +1 -1
  77. package/models/chart.js +3 -1
  78. package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
  79. package/models/event.js +7 -0
  80. package/models/management.cattle.io.authconfig.js +1 -0
  81. package/models/provisioning.cattle.io.cluster.js +9 -0
  82. package/package.json +2 -2
  83. package/pages/auth/login.vue +5 -2
  84. package/pages/auth/verify.vue +1 -1
  85. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
  86. package/pages/c/_cluster/apps/charts/chart.vue +2 -2
  87. package/pages/c/_cluster/explorer/EventsTable.vue +92 -9
  88. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  89. package/pages/c/_cluster/settings/performance.vue +13 -26
  90. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
  91. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
  92. package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
  93. package/pages/c/_cluster/uiplugins/index.vue +110 -94
  94. package/pages/home.vue +313 -12
  95. package/plugins/__tests__/subscribe.events.test.ts +194 -0
  96. package/plugins/axios.js +2 -1
  97. package/plugins/dashboard-store/actions.js +4 -1
  98. package/plugins/dashboard-store/getters.js +1 -1
  99. package/plugins/dashboard-store/resource-class.js +20 -5
  100. package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
  101. package/plugins/steve/index.js +18 -10
  102. package/plugins/steve/mutations.js +2 -2
  103. package/plugins/steve/resourceWatcher.js +2 -2
  104. package/plugins/steve/steve-pagination-utils.ts +12 -9
  105. package/plugins/steve/subscribe.js +113 -85
  106. package/plugins/subscribe-events.ts +211 -0
  107. package/rancher-components/BadgeState/BadgeState.vue +8 -6
  108. package/rancher-components/Banner/Banner.vue +2 -1
  109. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  110. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  111. package/scripts/extension/publish +1 -1
  112. package/store/auth.js +8 -3
  113. package/store/aws.js +8 -6
  114. package/store/features.js +1 -0
  115. package/store/index.js +21 -25
  116. package/store/prefs.js +6 -0
  117. package/types/extension-manager.ts +8 -1
  118. package/types/kube/kube-api.ts +2 -1
  119. package/types/rancher/index.d.ts +1 -0
  120. package/types/resources/settings.d.ts +52 -23
  121. package/types/shell/index.d.ts +412 -336
  122. package/types/store/subscribe-events.types.ts +70 -0
  123. package/types/store/subscribe.types.ts +6 -22
  124. package/utils/__tests__/cluster.test.ts +379 -1
  125. package/utils/cluster.js +157 -3
  126. package/utils/dynamic-content/__tests__/config.test.ts +187 -0
  127. package/utils/dynamic-content/__tests__/index.test.ts +390 -0
  128. package/utils/dynamic-content/__tests__/info.test.ts +263 -0
  129. package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
  130. package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
  131. package/utils/dynamic-content/__tests__/util.test.ts +235 -0
  132. package/utils/dynamic-content/config.ts +55 -0
  133. package/utils/dynamic-content/index.ts +273 -0
  134. package/utils/dynamic-content/info.ts +219 -0
  135. package/utils/dynamic-content/new-release.ts +126 -0
  136. package/utils/dynamic-content/support-notice.ts +169 -0
  137. package/utils/dynamic-content/types.d.ts +101 -0
  138. package/utils/dynamic-content/util.ts +122 -0
  139. package/utils/inactivity.ts +104 -0
  140. package/utils/pagination-utils.ts +105 -31
  141. package/utils/pagination-wrapper.ts +6 -8
  142. package/utils/release-notes.ts +1 -1
  143. package/utils/sort.js +5 -0
  144. package/utils/unit-tests/pagination-utils.spec.ts +283 -0
  145. package/utils/validators/formRules/__tests__/index.test.ts +7 -0
  146. package/utils/validators/formRules/index.ts +2 -2
@@ -2,10 +2,9 @@ import paginationUtils from '@shell/utils/pagination-utils';
2
2
  import { PaginationArgs, PaginationResourceContext } from '@shell/types/store/pagination.types';
3
3
  import { VuexStore } from '@shell/types/store/vuex';
4
4
  import { ActionFindPageArgs, ActionFindPageTransientResult } from '@shell/types/store/dashboard-store.types';
5
- import {
6
- STEVE_WATCH_EVENT_LISTENER_CALLBACK, STEVE_UNWATCH_EVENT_PARAMS, STEVE_WATCH_EVENT, STEVE_WATCH_EVENT_PARAMS, STEVE_WATCH_EVENT_PARAMS_COMMON, STEVE_WATCH_MODE
7
- } from '@shell/types/store/subscribe.types';
5
+ import { STEVE_WATCH_EVENT_TYPES, STEVE_WATCH_MODE } from '@shell/types/store/subscribe.types';
8
6
  import { Reactive, reactive } from 'vue';
7
+ import { STEVE_UNWATCH_EVENT_PARAMS, STEVE_WATCH_EVENT_LISTENER_CALLBACK, STEVE_WATCH_EVENT_PARAMS, STEVE_WATCH_EVENT_PARAMS_COMMON } from '@shell/types/store/subscribe-events.types';
9
8
 
10
9
  interface Args {
11
10
  $store: VuexStore,
@@ -70,7 +69,7 @@ class PaginationWrapper<T extends object> {
70
69
  this.classify = formatResponse?.classify || false;
71
70
  this.reactive = formatResponse?.reactive || false;
72
71
 
73
- this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters }, enabledFor);
72
+ this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $plugin: this.$store.$plugin }, enabledFor);
74
73
  }
75
74
 
76
75
  async request(args: {
@@ -92,7 +91,7 @@ class PaginationWrapper<T extends object> {
92
91
  // Watch
93
92
  if (this.onChange && !this.steveWatchParams) {
94
93
  this.steveWatchParams = {
95
- event: STEVE_WATCH_EVENT.CHANGES,
94
+ event: STEVE_WATCH_EVENT_TYPES.CHANGES,
96
95
  id: this.id,
97
96
  params: {
98
97
  type: this.enabledFor.resource?.id as string,
@@ -126,7 +125,7 @@ class PaginationWrapper<T extends object> {
126
125
  }
127
126
  const watchParams: STEVE_WATCH_EVENT_PARAMS = {
128
127
  ...this.steveWatchParams,
129
- callback: this.onChange as STEVE_WATCH_EVENT_LISTENER_CALLBACK, // we must have it by now
128
+ callback: this.onChange as STEVE_WATCH_EVENT_LISTENER_CALLBACK, // we must have onChange by now
130
129
  };
131
130
 
132
131
  await this.$store.dispatch(`${ this.enabledFor.store }/watchEvent`, watchParams);
@@ -134,8 +133,7 @@ class PaginationWrapper<T extends object> {
134
133
 
135
134
  private async unWatch() {
136
135
  if (!this.steveWatchParams) {
137
- console.error('Calling unWatch but no watch params created'); // eslint-disable-line no-console
138
-
136
+ // We're unwatching before we've made the initial request
139
137
  return;
140
138
  }
141
139
 
@@ -20,7 +20,7 @@ export async function addReleaseNotesNotification(dispatch: any, getters: any) {
20
20
  if (notification.id === id) {
21
21
  found = true;
22
22
  } else {
23
- await dispatch('notifications/delete', notification.id);
23
+ await dispatch('notifications/remove', notification.id);
24
24
  }
25
25
  }
26
26
  }
package/utils/sort.js CHANGED
@@ -169,6 +169,11 @@ export function compare(a, b) {
169
169
  return 0;
170
170
  }
171
171
 
172
+ /**
173
+ * Should the logic of this sort field be flipped?
174
+ *
175
+ * For instance show descending but sort by ascending
176
+ */
172
177
  export function parseField(str) {
173
178
  const parts = str.split(/:/);
174
179
 
@@ -0,0 +1,283 @@
1
+ import { STEVE_CACHE } from '@shell/store/features';
2
+ import { SETTING } from '@shell/config/settings';
3
+ import { EXT_IDS } from '@shell/core/plugin';
4
+ import { PaginationResourceContext } from '@shell/types/store/pagination.types';
5
+ import { PaginationSettings, PaginationSettingsStore } from '@shell/types/resources/settings';
6
+ import paginationUtils from '@shell/utils/pagination-utils';
7
+
8
+ describe('pagination-utils', () => {
9
+ describe('isEnabledInStore', () => {
10
+ const mockRootGetters = {
11
+ 'type-map/configuredHeaders': jest.fn(),
12
+ 'type-map/configuredPaginationHeaders': jest.fn(),
13
+ 'type-map/hasCustomList': jest.fn(),
14
+ };
15
+
16
+ beforeEach(() => {
17
+ jest.resetAllMocks();
18
+ });
19
+
20
+ it('should return false if no store settings are provided', () => {
21
+ const result = paginationUtils.isEnabledInStore({
22
+ ctx: { rootGetters: mockRootGetters },
23
+ storeSettings: undefined as unknown as PaginationSettingsStore,
24
+ enabledFor: { store: 'cluster' }
25
+ });
26
+
27
+ expect(result).toBe(false);
28
+ });
29
+
30
+ it('should return true if no specific resource is being checked', () => {
31
+ const storeSettings: PaginationSettingsStore = { resources: {} };
32
+ const result = paginationUtils.isEnabledInStore({
33
+ ctx: { rootGetters: mockRootGetters },
34
+ storeSettings,
35
+ enabledFor: { store: 'cluster' }
36
+ });
37
+
38
+ expect(result).toBe(true);
39
+ });
40
+
41
+ it('should return true if enableAll is true for the store', () => {
42
+ const storeSettings: PaginationSettingsStore = { resources: { enableAll: true } };
43
+ const result = paginationUtils.isEnabledInStore({
44
+ ctx: { rootGetters: mockRootGetters },
45
+ storeSettings,
46
+ enabledFor: { store: 'cluster', resource: { id: 'pod' } }
47
+ });
48
+
49
+ expect(result).toBe(true);
50
+ });
51
+
52
+ it('should return false if a resource is checked but has no id', () => {
53
+ const storeSettings: PaginationSettingsStore = { resources: {} };
54
+ const result = paginationUtils.isEnabledInStore({
55
+ ctx: { rootGetters: mockRootGetters },
56
+ storeSettings,
57
+ enabledFor: { store: 'cluster', resource: { id: undefined as unknown as string } }
58
+ });
59
+
60
+ expect(result).toBe(false);
61
+ });
62
+
63
+ it('should return true for a generic resource when generic is enabled', () => {
64
+ mockRootGetters['type-map/configuredHeaders'].mockReturnValue(false);
65
+ mockRootGetters['type-map/configuredPaginationHeaders'].mockReturnValue(false);
66
+ mockRootGetters['type-map/hasCustomList'].mockReturnValue(false);
67
+
68
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { generic: true } } };
69
+ const result = paginationUtils.isEnabledInStore({
70
+ ctx: { rootGetters: mockRootGetters },
71
+ storeSettings,
72
+ enabledFor: { store: 'cluster', resource: { id: 'pod' } }
73
+ });
74
+
75
+ expect(result).toBe(true);
76
+ });
77
+
78
+ it('should return false for a non-generic resource when only generic is enabled', () => {
79
+ mockRootGetters['type-map/hasCustomList'].mockReturnValue(true);
80
+
81
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { generic: true } } };
82
+ const result = paginationUtils.isEnabledInStore({
83
+ ctx: { rootGetters: mockRootGetters },
84
+ storeSettings,
85
+ enabledFor: { store: 'cluster', resource: { id: 'pod' } }
86
+ });
87
+
88
+ expect(result).toBe(false);
89
+ });
90
+
91
+ it('should return true if resource id is in enabled list as a string', () => {
92
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { enabled: ['pod'] } } };
93
+ const result = paginationUtils.isEnabledInStore({
94
+ ctx: { rootGetters: mockRootGetters },
95
+ storeSettings,
96
+ enabledFor: { store: 'cluster', resource: { id: 'pod' } }
97
+ });
98
+
99
+ expect(result).toBe(true);
100
+ });
101
+
102
+ it('should return true if resource id is in enabled list as an object without context', () => {
103
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { enabled: [{ resource: 'pod' }] } } };
104
+ const result = paginationUtils.isEnabledInStore({
105
+ ctx: { rootGetters: mockRootGetters },
106
+ storeSettings,
107
+ enabledFor: { store: 'cluster', resource: { id: 'pod' } }
108
+ });
109
+
110
+ expect(result).toBe(true);
111
+ });
112
+
113
+ it('should return false if resource id is in enabled list as an object with empty context', () => {
114
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { enabled: [{ resource: 'pod', context: [] }] } } };
115
+ const result = paginationUtils.isEnabledInStore({
116
+ ctx: { rootGetters: mockRootGetters },
117
+ storeSettings,
118
+ enabledFor: { store: 'cluster', resource: { id: 'pod' } }
119
+ });
120
+
121
+ expect(result).toBe(false);
122
+ });
123
+
124
+ it('should return true if resource id and context match an enabled setting', () => {
125
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { enabled: [{ resource: 'pod', context: ['list'] }] } } };
126
+ const result = paginationUtils.isEnabledInStore({
127
+ ctx: { rootGetters: mockRootGetters },
128
+ storeSettings,
129
+ enabledFor: { store: 'cluster', resource: { id: 'pod', context: 'list' } }
130
+ });
131
+
132
+ expect(result).toBe(true);
133
+ });
134
+
135
+ it('should return false if resource context does not match enabled setting', () => {
136
+ const storeSettings: PaginationSettingsStore = { resources: { enableSome: { enabled: [{ resource: 'pod', context: ['detail'] }] } } };
137
+ const result = paginationUtils.isEnabledInStore({
138
+ ctx: { rootGetters: mockRootGetters },
139
+ storeSettings,
140
+ enabledFor: { store: 'cluster', resource: { id: 'pod', context: 'list' } }
141
+ });
142
+
143
+ expect(result).toBe(false);
144
+ });
145
+ });
146
+
147
+ describe('isEnabled', () => {
148
+ let mockRootGetters: any;
149
+ let mockPlugin: any;
150
+ let enabledFor: PaginationResourceContext;
151
+
152
+ beforeEach(() => {
153
+ enabledFor = { store: 'cluster', resource: { id: 'pod' } };
154
+ mockPlugin = { getAll: jest.fn().mockReturnValue({}) };
155
+ mockRootGetters = {
156
+ 'features/get': jest.fn(),
157
+ 'management/byId': jest.fn(),
158
+ 'type-map/configuredHeaders': jest.fn().mockReturnValue(false),
159
+ 'type-map/configuredPaginationHeaders': jest.fn().mockReturnValue(false),
160
+ 'type-map/hasCustomList': jest.fn().mockReturnValue(false),
161
+ };
162
+ });
163
+
164
+ it('should return false if steve cache is disabled', () => {
165
+ mockRootGetters['features/get'].mockImplementation((feature: string) => feature === STEVE_CACHE ? false : undefined);
166
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
167
+
168
+ expect(result).toBe(false);
169
+ });
170
+
171
+ it('should return false if pagination settings are not defined', () => {
172
+ jest.spyOn(paginationUtils, 'getSettings').mockReturnValue(undefined);
173
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
174
+
175
+ expect(result).toBe(false);
176
+
177
+ jest.clearAllMocks();
178
+ });
179
+
180
+ it('should return false if enabledFor is not provided', () => {
181
+ mockRootGetters['features/get'].mockReturnValue(true);
182
+ const settings: PaginationSettings = { useDefaultStores: true };
183
+
184
+ mockRootGetters['management/byId'].mockImplementation((type: string, id: string) => {
185
+ if (type === 'management.cattle.io.setting' && id === SETTING.UI_PERFORMANCE) {
186
+ return { value: JSON.stringify({ serverPagination: settings }) };
187
+ }
188
+
189
+ return null;
190
+ });
191
+
192
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, undefined as unknown as PaginationResourceContext);
193
+
194
+ expect(result).toBe(false);
195
+ });
196
+
197
+ it('should return true if an extension enables the resource', () => {
198
+ mockRootGetters['features/get'].mockReturnValue(true);
199
+ const extensionSettings = { cluster: { resources: { enableAll: true } } };
200
+
201
+ mockPlugin.getAll.mockReturnValue({ [EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES]: { 'my-ext': () => extensionSettings } });
202
+
203
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
204
+
205
+ expect(result).toBe(true);
206
+ });
207
+
208
+ it('should still return true if an extension enables the resource but core store does not', () => {
209
+ mockRootGetters['features/get'].mockReturnValue(true);
210
+ const extensionSettings = { cluster: { resources: { enableAll: true } } };
211
+ const defaultSettings = { cluster: { resources: { enableAll: false, enableSome: { enabled: [] } } } };
212
+
213
+ mockPlugin.getAll.mockReturnValue({ [EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES]: { 'my-ext': () => extensionSettings } });
214
+
215
+ // Mocking PAGINATION_SETTINGS_STORE_DEFAULTS behavior
216
+ jest.spyOn(paginationUtils, 'getStoreDefault').mockReturnValue(defaultSettings);
217
+
218
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
219
+
220
+ expect(result).toBe(true);
221
+ });
222
+
223
+ it('should use default store settings and enable the resource', () => {
224
+ mockRootGetters['features/get'].mockReturnValue(true);
225
+ const settings: PaginationSettings = { useDefaultStores: true };
226
+
227
+ mockRootGetters['management/byId'].mockImplementation((type: string, id: string) => {
228
+ if (type === 'management.cattle.io.setting' && id === SETTING.UI_PERFORMANCE) {
229
+ return { value: JSON.stringify({ serverPagination: settings }) };
230
+ }
231
+
232
+ return null;
233
+ });
234
+
235
+ // Mocking PAGINATION_SETTINGS_STORE_DEFAULTS behavior
236
+ jest.spyOn(paginationUtils, 'getStoreDefault').mockReturnValue({ cluster: { resources: { enableAll: true } } });
237
+
238
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
239
+
240
+ expect(result).toBe(true);
241
+ });
242
+
243
+ it('should use custom store settings and enable the resource', () => {
244
+ mockRootGetters['features/get'].mockReturnValue(true);
245
+ const settings: PaginationSettings = {
246
+ useDefaultStores: false,
247
+ stores: { cluster: { resources: { enableSome: { enabled: ['pod'] } } } }
248
+ };
249
+
250
+ mockRootGetters['management/byId'].mockImplementation((type: string, id: string) => {
251
+ if (type === 'management.cattle.io.setting' && id === SETTING.UI_PERFORMANCE) {
252
+ return { value: JSON.stringify({ serverPagination: settings }) };
253
+ }
254
+
255
+ return null;
256
+ });
257
+
258
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
259
+
260
+ expect(result).toBe(true);
261
+ });
262
+
263
+ it('should return false if neither extension nor main settings enable the resource', () => {
264
+ mockRootGetters['features/get'].mockReturnValue(true);
265
+ const settings: PaginationSettings = {
266
+ useDefaultStores: false,
267
+ stores: { cluster: { resources: { enableSome: { enabled: ['service'] } } } }
268
+ };
269
+
270
+ mockRootGetters['management/byId'].mockImplementation((type: string, id: string) => {
271
+ if (type === 'management.cattle.io.setting' && id === SETTING.UI_PERFORMANCE) {
272
+ return { value: JSON.stringify({ serverPagination: settings }) };
273
+ }
274
+
275
+ return null;
276
+ });
277
+
278
+ const result = paginationUtils.isEnabled({ rootGetters: mockRootGetters, $plugin: mockPlugin }, enabledFor);
279
+
280
+ expect(result).toBe(false);
281
+ });
282
+ });
283
+ });
@@ -116,6 +116,10 @@ describe('formRules', () => {
116
116
  ['git@github.com:rancher/dashboard/%20', undefined],
117
117
  ['git@git.apps.local:fleet/fleet-local.git', undefined],
118
118
  ['git@git.apps.local:33333/fleet/fleet-local.git', undefined],
119
+ ['ssh://git@github.com:rancher/dashboard', undefined],
120
+ ['ssh://git@github.com:rancher/dashboard/', undefined],
121
+ ['ssh://git@git.apps.local:fleet/fleet-local.git', undefined],
122
+ ['ssh://git@git.apps.local:33333/fleet/fleet-local.git', undefined],
119
123
 
120
124
  // Not valid HTTP(s)
121
125
  ['https://github.com/rancher/ dashboard.git', message],
@@ -145,6 +149,9 @@ describe('formRules', () => {
145
149
  ['git@git.apps.local:/fleet/fleet-local.git', message],
146
150
  ['git@.git', message],
147
151
  ['git@', message],
152
+ ['ssh://git@github.com:/rancher/dashboard.git ', message],
153
+ ['ssh://git@github.com/rancher/ dashboard.git', message],
154
+ ['ssh://git@github.com/rancher/ dashboard', message],
148
155
 
149
156
  [undefined, message],
150
157
  ['', message]
@@ -200,8 +200,8 @@ export default function(
200
200
  return message;
201
201
  }
202
202
 
203
- // Test http(s) protocol
204
- if (protocol && (!/^(http|http(s))/gm.test(protocol) || (!url.startsWith('https://') && !url.startsWith('http://')))) {
203
+ // Test http(s)/ssh protocol
204
+ if (protocol && (!/^(http|https|ssh)$/gm.test(protocol) || (!url.startsWith('https://') && !url.startsWith('http://') && !url.startsWith('ssh://')))) {
205
205
  return message;
206
206
  }
207
207