@rancher/shell 3.0.6 → 3.0.7

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 (78) 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/styles/base/_basic.scss +2 -2
  4. package/assets/styles/base/_color-classic.scss +51 -0
  5. package/assets/styles/base/_color.scss +3 -3
  6. package/assets/styles/base/_mixins.scss +1 -1
  7. package/assets/styles/base/_variables-classic.scss +47 -0
  8. package/assets/styles/global/_button.scss +49 -17
  9. package/assets/styles/global/_form.scss +1 -1
  10. package/assets/styles/themes/_dark.scss +4 -0
  11. package/assets/styles/themes/_light.scss +3 -69
  12. package/assets/styles/themes/_modern.scss +194 -50
  13. package/assets/styles/vendor/vue-select.scss +1 -2
  14. package/assets/translations/en-us.yaml +33 -21
  15. package/components/ClusterIconMenu.vue +1 -1
  16. package/components/ClusterProviderIcon.vue +1 -1
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/IconOrSvg.vue +40 -29
  19. package/components/ResourceDetail/index.vue +1 -0
  20. package/components/SortableTable/sorting.js +3 -1
  21. package/components/Tabbed/index.vue +5 -5
  22. package/components/form/ResourceTabs/index.vue +37 -18
  23. package/components/form/SecretSelector.vue +6 -2
  24. package/components/nav/Group.vue +29 -9
  25. package/components/nav/Header.vue +6 -8
  26. package/components/nav/NamespaceFilter.vue +1 -1
  27. package/components/nav/TopLevelMenu.helper.ts +47 -20
  28. package/components/nav/TopLevelMenu.vue +44 -14
  29. package/components/nav/Type.vue +0 -5
  30. package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
  31. package/config/pagination-table-headers.js +10 -2
  32. package/config/product/explorer.js +4 -3
  33. package/config/table-headers.js +9 -0
  34. package/core/plugin.ts +18 -6
  35. package/core/types.ts +8 -0
  36. package/detail/provisioning.cattle.io.cluster.vue +1 -0
  37. package/dialog/InstallExtensionDialog.vue +71 -45
  38. package/dialog/UninstallExtensionDialog.vue +2 -1
  39. package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
  40. package/edit/auth/oidc.vue +86 -16
  41. package/mixins/__tests__/chart.test.ts +1 -1
  42. package/mixins/chart.js +1 -1
  43. package/models/event.js +7 -0
  44. package/models/provisioning.cattle.io.cluster.js +9 -0
  45. package/package.json +1 -1
  46. package/pages/c/_cluster/explorer/EventsTable.vue +3 -6
  47. package/pages/c/_cluster/settings/performance.vue +1 -1
  48. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
  49. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
  50. package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
  51. package/pages/c/_cluster/uiplugins/index.vue +110 -94
  52. package/plugins/__tests__/subscribe.events.test.ts +194 -0
  53. package/plugins/dashboard-store/actions.js +3 -0
  54. package/plugins/dashboard-store/getters.js +1 -1
  55. package/plugins/dashboard-store/resource-class.js +3 -3
  56. package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
  57. package/plugins/steve/index.js +18 -10
  58. package/plugins/steve/mutations.js +2 -2
  59. package/plugins/steve/resourceWatcher.js +2 -2
  60. package/plugins/steve/steve-pagination-utils.ts +12 -9
  61. package/plugins/steve/subscribe.js +113 -85
  62. package/plugins/subscribe-events.ts +211 -0
  63. package/rancher-components/BadgeState/BadgeState.vue +8 -6
  64. package/rancher-components/Banner/Banner.vue +2 -1
  65. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  66. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  67. package/store/index.js +12 -22
  68. package/types/extension-manager.ts +8 -1
  69. package/types/resources/settings.d.ts +24 -17
  70. package/types/shell/index.d.ts +352 -335
  71. package/types/store/subscribe-events.types.ts +70 -0
  72. package/types/store/subscribe.types.ts +6 -22
  73. package/utils/pagination-utils.ts +87 -28
  74. package/utils/pagination-wrapper.ts +6 -8
  75. package/utils/sort.js +5 -0
  76. package/utils/unit-tests/pagination-utils.spec.ts +283 -0
  77. package/utils/validators/formRules/__tests__/index.test.ts +7 -0
  78. package/utils/validators/formRules/index.ts +2 -2
