@rancher/shell 3.0.8 → 3.0.9-rc.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 (192) hide show
  1. package/apis/intf/modal.ts +38 -0
  2. package/apis/intf/slide-in.ts +3 -1
  3. package/apis/shell/__tests__/slide-in.test.ts +36 -0
  4. package/apis/shell/slide-in.ts +5 -1
  5. package/assets/styles/base/_color.scss +1 -0
  6. package/assets/styles/base/_typography.scss +14 -5
  7. package/assets/styles/themes/_light.scss +1 -1
  8. package/assets/styles/themes/_modern.scss +1 -1
  9. package/assets/translations/en-us.yaml +94 -33
  10. package/assets/translations/zh-hans.yaml +0 -2
  11. package/components/ActionMenuShell.vue +4 -4
  12. package/components/CodeMirror.vue +4 -3
  13. package/components/DetailText.vue +54 -7
  14. package/components/Drawer/Chrome.vue +11 -4
  15. package/components/Drawer/DrawerCard.vue +19 -0
  16. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -11
  17. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +2 -2
  18. package/components/Drawer/ResourceDetailDrawer/index.vue +3 -20
  19. package/components/Drawer/types.ts +1 -0
  20. package/components/DynamicContent/DynamicContentCloseButton.vue +2 -2
  21. package/components/LocaleSelector.vue +1 -1
  22. package/components/Markdown.vue +1 -1
  23. package/components/PopoverCard.vue +3 -3
  24. package/components/Resource/Detail/Card/ExtrasCard.vue +39 -0
  25. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +142 -0
  26. package/components/Resource/Detail/Card/StateCard/composables.ts +41 -11
  27. package/components/Resource/Detail/Card/StateCard/index.vue +3 -9
  28. package/components/Resource/Detail/Card/StateCard/types.ts +6 -0
  29. package/components/Resource/Detail/Card/{PodsCard → StatusCard}/index.vue +11 -10
  30. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +24 -25
  31. package/components/Resource/Detail/Cards.vue +27 -0
  32. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +70 -0
  33. package/components/Resource/Detail/Masthead/index.vue +5 -0
  34. package/components/Resource/Detail/Metadata/KeyValueRow.vue +4 -2
  35. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -2
  36. package/components/Resource/Detail/ResourceRow.types.ts +14 -0
  37. package/components/Resource/Detail/ResourceRow.vue +23 -35
  38. package/components/Resource/Detail/StatusRow.vue +5 -2
  39. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +38 -7
  40. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +106 -2
  41. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  42. package/components/Resource/Detail/TitleBar/index.vue +41 -6
  43. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +49 -1
  44. package/components/ResourceDetail/Masthead/__tests__/latest.test.ts +85 -0
  45. package/components/ResourceDetail/Masthead/index.vue +1 -0
  46. package/components/ResourceDetail/Masthead/latest.vue +8 -1
  47. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  48. package/components/Setting.vue +1 -1
  49. package/components/SortableTable/index.vue +25 -0
  50. package/components/SortableTable/selection.js +25 -12
  51. package/components/SortableTable/sorting.js +1 -1
  52. package/components/Tabbed/Tab.vue +1 -0
  53. package/components/Tabbed/index.vue +29 -6
  54. package/components/Window/ContainerShell.vue +10 -13
  55. package/components/fleet/FleetClusterTargets/TargetsList.vue +47 -29
  56. package/components/fleet/FleetClusterTargets/index.vue +82 -29
  57. package/components/fleet/FleetClusters.vue +26 -12
  58. package/components/fleet/FleetGitRepoPaths.vue +2 -2
  59. package/components/fleet/FleetResources.vue +14 -0
  60. package/components/fleet/FleetValuesFrom.vue +2 -2
  61. package/components/fleet/__tests__/FleetClusterTargets.test.ts +531 -0
  62. package/components/fleet/__tests__/FleetClusters.test.ts +576 -0
  63. package/components/fleet/dashboard/ResourceDetails.vue +96 -123
  64. package/components/form/Conditions.vue +1 -15
  65. package/components/form/HookOption.vue +5 -0
  66. package/components/form/LabeledSelect.vue +1 -1
  67. package/components/form/LifecycleHooks.vue +2 -6
  68. package/components/form/ResourceLabeledSelect.vue +12 -1
  69. package/components/form/SeccompProfile.vue +113 -0
  70. package/components/form/Security.vue +244 -133
  71. package/components/form/__tests__/LabeledSelect.test.ts +1 -1
  72. package/components/form/__tests__/SeccompProfile.test.js +124 -0
  73. package/components/form/__tests__/Security.test.ts +125 -37
  74. package/components/formatter/Autoscaler.vue +2 -2
  75. package/components/formatter/FleetSummaryGraph.vue +4 -1
  76. package/components/nav/Group.vue +5 -0
  77. package/components/nav/Header.vue +3 -3
  78. package/components/nav/HeaderPageActionMenu.vue +1 -1
  79. package/components/nav/NamespaceFilter.vue +6 -6
  80. package/components/nav/NotificationCenter/index.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +41 -16
  82. package/components/nav/TopLevelMenu.vue +45 -25
  83. package/components/nav/WorkspaceSwitcher.vue +1 -1
  84. package/components/nav/__tests__/TopLevelMenu.helper.test.ts +277 -0
  85. package/components/nav/__tests__/TopLevelMenu.test.ts +160 -4
  86. package/components/templates/default.vue +0 -3
  87. package/components/templates/home.vue +0 -3
  88. package/components/templates/plain.vue +0 -3
  89. package/composables/useClickOutside.ts +1 -1
  90. package/config/product/explorer.js +1 -2
  91. package/config/types.js +41 -8
  92. package/detail/__tests__/workload.test.ts +8 -16
  93. package/detail/catalog.cattle.io.app.vue +6 -0
  94. package/detail/fleet.cattle.io.cluster.vue +6 -0
  95. package/detail/workload/index.vue +7 -109
  96. package/edit/__tests__/projectsecret.test.ts +42 -0
  97. package/edit/auth/__tests__/oidc.test.ts +50 -0
  98. package/edit/auth/oidc.vue +68 -44
  99. package/edit/autoscaling.horizontalpodautoscaler/index.vue +140 -59
  100. package/edit/autoscaling.horizontalpodautoscaler/metrics-row.vue +41 -5
  101. package/edit/projectsecret.vue +29 -0
  102. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +89 -200
  103. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +58 -17
  104. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -0
  105. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +3 -63
  106. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +82 -14
  107. package/edit/workload/__tests__/index.test.ts +122 -85
  108. package/edit/workload/index.vue +48 -29
  109. package/edit/workload/mixins/workload.js +85 -32
  110. package/list/catalog.cattle.io.clusterrepo.vue +1 -1
  111. package/list/projectsecret.vue +2 -2
  112. package/machine-config/__tests__/vmwarevsphere.test.ts +64 -0
  113. package/machine-config/amazonec2.vue +2 -2
  114. package/machine-config/vmwarevsphere.vue +58 -4
  115. package/mixins/__tests__/brand.spec.ts +18 -13
  116. package/mixins/__tests__/chart.test.ts +63 -0
  117. package/mixins/chart.js +56 -51
  118. package/models/__tests__/catalog.cattle.io.app.test.ts +33 -0
  119. package/models/__tests__/workload.test.ts +333 -0
  120. package/models/catalog.cattle.io.app.js +8 -0
  121. package/models/pod.js +14 -0
  122. package/models/secret.js +1 -1
  123. package/models/workload.js +93 -27
  124. package/package.json +4 -4
  125. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +91 -0
  126. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  127. package/pages/c/_cluster/explorer/EventsTable.vue +2 -2
  128. package/pages/c/_cluster/fleet/index.vue +18 -12
  129. package/pages/c/_cluster/manager/hostedprovider/index.vue +1 -19
  130. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  131. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  132. package/plugins/dashboard-store/__tests__/resource-class.test.ts +234 -0
  133. package/plugins/dashboard-store/actions.js +9 -8
  134. package/plugins/dashboard-store/resource-class.js +97 -1
  135. package/plugins/steve/__tests__/revision.test.ts +84 -0
  136. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +30 -0
  137. package/plugins/steve/__tests__/subscribe.spec.ts +134 -0
  138. package/plugins/steve/mutations.js +9 -0
  139. package/plugins/steve/revision.ts +26 -0
  140. package/plugins/steve/steve-pagination-utils.ts +6 -5
  141. package/plugins/steve/subscribe.js +211 -51
  142. package/plugins/subscribe-events.ts +2 -2
  143. package/rancher-components/Form/Checkbox/Checkbox.vue +13 -0
  144. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -1
  145. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -1
  146. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +3 -1
  147. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -1
  148. package/rancher-components/Pill/RcTag/RcTag.vue +1 -1
  149. package/rancher-components/Pill/index.ts +4 -0
  150. package/rancher-components/RcButton/RcButton.test.ts +53 -9
  151. package/rancher-components/RcButton/RcButton.vue +217 -25
  152. package/rancher-components/RcButton/types.ts +27 -1
  153. package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -4
  154. package/rancher-components/RcDropdown/types.ts +3 -3
  155. package/rancher-components/RcIcon/RcIcon.test.ts +42 -0
  156. package/rancher-components/RcIcon/RcIcon.vue +9 -6
  157. package/rancher-components/RcIcon/types.ts +13 -9
  158. package/rancher-components/utils/status.test.ts +10 -15
  159. package/rancher-components/utils/status.ts +5 -6
  160. package/store/aws.js +18 -12
  161. package/store/index.js +4 -8
  162. package/store/type-map.utils.ts +1 -1
  163. package/types/kube/kube-api.ts +29 -3
  164. package/types/rancher/steve.api.ts +40 -0
  165. package/types/shell/index.d.ts +99 -0
  166. package/types/store/dashboard-store.types.ts +29 -7
  167. package/types/store/pagination.types.ts +1 -0
  168. package/types/store/subscribe-events.types.ts +1 -0
  169. package/utils/__tests__/azure.test.ts +56 -0
  170. package/utils/__tests__/back-off.test.ts +364 -245
  171. package/utils/__tests__/error.test.ts +44 -0
  172. package/utils/__tests__/fleet.test.ts +8 -1
  173. package/utils/__tests__/pagination-wrapper.test.ts +167 -0
  174. package/utils/__tests__/version.test.ts +55 -1
  175. package/utils/azure.js +12 -0
  176. package/utils/back-off.ts +302 -69
  177. package/utils/cspAdaptor.ts +32 -14
  178. package/utils/dynamic-content/__tests__/index.test.ts +1 -1
  179. package/utils/dynamic-content/__tests__/new-release.test.ts +48 -7
  180. package/utils/dynamic-content/__tests__/support-notice.test.ts +1 -4
  181. package/utils/dynamic-content/index.ts +1 -6
  182. package/utils/dynamic-content/new-release.ts +5 -3
  183. package/utils/dynamic-content/types.d.ts +0 -1
  184. package/utils/error.js +9 -0
  185. package/utils/fleet.ts +2 -2
  186. package/utils/inactivity.ts +2 -3
  187. package/utils/pagination-wrapper.ts +101 -17
  188. package/utils/validators/formRules/index.ts +3 -0
  189. package/utils/version.js +38 -0
  190. package/components/auth/AzureWarning.vue +0 -77
  191. /package/components/Resource/Detail/{Card/PodsCard/Bubble.vue → Bubble.vue} +0 -0
  192. /package/components/Resource/Detail/Card/{PodsCard → StatusCard}/composable.ts +0 -0
