@rancher/shell 3.0.11 → 3.0.12-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.
- package/assets/styles/base/_mixins.scss +31 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -5
- package/assets/translations/en-us.yaml +5 -4
- package/assets/translations/zh-hans.yaml +0 -3
- package/components/EmptyProductPage.vue +76 -0
- package/components/Resource/Detail/CopyToClipboard.vue +1 -2
- package/components/Resource/Detail/Metadata/KeyValueRow.vue +9 -3
- package/components/Resource/Detail/TitleBar/__tests__/__snapshots__/index.test.ts.snap +31 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +45 -1
- package/components/Resource/Detail/TitleBar/index.vue +1 -1
- package/components/Resource/Detail/ViewOptions/__tests__/__snapshots__/index.test.ts.snap +9 -0
- package/components/Resource/Detail/ViewOptions/__tests__/index.test.ts +62 -0
- package/components/Resource/Detail/ViewOptions/index.vue +2 -1
- package/components/ResourceList/Masthead.vue +25 -2
- package/components/SideNav.vue +13 -0
- package/components/__tests__/PromptModal.test.ts +2 -0
- package/components/fleet/FleetClusters.vue +1 -0
- package/components/fleet/__tests__/FleetClusters.test.ts +71 -0
- package/components/form/NodeScheduling.vue +17 -3
- package/components/form/PrivateRegistry.vue +69 -0
- package/components/form/__tests__/PrivateRegistry.test.ts +133 -0
- package/components/formatter/WorkloadHealthScale.vue +3 -1
- package/components/nav/Group.vue +26 -3
- package/components/nav/Header.vue +32 -7
- package/components/nav/TopLevelMenu.vue +15 -1
- package/config/pagination-table-headers.js +8 -1
- package/config/product/apps.js +2 -1
- package/config/product/auth.js +1 -0
- package/config/product/backup.js +1 -0
- package/config/product/compliance.js +1 -1
- package/config/product/explorer.js +25 -6
- package/config/product/fleet.js +1 -0
- package/config/product/gatekeeper.js +1 -0
- package/config/product/istio.js +1 -0
- package/config/product/logging.js +1 -0
- package/config/product/longhorn.js +2 -1
- package/config/product/manager.js +1 -0
- package/config/product/monitoring.js +1 -0
- package/config/product/navlinks.js +1 -0
- package/config/product/neuvector.js +2 -1
- package/config/product/settings.js +1 -0
- package/config/product/uiplugins.js +1 -0
- package/core/__tests__/plugin-products-helpers.test.ts +454 -0
- package/core/__tests__/plugin-products.test.ts +3219 -0
- package/core/extension-manager-impl.js +30 -1
- package/core/plugin-products-base.ts +375 -0
- package/core/plugin-products-extending.ts +44 -0
- package/core/plugin-products-helpers.ts +262 -0
- package/core/plugin-products-top-level.ts +66 -0
- package/core/plugin-products-type-guards.ts +33 -0
- package/core/plugin-products.ts +50 -0
- package/core/plugin-types.ts +222 -0
- package/core/plugin.ts +45 -10
- package/core/productDebugger.js +48 -0
- package/core/types.ts +95 -11
- package/detail/__tests__/__snapshots__/fleet.cattle.io.bundle.test.ts.snap +52 -0
- package/detail/__tests__/fleet.cattle.io.bundle.test.ts +171 -0
- package/detail/fleet.cattle.io.bundle.vue +21 -34
- package/dialog/ExtensionCatalogInstallDialog.vue +1 -1
- package/dialog/InstallExtensionDialog.vue +6 -27
- package/dialog/UninstallExistingExtensionDialog.vue +141 -0
- package/dialog/UninstallExtensionDialog.vue +4 -26
- package/dialog/__tests__/UninstallExistingExtensionDialog.test.ts +114 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
- package/list/provisioning.cattle.io.cluster.vue +0 -1
- package/list/workload.vue +11 -4
- package/mixins/resource-fetch.js +12 -3
- package/models/pod.js +18 -0
- package/models/workload.js +20 -2
- package/package.json +1 -2
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +231 -13
- package/pages/c/_cluster/uiplugins/index.vue +143 -37
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -0
- package/plugins/dashboard-store/actions.js +3 -2
- package/plugins/dashboard-store/resource-class.js +62 -6
- package/plugins/plugin.js +16 -0
- package/plugins/steve/steve-pagination-utils.ts +7 -0
- package/scripts/typegen.sh +13 -1
- package/store/__tests__/type-map.test.ts +84 -24
- package/store/type-map.js +42 -3
- package/tsconfig.paths.json +1 -0
- package/types/resources/pod.ts +18 -0
- package/types/shell/index.d.ts +8506 -2909
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/axios.js +1 -4
- package/utils/dynamic-importer.js +3 -2
- package/utils/pagination-utils.ts +1 -1
- package/utils/uiplugins.ts +12 -16
- package/utils/validators/__tests__/private-registry.test.ts +76 -0
- package/utils/validators/private-registry.ts +28 -0
package/mixins/resource-fetch.js
CHANGED
|
@@ -87,6 +87,14 @@ export default {
|
|
|
87
87
|
type: Function,
|
|
88
88
|
default: null,
|
|
89
89
|
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* When making a supporting HTTP request include associated resource data
|
|
93
|
+
*/
|
|
94
|
+
includeAssociatedData: {
|
|
95
|
+
type: Boolean,
|
|
96
|
+
default: false,
|
|
97
|
+
},
|
|
90
98
|
},
|
|
91
99
|
|
|
92
100
|
computed: {
|
|
@@ -185,9 +193,10 @@ export default {
|
|
|
185
193
|
return;
|
|
186
194
|
}
|
|
187
195
|
const opt = {
|
|
188
|
-
hasManualRefresh:
|
|
189
|
-
pagination:
|
|
190
|
-
force:
|
|
196
|
+
hasManualRefresh: this.hasManualRefresh,
|
|
197
|
+
pagination: { ...this.pagination },
|
|
198
|
+
force: this.paginating !== null, // Fix for manual refresh (before ripped out).
|
|
199
|
+
includeAssociatedData: this.includeAssociatedData,
|
|
191
200
|
};
|
|
192
201
|
|
|
193
202
|
if (this.apiFilter) {
|
package/models/pod.js
CHANGED
|
@@ -4,6 +4,7 @@ import { NODE, WORKLOAD_TYPES } from '@shell/config/types';
|
|
|
4
4
|
import { escapeHtml, shortenedImage } from '@shell/utils/string';
|
|
5
5
|
import WorkloadService from '@shell/models/workload.service';
|
|
6
6
|
import { deleteProperty } from '@shell/utils/object';
|
|
7
|
+
import { POD_RESTARTS_REG_EX } from '@shell/types/resources/pod';
|
|
7
8
|
|
|
8
9
|
export const WORKLOAD_PRIORITY = {
|
|
9
10
|
[WORKLOAD_TYPES.DEPLOYMENT]: 1,
|
|
@@ -222,6 +223,9 @@ export default class Pod extends WorkloadService {
|
|
|
222
223
|
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.node', { name: escapeHtml(name) });
|
|
223
224
|
}
|
|
224
225
|
|
|
226
|
+
/**
|
|
227
|
+
* How many times has the first container restarted
|
|
228
|
+
*/
|
|
225
229
|
get restartCount() {
|
|
226
230
|
if (this.status.containerStatuses) {
|
|
227
231
|
return this.status?.containerStatuses[0].restartCount || 0;
|
|
@@ -230,6 +234,20 @@ export default class Pod extends WorkloadService {
|
|
|
230
234
|
return 0;
|
|
231
235
|
}
|
|
232
236
|
|
|
237
|
+
/**
|
|
238
|
+
* How many times does native kube report this pod has restarted
|
|
239
|
+
*/
|
|
240
|
+
get restartsCount() {
|
|
241
|
+
return this.metadata?.fields?.[3]?.match(POD_RESTARTS_REG_EX)?.[1] || '';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* When does native kube think the last pod restart happen?
|
|
246
|
+
*/
|
|
247
|
+
get restartsLaster() {
|
|
248
|
+
return this.metadata?.fields?.[3]?.match(POD_RESTARTS_REG_EX)?.[2] || '';
|
|
249
|
+
}
|
|
250
|
+
|
|
233
251
|
processSaveResponse(res) {
|
|
234
252
|
if (res._headers && res._headers.warning) {
|
|
235
253
|
const warnings = res._headers.warning.split('299') || [];
|
package/models/workload.js
CHANGED
|
@@ -9,6 +9,7 @@ import WorkloadService from '@shell/models/workload.service';
|
|
|
9
9
|
import { matching } from '@shell/utils/selector-typed';
|
|
10
10
|
import { defineAsyncComponent, markRaw } from 'vue';
|
|
11
11
|
import { useResourceCardRow } from '@shell/components/Resource/Detail/Card/StateCard/composables';
|
|
12
|
+
import { colorForState as colorForStateFn, stateDisplay as stateDisplayFn } from '@shell/plugins/dashboard-store/resource-class';
|
|
12
13
|
|
|
13
14
|
export const defaultContainer = {
|
|
14
15
|
imagePullPolicy: 'Always',
|
|
@@ -622,12 +623,29 @@ export default class Workload extends WorkloadService {
|
|
|
622
623
|
|
|
623
624
|
calcPodGauges(pods) {
|
|
624
625
|
const out = { };
|
|
626
|
+
let refPods = pods;
|
|
625
627
|
|
|
626
|
-
if (
|
|
628
|
+
if (this.metadata.associatedData) {
|
|
629
|
+
refPods = [];
|
|
630
|
+
this.metadata.associatedData.forEach((w) => {
|
|
631
|
+
if (w.gvk.kind.toLowerCase() !== POD) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return w.data.forEach((p) => {
|
|
636
|
+
refPods.push({
|
|
637
|
+
stateColor: colorForStateFn(p.state.name, p.state.error === 'true', p.state.transitioning === 'true'),
|
|
638
|
+
stateDisplay: stateDisplayFn(p.state.name),
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (!refPods) {
|
|
627
645
|
return out;
|
|
628
646
|
}
|
|
629
647
|
|
|
630
|
-
|
|
648
|
+
refPods.map((pod) => {
|
|
631
649
|
const { stateColor, stateDisplay } = pod;
|
|
632
650
|
|
|
633
651
|
if (out[stateDisplay]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rancher/shell",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.12-rc.1",
|
|
4
4
|
"description": "Rancher Dashboard Shell",
|
|
5
5
|
"repository": "https://github.com/rancher/dashboard",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"d3-selection": "3.0.0",
|
|
77
77
|
"d3": "7.3.0",
|
|
78
78
|
"dayjs": "1.11.18",
|
|
79
|
-
"defu": "6.1.4",
|
|
80
79
|
"diff2html": "3.4.24",
|
|
81
80
|
"dompurify": "3.2.5",
|
|
82
81
|
"element-matches": "^0.1.2",
|
|
@@ -440,14 +440,14 @@ export default {
|
|
|
440
440
|
/>
|
|
441
441
|
</div>
|
|
442
442
|
<SimpleBox
|
|
443
|
-
v-if="uiLoginBackgroundLight
|
|
443
|
+
v-if="uiLoginBackgroundLight"
|
|
444
444
|
class="theme-light mb-10"
|
|
445
445
|
>
|
|
446
446
|
<label class="text-muted">{{ t('branding.loginBackground.lightPreview') }}</label>
|
|
447
447
|
<img
|
|
448
448
|
class="img-preview"
|
|
449
449
|
data-testid="branding-login-background-light-preview"
|
|
450
|
-
:src="uiLoginBackgroundLight
|
|
450
|
+
:src="uiLoginBackgroundLight"
|
|
451
451
|
>
|
|
452
452
|
</SimpleBox>
|
|
453
453
|
</div>
|
|
@@ -465,14 +465,14 @@ export default {
|
|
|
465
465
|
/>
|
|
466
466
|
</div>
|
|
467
467
|
<SimpleBox
|
|
468
|
-
v-if="uiLoginBackgroundDark
|
|
468
|
+
v-if="uiLoginBackgroundDark"
|
|
469
469
|
class="theme-dark mb-10"
|
|
470
470
|
>
|
|
471
471
|
<label class="text-muted">{{ t('branding.loginBackground.darkPreview') }}</label>
|
|
472
472
|
<img
|
|
473
473
|
class="img-preview"
|
|
474
474
|
data-testid="branding-login-background-dark-preview"
|
|
475
|
-
:src="uiLoginBackgroundDark
|
|
475
|
+
:src="uiLoginBackgroundDark"
|
|
476
476
|
>
|
|
477
477
|
</SimpleBox>
|
|
478
478
|
</div>
|
|
@@ -207,6 +207,68 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
207
207
|
|
|
208
208
|
expect(items).toHaveLength(0);
|
|
209
209
|
});
|
|
210
|
+
|
|
211
|
+
it('should display repository name for non-installed plugins with chart info', () => {
|
|
212
|
+
const plugin = {
|
|
213
|
+
installed: false,
|
|
214
|
+
chart: { repoNameDisplay: 'rancher-charts' },
|
|
215
|
+
certified: true
|
|
216
|
+
};
|
|
217
|
+
const items = wrapper.vm.getFooterItems(plugin);
|
|
218
|
+
|
|
219
|
+
expect(items).toHaveLength(1);
|
|
220
|
+
expect(items[0].icon).toBe('repository-alt');
|
|
221
|
+
expect(items[0].labels).toStrictEqual(['rancher-charts']);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should display repository name for installed plugins when chart info is present (original repo exists)', () => {
|
|
225
|
+
const plugin = {
|
|
226
|
+
installed: true,
|
|
227
|
+
chart: { repoNameDisplay: 'rancher-charts' },
|
|
228
|
+
certified: true
|
|
229
|
+
};
|
|
230
|
+
const items = wrapper.vm.getFooterItems(plugin);
|
|
231
|
+
|
|
232
|
+
expect(items).toHaveLength(1);
|
|
233
|
+
expect(items[0].icon).toBe('repository-alt');
|
|
234
|
+
expect(items[0].labels).toStrictEqual(['rancher-charts']);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should display original repository name for installed plugins when original repo was removed', () => {
|
|
238
|
+
const plugin = {
|
|
239
|
+
installed: true,
|
|
240
|
+
originalRepoNameDisplay: 'removed-repo',
|
|
241
|
+
certified: true
|
|
242
|
+
};
|
|
243
|
+
const items = wrapper.vm.getFooterItems(plugin);
|
|
244
|
+
|
|
245
|
+
expect(items).toHaveLength(1);
|
|
246
|
+
expect(items[0].icon).toBe('repository-alt');
|
|
247
|
+
expect(items[0].labels).toStrictEqual(['removed-repo']);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should prefer chart.repoNameDisplay over originalRepoNameDisplay when both are present', () => {
|
|
251
|
+
const plugin = {
|
|
252
|
+
installed: true,
|
|
253
|
+
chart: { repoNameDisplay: 'current-repo' },
|
|
254
|
+
originalRepoNameDisplay: 'original-repo',
|
|
255
|
+
certified: true
|
|
256
|
+
};
|
|
257
|
+
const items = wrapper.vm.getFooterItems(plugin);
|
|
258
|
+
|
|
259
|
+
expect(items).toHaveLength(1);
|
|
260
|
+
expect(items[0].labels).toStrictEqual(['current-repo']);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should NOT display repository name when neither chart nor originalRepoNameDisplay are present', () => {
|
|
264
|
+
const plugin = {
|
|
265
|
+
installed: true,
|
|
266
|
+
certified: true
|
|
267
|
+
};
|
|
268
|
+
const items = wrapper.vm.getFooterItems(plugin);
|
|
269
|
+
|
|
270
|
+
expect(items).toHaveLength(0);
|
|
271
|
+
});
|
|
210
272
|
});
|
|
211
273
|
|
|
212
274
|
describe('getStatuses', () => {
|
|
@@ -269,6 +331,162 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
269
331
|
});
|
|
270
332
|
});
|
|
271
333
|
|
|
334
|
+
describe('showInstallDialog', () => {
|
|
335
|
+
let dispatchMock: jest.Mock;
|
|
336
|
+
|
|
337
|
+
const createWrapper = (availablePlugins: any[] = []) => {
|
|
338
|
+
dispatchMock = jest.fn().mockResolvedValue(true);
|
|
339
|
+
|
|
340
|
+
const store = {
|
|
341
|
+
getters: {
|
|
342
|
+
'prefs/get': jest.fn(),
|
|
343
|
+
'catalog/rawCharts': {},
|
|
344
|
+
'uiplugins/plugins': [],
|
|
345
|
+
'uiplugins/errors': {},
|
|
346
|
+
'management/all': () => [],
|
|
347
|
+
},
|
|
348
|
+
dispatch: dispatchMock,
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const wrapper = shallowMount(UiPluginsPage, {
|
|
352
|
+
global: {
|
|
353
|
+
mocks: {
|
|
354
|
+
$store: store,
|
|
355
|
+
t,
|
|
356
|
+
},
|
|
357
|
+
stubs: { ActionMenu: { template: '<div />' } }
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Mock the available computed property
|
|
362
|
+
Object.defineProperty(wrapper.vm, 'available', { get: () => availablePlugins });
|
|
363
|
+
|
|
364
|
+
return wrapper;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
it('should open UninstallExistingExtensionDialog when installing a plugin that is already installed from a different source', () => {
|
|
368
|
+
const installedPlugin = {
|
|
369
|
+
id: 'other-repo/my-plugin',
|
|
370
|
+
name: 'my-plugin',
|
|
371
|
+
installed: true
|
|
372
|
+
};
|
|
373
|
+
const pluginToInstall = {
|
|
374
|
+
id: 'new-repo/my-plugin',
|
|
375
|
+
name: 'my-plugin'
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const wrapper = createWrapper([installedPlugin, pluginToInstall]);
|
|
379
|
+
|
|
380
|
+
wrapper.vm.showInstallDialog(pluginToInstall, 'install', {});
|
|
381
|
+
|
|
382
|
+
expect(dispatchMock).toHaveBeenCalledWith(
|
|
383
|
+
'management/promptModal',
|
|
384
|
+
expect.objectContaining({ component: 'UninstallExistingExtensionDialog' })
|
|
385
|
+
);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should NOT open InstallExtensionDialog when plugin is already installed from a different source', () => {
|
|
389
|
+
const installedPlugin = {
|
|
390
|
+
id: 'other-repo/my-plugin',
|
|
391
|
+
name: 'my-plugin',
|
|
392
|
+
installed: true
|
|
393
|
+
};
|
|
394
|
+
const pluginToInstall = {
|
|
395
|
+
id: 'new-repo/my-plugin',
|
|
396
|
+
name: 'my-plugin'
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const wrapper = createWrapper([installedPlugin, pluginToInstall]);
|
|
400
|
+
|
|
401
|
+
wrapper.vm.showInstallDialog(pluginToInstall, 'install', {});
|
|
402
|
+
|
|
403
|
+
expect(dispatchMock).not.toHaveBeenCalledWith(
|
|
404
|
+
'management/promptModal',
|
|
405
|
+
expect.objectContaining({ component: 'InstallExtensionDialog' })
|
|
406
|
+
);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('should pass the installedPlugin to UninstallExistingExtensionDialog', () => {
|
|
410
|
+
const installedPlugin = {
|
|
411
|
+
id: 'other-repo/my-plugin',
|
|
412
|
+
name: 'my-plugin',
|
|
413
|
+
installed: true
|
|
414
|
+
};
|
|
415
|
+
const pluginToInstall = {
|
|
416
|
+
id: 'new-repo/my-plugin',
|
|
417
|
+
name: 'my-plugin'
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
const wrapper = createWrapper([installedPlugin, pluginToInstall]);
|
|
421
|
+
|
|
422
|
+
wrapper.vm.showInstallDialog(pluginToInstall, 'install', {});
|
|
423
|
+
|
|
424
|
+
expect(dispatchMock).toHaveBeenCalledWith(
|
|
425
|
+
'management/promptModal',
|
|
426
|
+
expect.objectContaining({
|
|
427
|
+
component: 'UninstallExistingExtensionDialog',
|
|
428
|
+
componentProps: expect.objectContaining({ installedPlugin })
|
|
429
|
+
})
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('should open InstallExtensionDialog when installing a plugin that is NOT installed from another source', () => {
|
|
434
|
+
const pluginToInstall = {
|
|
435
|
+
id: 'repo/my-plugin',
|
|
436
|
+
name: 'my-plugin'
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const wrapper = createWrapper([pluginToInstall]);
|
|
440
|
+
|
|
441
|
+
wrapper.vm.showInstallDialog(pluginToInstall, 'install', {});
|
|
442
|
+
|
|
443
|
+
expect(dispatchMock).toHaveBeenCalledWith(
|
|
444
|
+
'management/promptModal',
|
|
445
|
+
expect.objectContaining({ component: 'InstallExtensionDialog' })
|
|
446
|
+
);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should open InstallExtensionDialog when the same plugin id is being re-installed (same source)', () => {
|
|
450
|
+
const installedPlugin = {
|
|
451
|
+
id: 'repo/my-plugin',
|
|
452
|
+
name: 'my-plugin',
|
|
453
|
+
installed: true
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const wrapper = createWrapper([installedPlugin]);
|
|
457
|
+
|
|
458
|
+
wrapper.vm.showInstallDialog(installedPlugin, 'install', {});
|
|
459
|
+
|
|
460
|
+
expect(dispatchMock).toHaveBeenCalledWith(
|
|
461
|
+
'management/promptModal',
|
|
462
|
+
expect.objectContaining({ component: 'InstallExtensionDialog' })
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('should open InstallExtensionDialog for upgrade action even if same name exists in another repo', () => {
|
|
467
|
+
const installedPlugin = {
|
|
468
|
+
id: 'repo-a/my-plugin',
|
|
469
|
+
name: 'my-plugin',
|
|
470
|
+
installed: true
|
|
471
|
+
};
|
|
472
|
+
const otherPlugin = {
|
|
473
|
+
id: 'repo-b/my-plugin',
|
|
474
|
+
name: 'my-plugin',
|
|
475
|
+
installed: false
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const wrapper = createWrapper([installedPlugin, otherPlugin]);
|
|
479
|
+
|
|
480
|
+
// Upgrade action should always go to InstallExtensionDialog
|
|
481
|
+
wrapper.vm.showInstallDialog(installedPlugin, 'upgrade', {});
|
|
482
|
+
|
|
483
|
+
expect(dispatchMock).toHaveBeenCalledWith(
|
|
484
|
+
'management/promptModal',
|
|
485
|
+
expect.objectContaining({ component: 'InstallExtensionDialog' })
|
|
486
|
+
);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
272
490
|
describe('watch: helmOps', () => {
|
|
273
491
|
let wrapper: VueWrapper<any>;
|
|
274
492
|
let updatePluginInstallStatusMock: jest.Mock;
|
|
@@ -296,10 +514,10 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
296
514
|
computed: {
|
|
297
515
|
// Override the computed property for this test suite
|
|
298
516
|
available: () => [
|
|
299
|
-
{ name: 'plugin1' },
|
|
300
|
-
{ name: 'plugin2' },
|
|
301
|
-
{ name: 'plugin3' },
|
|
302
|
-
{ name: 'plugin4' },
|
|
517
|
+
{ name: 'plugin1', id: 'repo/plugin1' },
|
|
518
|
+
{ name: 'plugin2', id: 'repo/plugin2' },
|
|
519
|
+
{ name: 'plugin3', id: 'repo/plugin3' },
|
|
520
|
+
{ name: 'plugin4', id: 'repo/plugin4' },
|
|
303
521
|
],
|
|
304
522
|
hasMenuActions: () => true,
|
|
305
523
|
menuActions: () => []
|
|
@@ -311,9 +529,9 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
311
529
|
|
|
312
530
|
// Set the 'installing' status on the component instance
|
|
313
531
|
wrapper.vm.installing = {
|
|
314
|
-
plugin1: 'install',
|
|
315
|
-
plugin2: 'downgrade',
|
|
316
|
-
plugin3: 'uninstall',
|
|
532
|
+
'repo/plugin1': 'install',
|
|
533
|
+
'repo/plugin2': 'downgrade',
|
|
534
|
+
'repo/plugin3': 'uninstall',
|
|
317
535
|
};
|
|
318
536
|
|
|
319
537
|
// Reset errors
|
|
@@ -330,7 +548,7 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
330
548
|
wrapper.vm.helmOps = helmOps;
|
|
331
549
|
await wrapper.vm.$nextTick();
|
|
332
550
|
|
|
333
|
-
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin1', 'install');
|
|
551
|
+
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('repo/plugin1', 'install');
|
|
334
552
|
});
|
|
335
553
|
|
|
336
554
|
it('should not update status for an upgrade op when a downgrade was initiated', async() => {
|
|
@@ -344,7 +562,7 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
344
562
|
await wrapper.vm.$nextTick();
|
|
345
563
|
|
|
346
564
|
// It should not be called with 'upgrade' for plugin2
|
|
347
|
-
expect(updatePluginInstallStatusMock).not.toHaveBeenCalledWith('plugin2', 'upgrade');
|
|
565
|
+
expect(updatePluginInstallStatusMock).not.toHaveBeenCalledWith('repo/plugin2', 'upgrade');
|
|
348
566
|
});
|
|
349
567
|
|
|
350
568
|
it('should clear status for a completed uninstall operation', async() => {
|
|
@@ -357,7 +575,7 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
357
575
|
wrapper.vm.helmOps = helmOps;
|
|
358
576
|
await wrapper.vm.$nextTick();
|
|
359
577
|
|
|
360
|
-
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin3', false);
|
|
578
|
+
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('repo/plugin3', false);
|
|
361
579
|
});
|
|
362
580
|
|
|
363
581
|
it('should set error and clear status for a failed operation', async() => {
|
|
@@ -370,8 +588,8 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
370
588
|
wrapper.vm.helmOps = helmOps;
|
|
371
589
|
await wrapper.vm.$nextTick();
|
|
372
590
|
|
|
373
|
-
expect(wrapper.vm.errors
|
|
374
|
-
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin1', false);
|
|
591
|
+
expect(wrapper.vm.errors['repo/plugin1']).toBe(true);
|
|
592
|
+
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('repo/plugin1', false);
|
|
375
593
|
});
|
|
376
594
|
|
|
377
595
|
it('should clear status for plugins with no active operation', async() => {
|
|
@@ -385,7 +603,7 @@ describe('page: UI plugins/Extensions', () => {
|
|
|
385
603
|
await wrapper.vm.$nextTick();
|
|
386
604
|
|
|
387
605
|
// plugin4 has no op, so its status should be cleared
|
|
388
|
-
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('plugin4', false);
|
|
606
|
+
expect(updatePluginInstallStatusMock).toHaveBeenCalledWith('repo/plugin4', false);
|
|
389
607
|
});
|
|
390
608
|
});
|
|
391
609
|
});
|