@@ -1,5 +1,6 @@
1
- import { shallowMount } from '@vue/test-utils';
1
+ import { shallowMount, VueWrapper } from '@vue/test-utils';
2
2
  import UiPluginsPage from '@shell/pages/c/_cluster/uiplugins/index.vue';
3
+ import { UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins';
3
4
 
4
5
  const t = (key: string, args: Object) => {
5
6
  if (args) {
@@ -10,11 +11,16 @@ const t = (key: string, args: Object) => {
10
11
  };
11
12
 
12
13
  describe('page: UI plugins/Extensions', () => {
13
- let wrapper;
14
+ let wrapper: VueWrapper<any>;
14
15
 
15
16
  const mountComponent = (mocks = {}) => {
16
17
  const store = {
17
- getters: { 'prefs/get': jest.fn() },
18
+ getters: {
19
+ 'prefs/get': jest.fn(),
20
+ 'catalog/rawCharts': [],
21
+ 'uiplugins/plugins': [],
22
+ 'uiplugins/errors': {},
23
+ },
18
24
  dispatch: () => Promise.resolve(),
19
25
  };
20
26
 
@@ -57,8 +63,7 @@ describe('page: UI plugins/Extensions', () => {
57
63
  };
58
64
  const actions = wrapper.vm.getPluginActions(plugin);
59
65
 
60
- expect(actions).toHaveLength(1);
61
- expect(actions[0].action).toBe('uninstall');
66
+ expect(actions.find((action: any) => action.action === 'uninstall')).toBeDefined();
62
67
  });
63
68
 
64
69
  it('should not return uninstall action for a builtin plugin', () => {
@@ -70,10 +75,10 @@ describe('page: UI plugins/Extensions', () => {
70
75
  };
71
76
  const actions = wrapper.vm.getPluginActions(plugin);
72
77
 
73
- expect(actions.some((a) => a.action === 'uninstall')).toBe(false);
78
+ expect(actions.some((action: any) => action.action === 'uninstall')).toBe(false);
74
79
  });
75
80
 
76
- it('should return update action for an installed plugin with an upgrade', () => {
81
+ it('should return upgrade action for an installed plugin with an upgrade', () => {
77
82
  const plugin = {
78
83
  installed: true,
79
84
  installableVersions: [],
@@ -82,33 +87,35 @@ describe('page: UI plugins/Extensions', () => {
82
87
  };
83
88
  const actions = wrapper.vm.getPluginActions(plugin);
84
89
 
85
- expect(actions.some((a) => a.action === 'update')).toBe(true);
90
+ expect(actions.some((action: any) => action.action === 'upgrade')).toBe(true);
86
91
  });
87
92
 
88
- it('should return rollback action for an installed plugin with multiple installable versions and no upgrade', () => {
93
+ it('should return downgrade action for an installed plugin with older installable versions', () => {
89
94
  const plugin = {
90
95
  installed: true,
91
96
  installableVersions: [{ version: '1.0.0' }, { version: '0.9.0' }],
92
97
  builtin: false,
93
98
  upgrade: null,
99
+ installedVersion: '1.0.0',
94
100
  };
95
101
  const actions = wrapper.vm.getPluginActions(plugin);
96
102
 
97
- expect(actions.some((a) => a.action === 'rollback')).toBe(true);
103
+ expect(actions.some((action: any) => action.action === 'downgrade')).toBe(true);
98
104
  });
99
105
 
100
- it('should return all applicable actions', () => {
106
+ it('should return all applicable actions (upgrade, downgrade, uninstall)', () => {
101
107
  const plugin = {
102
108
  installed: true,
103
- installableVersions: [{ version: '1.1.0' }, { version: '1.0.0' }],
109
+ installableVersions: [{ version: '1.1.0' }, { version: '1.0.0' }, { version: '0.9.0' }],
104
110
  builtin: false,
105
111
  upgrade: '1.1.0',
112
+ installedVersion: '1.0.0',
106
113
  };
107
114
  const actions = wrapper.vm.getPluginActions(plugin);
108
115
 
109
- expect(actions.map((a) => a.action)).toContain('uninstall');
110
- expect(actions.map((a) => a.action)).toContain('update');
111
- expect(actions.map((a) => a.action)).not.toContain('rollback');
116
+ expect(actions.map((action: any) => action.action)).toContain('uninstall');
117
+ expect(actions.map((action: any) => action.action)).toContain('upgrade');
118
+ expect(actions.map((action: any) => action.action)).toContain('downgrade');
112
119
  });
113
120
  });
114
121
 
@@ -120,43 +127,39 @@ describe('page: UI plugins/Extensions', () => {
120
127
  expect(items[0].label).toBe('v1.0.0');
121
128
  });
122
129
 
123
- it('should include upgrade availability in tooltip', () => {
124
- const plugin = { displayVersionLabel: 'v1.0.0', upgrade: 'v1.1.0' };
125
- const items = wrapper.vm.getSubHeaderItems(plugin);
126
-
127
- expect(items[0].labelTooltip).toBe('plugins.upgradeAvailableTooltip with {"version":"v1.1.0"}');
128
- });
129
-
130
130
  it('should show installing status', () => {
131
131
  const plugin = { displayVersionLabel: 'v1.0.0', installing: 'install' };
132
132
  const items = wrapper.vm.getSubHeaderItems(plugin);
133
133
 
134
- expect(items).toHaveLength(2);
135
- expect(items[1].label).toBe('plugins.labels.installing');
134
+ expect(items.find((item: any) => item.label === 'plugins.labels.installing')).toBeDefined();
136
135
  });
137
136
 
138
137
  it('should show uninstalling status', () => {
139
138
  const plugin = { displayVersionLabel: 'v1.0.0', installing: 'uninstall' };
140
139
  const items = wrapper.vm.getSubHeaderItems(plugin);
141
140
 
142
- expect(items).toHaveLength(2);
143
- expect(items[1].label).toBe('plugins.labels.uninstalling');
141
+ expect(items.find((item: any) => item.label === 'plugins.labels.uninstalling')).toBeDefined();
142
+ });
143
+
144
+ it('should show upgrading status', () => {
145
+ const plugin = { displayVersionLabel: 'v1.0.0', installing: 'upgrade' };
146
+ const items = wrapper.vm.getSubHeaderItems(plugin);
147
+
148
+ expect(items.find((item: any) => item.label === 'plugins.labels.upgrading')).toBeDefined();
144
149
  });
145
150
 
146
- it('should show updating status', () => {
147
- const plugin = { displayVersionLabel: 'v1.0.0', installing: 'update' };
151
+ it('should show downgrading status', () => {
152
+ const plugin = { displayVersionLabel: 'v1.0.0', installing: 'downgrade' };
148
153
  const items = wrapper.vm.getSubHeaderItems(plugin);
149
154
 
150
- expect(items).toHaveLength(2);
151
- expect(items[1].label).toBe('plugins.labels.updating');
155
+ expect(items.find((item: any) => item.label === 'plugins.labels.downgrading')).toBeDefined();
152
156
  });
153
157
 
154
- it('should show rolling back status', () => {
155
- const plugin = { displayVersionLabel: 'v1.0.0', installing: 'rollback' };
158
+ it('should include date info', () => {
159
+ const plugin = { created: '2023-01-01T00:00:00Z' };
156
160
  const items = wrapper.vm.getSubHeaderItems(plugin);
157
161
 
158
- expect(items).toHaveLength(2);
159
- expect(items[1].label).toBe('plugins.labels.rollingBack');
162
+ expect(items.find((item: any) => item.icon === 'icon-refresh-alt')).toBeDefined();
160
163
  });
161
164
  });
162
165
 
@@ -200,13 +203,16 @@ describe('page: UI plugins/Extensions', () => {
200
203
  });
201
204
 
202
205
  describe('getStatuses', () => {
203
- it('should return "installed" status for installed, non-builtin plugins', () => {
206
+ it('should return "installed" status with version for installed, non-builtin plugins', () => {
204
207
  const plugin = {
205
- installed: true, builtin: false, installing: false
208
+ installed: true,
209
+ builtin: false,
210
+ installing: false,
211
+ installedVersion: '1.2.3',
206
212
  };
207
213
  const statuses = wrapper.vm.getStatuses(plugin);
208
214
 
209
- expect(statuses[0].tooltip.key).toBe('generic.installed');
215
+ expect(statuses[0].tooltip.text).toBe('generic.installed (1.2.3)');
210
216
  });
211
217
 
212
218
  it('should return "upgradeable" status for plugins with an upgrade', () => {
@@ -250,14 +256,15 @@ describe('page: UI plugins/Extensions', () => {
250
256
  it('should combine deprecated and other errors in tooltip', () => {
251
257
  const plugin = { chart: { deprecated: true }, helmError: true };
252
258
  const statuses = wrapper.vm.getStatuses(plugin);
253
- const warningStatus = statuses.find((s) => s.icon === 'icon-alert-alt');
259
+ const warningStatus = statuses.find((status: any) => status.icon === 'icon-alert-alt');
254
260
 
255
261
  expect(warningStatus.tooltip.text).toBe('generic.deprecated. generic.error: plugins.helmError');
256
262
  });
257
263
  });
258
264
 
259
265
  describe('watch: helmOps', () => {
260
- let wrapper;
266
+ let wrapper: VueWrapper<any>;
267
+ let updatePluginInstallStatusMock: jest.Mock;
261
268
 
262
269
  beforeEach(() => {
263
270
  const store = {
@@ -265,9 +272,10 @@ describe('page: UI plugins/Extensions', () => {
265
272
  'prefs/get': jest.fn(),
266
273
  'catalog/rawCharts': {},
267
274
  'uiplugins/plugins': [],
268
- 'uiplugins/errors': {}
275
+ 'uiplugins/errors': {},
276
+ 'features/get': () => true,
269
277
  },
270
- dispatch: () => Promise.resolve(),
278
+ dispatch: () => Promise.resolve(true),
271
279
  };
272
280
 
273
281
  wrapper = shallowMount(UiPluginsPage, {
@@ -277,42 +285,100 @@ describe('page: UI plugins/Extensions', () => {
277
285
  t,
278
286
  },
279
287
  stubs: { ActionMenu: { template: '<div />' } }
288
+ },
289
+ computed: {
290
+ // Override the computed property for this test suite
291
+ available: () => [
292
+ { name: 'plugin1' },
293
+ { name: 'plugin2' },
294
+ { name: 'plugin3' },
295
+ { name: 'plugin4' },
296
+ ],
297
+ hasMenuActions: () => true,
298
+ menuActions: () => []
280
299
  }
281
300
  });
301
+
302
+ updatePluginInstallStatusMock = jest.fn();
303
+ wrapper.vm.updatePluginInstallStatus = updatePluginInstallStatusMock;
304
+
305
+ // Set the 'installing' status on the component instance
306
+ wrapper.vm.installing = {
307
+ plugin1: 'install',
308
+ plugin2: 'downgrade',
309
+ plugin3: 'uninstall',
310
+ };
311
+
312
+ // Reset errors
313
+ wrapper.vm.errors = {};
314
+ });
315
+
316
+ it('should update status for an active operation', async() => {
317
+ const helmOps = [{
318
+ namespace: UI_PLUGIN_NAMESPACE,
319
+ status: { releaseName: 'plugin1', action: 'install' },
320
+ metadata: { creationTimestamp: '1', state: { transitioning: true } }
321
+ }];
322
+
323
+ wrapper.vm.helmOps = helmOps;
324
+ await wrapper.vm.$nextTick();
325
+
326
+ expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin1', 'install');
282
327
  });
283
328
 
284
- it('should set status to "update" for an upgrade operation', async() => {
285
- const plugin = { name: 'my-plugin' };
329
+ it('should not update status for an upgrade op when a downgrade was initiated', async() => {
330
+ const helmOps = [{
331
+ namespace: UI_PLUGIN_NAMESPACE,
332
+ status: { releaseName: 'plugin2', action: 'upgrade' },
333
+ metadata: { creationTimestamp: '1', state: { transitioning: true } }
334
+ }];
286
335
 
287
- wrapper.vm.available = [plugin];
288
- wrapper.vm.installing['my-plugin'] = 'update';
336
+ wrapper.vm.helmOps = helmOps;
337
+ await wrapper.vm.$nextTick();
289
338
 
339
+ // It should not be called with 'upgrade' for plugin2
340
+ expect(updatePluginInstallStatusMock).not.toHaveBeenCalledWith('plugin2', 'upgrade');
341
+ });
342
+
343
+ it('should clear status for a completed uninstall operation', async() => {
290
344
  const helmOps = [{
291
- metadata: { state: { transitioning: true } },
292
- status: { releaseName: 'my-plugin', action: 'upgrade' }
345
+ namespace: UI_PLUGIN_NAMESPACE,
346
+ status: { releaseName: 'plugin3', action: 'uninstall' },
347
+ metadata: { creationTimestamp: '1', state: { transitioning: false } }
293
348
  }];
294
349
 
295
350
  wrapper.vm.helmOps = helmOps;
296
351
  await wrapper.vm.$nextTick();
297
352
 
298
- expect(wrapper.vm.installing['my-plugin']).toBe('update');
353
+ expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin3', false);
299
354
  });
300
355
 
301
- it('should keep status as "rollback" during an upgrade operation if it was already rolling back', async() => {
302
- const plugin = { name: 'my-plugin' };
356
+ it('should set error and clear status for a failed operation', async() => {
357
+ const helmOps = [{
358
+ namespace: UI_PLUGIN_NAMESPACE,
359
+ status: { releaseName: 'plugin1', action: 'install' },
360
+ metadata: { creationTimestamp: '1', state: { transitioning: false, error: true } }
361
+ }];
362
+
363
+ wrapper.vm.helmOps = helmOps;
364
+ await wrapper.vm.$nextTick();
303
365
 
304
- wrapper.vm.available = [plugin];
305
- wrapper.vm.installing['my-plugin'] = 'rollback';
366
+ expect(wrapper.vm.errors.plugin1).toBe(true);
367
+ expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin1', false);
368
+ });
306
369
 
370
+ it('should clear status for plugins with no active operation', async() => {
307
371
  const helmOps = [{
308
- metadata: { state: { transitioning: true } },
309
- status: { releaseName: 'my-plugin', action: 'upgrade' }
372
+ namespace: UI_PLUGIN_NAMESPACE,
373
+ status: { releaseName: 'plugin1', action: 'install' },
374
+ metadata: { creationTimestamp: '1', state: { transitioning: true } }
310
375
  }];
311
376
 
312
377
  wrapper.vm.helmOps = helmOps;
313
378
  await wrapper.vm.$nextTick();
314
379
 
315
- expect(wrapper.vm.installing['my-plugin']).toBe('rollback');
380
+ // plugin4 has no op, so its status should be cleared
381
+ expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin4', false);
316
382
  });
317
383
  });
318
384
  });