@@ -0,0 +1,277 @@
1
+ import TopLevelMenuHelperService, { TopLevelMenuHelperLegacy, TopLevelMenuHelperPagination } from '../TopLevelMenu.helper';
2
+ import { CAPI, MANAGEMENT } from '@shell/config/types';
3
+ import PaginationWrapper from '@shell/utils/pagination-wrapper';
4
+
5
+ // Mock dependencies
6
+ jest.mock('@shell/utils/pagination-wrapper');
7
+ jest.mock('@shell/utils/cluster', () => ({
8
+ filterHiddenLocalCluster: jest.fn((clusters) => clusters),
9
+ filterOnlyKubernetesClusters: jest.fn((clusters) => clusters),
10
+ paginationFilterClusters: jest.fn(() => []),
11
+ }));
12
+
13
+ describe('topLevelMenu.helper', () => {
14
+ let mockStore: any;
15
+
16
+ beforeEach(() => {
17
+ mockStore = {
18
+ getters: {
19
+ 'management/schemaFor': jest.fn(),
20
+ 'management/all': jest.fn(),
21
+ 'management/paginationEnabled': jest.fn(),
22
+ },
23
+ dispatch: jest.fn(),
24
+ };
25
+
26
+ jest.clearAllMocks();
27
+ (PaginationWrapper as unknown as jest.Mock).mockImplementation(() => ({
28
+ request: jest.fn().mockResolvedValue({ data: [] }),
29
+ onDestroy: jest.fn(),
30
+ }));
31
+ });
32
+
33
+ describe('class: TopLevelMenuHelperLegacy', () => {
34
+ it('should dispatch findAll for CAPI.RANCHER_CLUSTER on init if schema exists', () => {
35
+ mockStore.getters['management/schemaFor'].mockReturnValue(true);
36
+ new TopLevelMenuHelperLegacy({ $store: mockStore });
37
+ expect(mockStore.dispatch).toHaveBeenCalledWith('management/findAll', { type: CAPI.RANCHER_CLUSTER });
38
+ });
39
+
40
+ it('should not dispatch findAll if schema does not exist', () => {
41
+ mockStore.getters['management/schemaFor'].mockReturnValue(false);
42
+ new TopLevelMenuHelperLegacy({ $store: mockStore });
43
+ expect(mockStore.dispatch).not.toHaveBeenCalled();
44
+ });
45
+
46
+ it('should filter and sort clusters correctly in update', async() => {
47
+ mockStore.getters['management/schemaFor'].mockReturnValue(true);
48
+ const mgmtClusters = [
49
+ {
50
+ id: 'c1', nameDisplay: 'Cluster 1', isReady: true, pinned: false, pin: jest.fn(), unpin: jest.fn()
51
+ },
52
+ {
53
+ id: 'c2', nameDisplay: 'Cluster 2', isReady: true, pinned: true, pin: jest.fn(), unpin: jest.fn()
54
+ },
55
+ {
56
+ id: 'local', nameDisplay: 'Local', isReady: true, pinned: true, isLocal: true, pin: jest.fn(), unpin: jest.fn()
57
+ },
58
+ ];
59
+ const provClusters = [
60
+ { mgmt: { id: 'c1' } },
61
+ { mgmt: { id: 'c2' } },
62
+ { mgmt: { id: 'local' } },
63
+ ];
64
+
65
+ mockStore.getters['management/all'].mockImplementation((type: string) => {
66
+ if (type === MANAGEMENT.CLUSTER) {
67
+ return mgmtClusters;
68
+ }
69
+ if (type === CAPI.RANCHER_CLUSTER) {
70
+ return provClusters;
71
+ }
72
+
73
+ return [];
74
+ });
75
+
76
+ const helper = new TopLevelMenuHelperLegacy({ $store: mockStore });
77
+
78
+ // Test with no search, expecting pinned clusters (local + c2)
79
+ await helper.update({
80
+ searchTerm: '',
81
+ pinnedIds: [],
82
+ });
83
+
84
+ expect(helper.clustersPinned).toHaveLength(2);
85
+ expect(helper.clustersPinned[0].id).toBe('local');
86
+ expect(helper.clustersPinned[1].id).toBe('c2');
87
+ expect(helper.clustersOthers).toHaveLength(1);
88
+ expect(helper.clustersOthers[0].id).toBe('c1');
89
+
90
+ // Test with search
91
+ await helper.update({
92
+ searchTerm: 'Cluster 1',
93
+ pinnedIds: [],
94
+ });
95
+
96
+ expect(helper.clustersOthers).toHaveLength(1);
97
+ expect(helper.clustersOthers[0].id).toBe('c1');
98
+ });
99
+ });
100
+
101
+ describe('class: TopLevelMenuHelperPagination', () => {
102
+ it('should initialize PaginationWrappers', () => {
103
+ mockStore.getters['management/schemaFor'].mockReturnValue(true);
104
+ new TopLevelMenuHelperPagination({ $store: mockStore });
105
+ expect(PaginationWrapper).toHaveBeenCalledTimes(3);
106
+ });
107
+
108
+ it('should update clusters correctly', async() => {
109
+ mockStore.getters['management/schemaFor'].mockReturnValue(true);
110
+ const mgmtPinned = [{
111
+ id: 'c1', nameDisplay: 'Pinned', isReady: true, pinned: true, pin: jest.fn(), unpin: jest.fn()
112
+ }];
113
+ const mgmtOthers = [{
114
+ id: 'c2', nameDisplay: 'Other', isReady: true, pinned: false, pin: jest.fn(), unpin: jest.fn()
115
+ }];
116
+ const provClusters = [
117
+ { mgmtClusterId: 'c1' },
118
+ { mgmtClusterId: 'c2' }
119
+ ];
120
+
121
+ const mockRequestPinned = jest.fn().mockResolvedValue({ data: mgmtPinned });
122
+ const mockRequestOthers = jest.fn().mockResolvedValue({ data: mgmtOthers });
123
+ const mockRequestProv = jest.fn().mockResolvedValue({ data: provClusters });
124
+
125
+ (PaginationWrapper as unknown as jest.Mock)
126
+ .mockImplementationOnce(() => ({ request: mockRequestPinned, onDestroy: jest.fn() }))
127
+ .mockImplementationOnce(() => ({ request: mockRequestOthers, onDestroy: jest.fn() }))
128
+ .mockImplementationOnce(() => ({ request: mockRequestProv, onDestroy: jest.fn() }));
129
+
130
+ const helper = new TopLevelMenuHelperPagination({ $store: mockStore });
131
+
132
+ const input = {
133
+ searchTerm: '',
134
+ pinnedIds: ['c1'],
135
+ };
136
+
137
+ await helper.update(input);
138
+
139
+ expect(mockRequestPinned).toHaveBeenCalledWith({
140
+ forceWatch: undefined,
141
+ pagination: {
142
+ filters: [{
143
+ equals: true,
144
+ fields: [{
145
+ equals: true, exact: true, field: 'id', value: input.pinnedIds[0]
146
+ }],
147
+ param: 'filter'
148
+ }],
149
+ page: 1,
150
+ projectsOrNamespaces: [],
151
+ sort: [{ asc: false, field: 'spec.internal' }, { asc: false, field: 'status.connected' }, { asc: true, field: 'spec.displayName' }]
152
+ },
153
+ revision: undefined
154
+ });
155
+ expect(mockRequestOthers).toHaveBeenCalledWith({
156
+ forceWatch: undefined,
157
+ pagination: {
158
+ filters: [{
159
+ equals: true,
160
+ fields: [{
161
+ equality: '!=', equals: false, exact: true, exists: false, field: 'id', value: input.pinnedIds[0]
162
+ }],
163
+ param: 'filter'
164
+ }],
165
+ page: 1,
166
+ pageSize: undefined,
167
+ projectsOrNamespaces: [],
168
+ sort: [{ asc: false, field: 'spec.internal' }, { asc: false, field: 'status.connected' }, { asc: true, field: 'spec.displayName' }]
169
+ },
170
+ revision: undefined
171
+ });
172
+ expect(mockRequestProv).toHaveBeenCalledWith({
173
+ forceWatch: undefined,
174
+ pagination: {
175
+ filters: [{
176
+ equals: true,
177
+ fields: [{
178
+ equals: true, exact: true, field: 'status.clusterName', value: mgmtOthers[0].id
179
+ }, {
180
+ equals: true, exact: true, field: 'status.clusterName', value: mgmtPinned[0].id
181
+ }],
182
+ param: 'filter'
183
+ }],
184
+ page: 1,
185
+ projectsOrNamespaces: [],
186
+ sort: []
187
+ },
188
+ revision: undefined
189
+ });
190
+
191
+ expect(helper.clustersPinned).toHaveLength(1);
192
+ expect(helper.clustersPinned[0].id).toBe('c1');
193
+ expect(helper.clustersOthers).toHaveLength(1);
194
+ expect(helper.clustersOthers[0].id).toBe('c2');
195
+ });
196
+
197
+ it('should filter out mgmt clusters without matching prov clusters', async() => {
198
+ mockStore.getters['management/schemaFor'].mockReturnValue(true);
199
+ const mgmtOthers = [{
200
+ id: 'c2', nameDisplay: 'Other', isReady: true, pinned: false, pin: jest.fn(), unpin: jest.fn()
201
+ }];
202
+ // No prov cluster for c2
203
+ const provClusters: any[] = [];
204
+
205
+ const mockRequestPinned = jest.fn().mockResolvedValue({ data: [] });
206
+ const mockRequestOthers = jest.fn().mockResolvedValue({ data: mgmtOthers });
207
+ const mockRequestProv = jest.fn().mockResolvedValue({ data: provClusters });
208
+
209
+ (PaginationWrapper as unknown as jest.Mock)
210
+ .mockImplementationOnce(() => ({ request: mockRequestPinned, onDestroy: jest.fn() }))
211
+ .mockImplementationOnce(() => ({ request: mockRequestOthers, onDestroy: jest.fn() }))
212
+ .mockImplementationOnce(() => ({ request: mockRequestProv, onDestroy: jest.fn() }));
213
+
214
+ const helper = new TopLevelMenuHelperPagination({ $store: mockStore });
215
+
216
+ await helper.update({
217
+ searchTerm: '',
218
+ pinnedIds: [],
219
+ });
220
+
221
+ expect(helper.clustersOthers).toHaveLength(0);
222
+ });
223
+ });
224
+
225
+ describe('class: TopLevelMenuHelperService', () => {
226
+ beforeEach(async() => {
227
+ await TopLevelMenuHelperService.reset();
228
+ });
229
+
230
+ it('should throw error if helper is accessed before init', () => {
231
+ expect(() => TopLevelMenuHelperService.helper).toThrow('Unable to use the side nav cluster helper (not initialised)');
232
+ });
233
+
234
+ it('should initialize with Legacy helper when pagination is disabled', () => {
235
+ mockStore.getters['management/paginationEnabled'].mockReturnValue(false);
236
+
237
+ TopLevelMenuHelperService.init(mockStore);
238
+
239
+ expect(TopLevelMenuHelperService.helper).toBeInstanceOf(TopLevelMenuHelperLegacy);
240
+ });
241
+
242
+ it('should initialize with Pagination helper when pagination is enabled', () => {
243
+ mockStore.getters['management/paginationEnabled'].mockReturnValue(true);
244
+
245
+ TopLevelMenuHelperService.init(mockStore);
246
+
247
+ expect(TopLevelMenuHelperService.helper).toBeInstanceOf(TopLevelMenuHelperPagination);
248
+ });
249
+
250
+ it('should not re-initialize if already initialized', () => {
251
+ mockStore.getters['management/paginationEnabled'].mockReturnValue(false);
252
+ TopLevelMenuHelperService.init(mockStore);
253
+ const helper1 = TopLevelMenuHelperService.helper;
254
+
255
+ // Change condition
256
+ mockStore.getters['management/paginationEnabled'].mockReturnValue(true);
257
+ TopLevelMenuHelperService.init(mockStore);
258
+ const helper2 = TopLevelMenuHelperService.helper;
259
+
260
+ expect(helper1).toBe(helper2);
261
+ expect(helper2).toBeInstanceOf(TopLevelMenuHelperLegacy);
262
+ });
263
+
264
+ it('should reset correctly', async() => {
265
+ mockStore.getters['management/paginationEnabled'].mockReturnValue(false);
266
+ TopLevelMenuHelperService.init(mockStore);
267
+
268
+ const helper = TopLevelMenuHelperService.helper;
269
+ const destroySpy = jest.spyOn(helper, 'destroy');
270
+
271
+ await TopLevelMenuHelperService.reset();
272
+
273
+ expect(destroySpy).toHaveBeenCalledWith();
274
+ expect(() => TopLevelMenuHelperService.helper).toThrow('Unable to use the side nav cluster helper (not initialised)');
275
+ });
276
+ });
277
+ });
@@ -5,6 +5,15 @@ import { PINNED_CLUSTERS } from '@shell/store/prefs';
5
5
  import { nextTick } from 'vue';
6
6
  import sideNavService from '@shell/components/nav/TopLevelMenu.helper';
7
7
 
8
+ jest.mock('@shell/utils/pagination-wrapper', () => {
9
+ return jest.fn().mockImplementation(() => {
10
+ return {
11
+ request: jest.fn().mockResolvedValue({ data: [] }),
12
+ onDestroy: jest.fn(),
13
+ };
14
+ });
15
+ });
16
+
8
17
  /**
9
18
  * `clusters` doubles up as both mgmt and prov clusters (don't shoot the messenger)
10
19
  */
@@ -55,11 +64,14 @@ describe('topLevelMenu', () => {
55
64
  beforeEach(() => {
56
65
  jest.useFakeTimers();
57
66
  sideNavService.reset();
67
+ sideNavService.initialized = false;
68
+ jest.restoreAllMocks();
58
69
  });
59
70
 
60
71
  afterEach(() => {
61
72
  jest.runOnlyPendingTimers();
62
73
  jest.useRealTimers();
74
+ jest.restoreAllMocks();
63
75
  });
64
76
 
65
77
  it('should display clusters', async() => {
@@ -434,8 +446,6 @@ describe('topLevelMenu', () => {
434
446
  describe('should displays a no results message if have clusters but', () => {
435
447
  it('given no matching clusters', async() => {
436
448
  const wrapper: Wrapper<InstanceType<typeof TopLevelMenu>> = mount(TopLevelMenu, {
437
- data: () => ({ clusterFilter: 'whatever' }),
438
-
439
449
  global: {
440
450
  mocks: {
441
451
  $route: {},
@@ -454,6 +464,8 @@ describe('topLevelMenu', () => {
454
464
  },
455
465
  });
456
466
 
467
+ await wrapper.setData({ clusterFilter: 'whatever' });
468
+
457
469
  await waitForIt();
458
470
 
459
471
  const noResults = wrapper.find('[data-testid="top-level-menu-no-results"]');
@@ -527,8 +539,6 @@ describe('topLevelMenu', () => {
527
539
  it('given clusters with status pinned', async() => {
528
540
  const search = 'you found me';
529
541
  const wrapper: Wrapper<InstanceType<typeof TopLevelMenu>> = mount(TopLevelMenu, {
530
- data: () => ({ clusterFilter: search }),
531
-
532
542
  global: {
533
543
  mocks: {
534
544
  $route: {},
@@ -548,6 +558,8 @@ describe('topLevelMenu', () => {
548
558
  },
549
559
  });
550
560
 
561
+ await wrapper.setData({ clusterFilter: search });
562
+
551
563
  await waitForIt();
552
564
 
553
565
  const noResults = wrapper.find('[data-testid="top-level-menu-no-results"]');
@@ -557,4 +569,148 @@ describe('topLevelMenu', () => {
557
569
  });
558
570
  });
559
571
  });
572
+
573
+ describe('initialization', () => {
574
+ it('should initialize sideNavService', () => {
575
+ const spyInit = jest.spyOn(sideNavService, 'init');
576
+
577
+ mount(TopLevelMenu, {
578
+ global: {
579
+ mocks: {
580
+ $route: {},
581
+ $store: { ...generateStore([]) },
582
+ },
583
+ stubs: ['BrandImage', 'router-link'],
584
+ },
585
+ });
586
+
587
+ expect(spyInit).toHaveBeenCalled(); // eslint-disable-line jest/prefer-called-with
588
+ });
589
+
590
+ it('should call helper.update if pagination is disabled', () => {
591
+ const store = generateStore([]);
592
+
593
+ store.getters['management/paginationEnabled'] = () => false;
594
+
595
+ jest.spyOn(sideNavService, 'init').mockImplementation(() => {});
596
+ const updateSpy = jest.fn();
597
+ const mockHelper = {
598
+ update: updateSpy, clustersPinned: [], clustersOthers: []
599
+ };
600
+
601
+ jest.spyOn(sideNavService, 'helper', 'get').mockReturnValue(mockHelper as any);
602
+
603
+ mount(TopLevelMenu, {
604
+ global: {
605
+ mocks: {
606
+ $route: {},
607
+ $store: store,
608
+ },
609
+ stubs: ['BrandImage', 'router-link'],
610
+ },
611
+ });
612
+
613
+ expect(updateSpy).toHaveBeenCalledWith({
614
+ pinnedIds: [], searchTerm: '', unPinnedMax: 10
615
+ });
616
+ });
617
+
618
+ it('should call helper.update if pagination is enabled but service not initialized', () => {
619
+ const store = generateStore([]);
620
+
621
+ store.getters['management/paginationEnabled'] = () => true;
622
+ sideNavService.initialized = false;
623
+
624
+ jest.spyOn(sideNavService, 'init').mockImplementation(() => {});
625
+ const updateSpy = jest.fn();
626
+ const mockHelper = {
627
+ update: updateSpy, clustersPinned: [], clustersOthers: []
628
+ };
629
+
630
+ jest.spyOn(sideNavService, 'helper', 'get').mockReturnValue(mockHelper as any);
631
+
632
+ mount(TopLevelMenu, {
633
+ global: {
634
+ mocks: {
635
+ $route: {},
636
+ $store: store,
637
+ },
638
+ stubs: ['BrandImage', 'router-link'],
639
+ },
640
+ });
641
+
642
+ expect(updateSpy).toHaveBeenCalledWith({
643
+ pinnedIds: [], searchTerm: '', unPinnedMax: 10
644
+ });
645
+ });
646
+
647
+ it('should NOT call helper.update if pagination is enabled and service initialized', () => {
648
+ const store = generateStore([]);
649
+
650
+ store.getters['management/paginationEnabled'] = () => true;
651
+ sideNavService.initialized = true;
652
+
653
+ jest.spyOn(sideNavService, 'init').mockImplementation(() => {});
654
+ const updateSpy = jest.fn();
655
+ const mockHelper = {
656
+ update: updateSpy, clustersPinned: [], clustersOthers: []
657
+ };
658
+
659
+ jest.spyOn(sideNavService, 'helper', 'get').mockReturnValue(mockHelper as any);
660
+
661
+ mount(TopLevelMenu, {
662
+ global: {
663
+ mocks: {
664
+ $route: {},
665
+ $store: store,
666
+ },
667
+ stubs: ['BrandImage', 'router-link'],
668
+ },
669
+ });
670
+
671
+ expect(updateSpy).not.toHaveBeenCalled();
672
+ });
673
+
674
+ it('should populate clusters from store if pagination is disabled', () => {
675
+ const clusters = [{ id: 'c1' }];
676
+ const store = generateStore(clusters);
677
+
678
+ store.getters['management/paginationEnabled'] = () => false;
679
+ store.getters['management/schemaFor'] = () => true;
680
+
681
+ const wrapper = mount(TopLevelMenu, {
682
+ global: {
683
+ mocks: {
684
+ $route: {},
685
+ $store: store,
686
+ },
687
+ stubs: ['BrandImage', 'router-link'],
688
+ },
689
+ });
690
+
691
+ expect(wrapper.vm.provClusters).toStrictEqual(clusters);
692
+ expect(wrapper.vm.mgmtClusters).toStrictEqual(clusters);
693
+ });
694
+
695
+ it('should NOT populate clusters from store if pagination is enabled', () => {
696
+ const clusters = [{ id: 'c1' }];
697
+ const store = generateStore(clusters);
698
+
699
+ store.getters['management/paginationEnabled'] = () => true;
700
+ store.getters['management/schemaFor'] = () => true;
701
+
702
+ const wrapper = mount(TopLevelMenu, {
703
+ global: {
704
+ mocks: {
705
+ $route: {},
706
+ $store: store,
707
+ },
708
+ stubs: ['BrandImage', 'router-link'],
709
+ },
710
+ });
711
+
712
+ expect(wrapper.vm.provClusters).toStrictEqual([]);
713
+ expect(wrapper.vm.mgmtClusters).toStrictEqual([]);
714
+ });
715
+ });
560
716
  });
@@ -18,7 +18,6 @@ import Inactivity from '@shell/components/Inactivity';
18
18
  import Brand from '@shell/mixins/brand';
19
19
  import FixedBanner from '@shell/components/FixedBanner';
20
20
  import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
21
- import AzureWarning from '@shell/components/auth/AzureWarning';
22
21
  import { MANAGEMENT } from '@shell/config/types';
23
22
  import { markSeenReleaseNotes } from '@shell/utils/version';
24
23
  import PageHeaderActions from '@shell/mixins/page-actions';
@@ -43,7 +42,6 @@ export default {
43
42
  WindowManager,
44
43
  FixedBanner,
45
44
  AwsComplianceBanner,
46
- AzureWarning,
47
45
  Inactivity,
48
46
  SideNav,
49
47
  },
@@ -163,7 +161,6 @@ export default {
163
161
  <div class="dashboard-root">
164
162
  <FixedBanner :header="true" />
165
163
  <AwsComplianceBanner v-if="managementReady" />
166
- <AzureWarning v-if="managementReady" />
167
164
  <div
168
165
  v-if="managementReady"
169
166
  class="dashboard-content"
@@ -7,7 +7,6 @@ import ModalManager from '@shell/components/ModalManager';
7
7
  import SlideInPanelManager from '@shell/components/SlideInPanelManager';
8
8
  import { mapPref, THEME_SHORTCUT } from '@shell/store/prefs';
9
9
  import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
10
- import AzureWarning from '@shell/components/auth/AzureWarning';
11
10
  import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
12
11
  import Inactivity from '@shell/components/Inactivity';
13
12
  import { mapState, mapGetters } from 'vuex';
@@ -23,7 +22,6 @@ export default {
23
22
  GrowlManager,
24
23
  ModalManager,
25
24
  SlideInPanelManager,
26
- AzureWarning,
27
25
  AwsComplianceBanner,
28
26
  Inactivity,
29
27
  PromptModal,
@@ -64,7 +62,6 @@ export default {
64
62
  <FixedBanner :header="true" />
65
63
  <Inactivity />
66
64
  <AwsComplianceBanner />
67
- <AzureWarning />
68
65
  <PromptModal />
69
66
  <ModalManager />
70
67
  <div
@@ -10,7 +10,6 @@ import GrowlManager from '@shell/components/GrowlManager';
10
10
  import ModalManager from '@shell/components/ModalManager';
11
11
  import SlideInPanelManager from '@shell/components/SlideInPanelManager';
12
12
  import AwsComplianceBanner from '@shell/components/AwsComplianceBanner';
13
- import AzureWarning from '@shell/components/auth/AzureWarning';
14
13
  import BrowserTabVisibility from '@shell/mixins/browser-tab-visibility';
15
14
  import Inactivity from '@shell/components/Inactivity';
16
15
  import { mapGetters } from 'vuex';
@@ -31,7 +30,6 @@ export default {
31
30
  ModalManager,
32
31
  SlideInPanelManager,
33
32
  AwsComplianceBanner,
34
- AzureWarning,
35
33
  Inactivity,
36
34
  WindowManager
37
35
  },
@@ -67,7 +65,6 @@ export default {
67
65
  <div class="dashboard-root">
68
66
  <FixedBanner :header="true" />
69
67
  <AwsComplianceBanner />
70
- <AzureWarning />
71
68
 
72
69
  <div
73
70
  class="dashboard-content"
@@ -76,6 +76,6 @@ export const useClickOutside = <T extends OnClickOutsideOptions>(
76
76
 
77
77
  onBeforeUnmount(() => {
78
78
  window.removeEventListener('click', listener as any);
79
- window.removeEventListener('pointerDown', setShouldListen);
79
+ window.removeEventListener('pointerdown', setShouldListen);
80
80
  });
81
81
  };
@@ -196,7 +196,7 @@ export function init(store) {
196
196
  configureType(SECRET, { showListMasthead: false });
197
197
  weightType(SECRET, 1, false);
198
198
 
199
- configureType(VIRTUAL_TYPES.PROJECT_SECRETS, { showListMasthead: false });
199
+ configureType(VIRTUAL_TYPES.PROJECT_SECRETS, { showListMasthead: false, resource: SECRET });
200
200
  weightType(VIRTUAL_TYPES.PROJECT_SECRETS, 2, false);
201
201
 
202
202
  configureType(EVENT, { limit: 500 });
@@ -647,7 +647,6 @@ export function init(store) {
647
647
  name: 'c-cluster-product-resource',
648
648
  params: { resource: VIRTUAL_TYPES.PROJECT_SECRETS }
649
649
  },
650
- exact: true,
651
650
  ifHaveType: [{
652
651
  store: 'management',
653
652
  type: SECRET
package/config/types.js CHANGED
@@ -81,7 +81,11 @@ export const RBAC = {
81
81
 
82
82
  export const WORKLOAD = 'workload';
83
83
 
84
- // The types that are aggregated into a "workload"
84
+ /**
85
+ * Rancher Workload types
86
+ *
87
+ * The types that are aggregated into a "workload"
88
+ */
85
89
  export const WORKLOAD_TYPES = {
86
90
  DEPLOYMENT: 'apps.deployment',
87
91
  CRON_JOB: 'batch.cronjob',
@@ -92,15 +96,44 @@ export const WORKLOAD_TYPES = {
92
96
  REPLICATION_CONTROLLER: 'replicationcontroller',
93
97
  };
94
98
 
99
+ /**
100
+ * Kube Workload Kinds
101
+ */
102
+ export const WORKLOAD_KINDS = {
103
+ DEPLOYMENT: 'Deployment',
104
+ CRON_JOB: 'CronJob',
105
+ DAEMON_SET: 'DaemonSet',
106
+ JOB: 'Job',
107
+ STATEFUL_SET: 'StatefulSet',
108
+ REPLICA_SET: 'ReplicaSet',
109
+ REPLICATION_CONTROLLER: 'ReplicationController',
110
+ };
111
+
112
+ /**
113
+ * Map Rancher Workload types to Kube Workload Kinds
114
+ */
95
115
  export const WORKLOAD_TYPE_TO_KIND_MAPPING = {
96
116
  // Each deployment creates a replicaset and the metrics are published for a replicaset.
97
- [WORKLOAD_TYPES.DEPLOYMENT]: 'ReplicaSet',
98
- [WORKLOAD_TYPES.CRON_JOB]: 'CronJob',
99
- [WORKLOAD_TYPES.DAEMON_SET]: 'DaemonSet',
100
- [WORKLOAD_TYPES.JOB]: 'Job',
101
- [WORKLOAD_TYPES.STATEFUL_SET]: 'StatefulSet',
102
- [WORKLOAD_TYPES.REPLICA_SET]: 'ReplicaSet',
103
- [WORKLOAD_TYPES.REPLICATION_CONTROLLER]: 'ReplicationController',
117
+ [WORKLOAD_TYPES.DEPLOYMENT]: WORKLOAD_KINDS.DEPLOYMENT,
118
+ [WORKLOAD_TYPES.CRON_JOB]: WORKLOAD_KINDS.CRON_JOB,
119
+ [WORKLOAD_TYPES.DAEMON_SET]: WORKLOAD_KINDS.DAEMON_SET,
120
+ [WORKLOAD_TYPES.JOB]: WORKLOAD_KINDS.JOB,
121
+ [WORKLOAD_TYPES.STATEFUL_SET]: WORKLOAD_KINDS.STATEFUL_SET,
122
+ [WORKLOAD_TYPES.REPLICA_SET]: WORKLOAD_KINDS.REPLICA_SET,
123
+ [WORKLOAD_TYPES.REPLICATION_CONTROLLER]: WORKLOAD_KINDS.REPLICATION_CONTROLLER,
124
+ };
125
+
126
+ /**
127
+ * Map Kube Workload Kinds types to Rancher Workload
128
+ */
129
+ export const WORKLOAD_KIND_TO_TYPE_MAPPING = {
130
+ [WORKLOAD_KINDS.DEPLOYMENT]: WORKLOAD_TYPES.DEPLOYMENT,
131
+ [WORKLOAD_KINDS.CRON_JOB]: WORKLOAD_TYPES.CRON_JOB,
132
+ [WORKLOAD_KINDS.DAEMON_SET]: WORKLOAD_TYPES.DAEMON_SET,
133
+ [WORKLOAD_KINDS.JOB]: WORKLOAD_TYPES.JOB,
134
+ [WORKLOAD_KINDS.STATEFUL_SET]: WORKLOAD_TYPES.STATEFUL_SET,
135
+ [WORKLOAD_KINDS.REPLICA_SET]: WORKLOAD_TYPES.REPLICA_SET,
136
+ [WORKLOAD_KINDS.REPLICATION_CONTROLLER]: WORKLOAD_TYPES.REPLICATION_CONTROLLER,
104
137
  };
105
138
 
106
139
  export const METRICS_SUPPORTED_KINDS = [