@rancher/shell 3.0.10 → 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 +12 -9
- package/assets/translations/zh-hans.yaml +0 -3
- package/chart/__tests__/rancher-backup-index.test.ts +248 -0
- package/chart/rancher-backup/index.vue +41 -2
- package/components/BrandImage.vue +6 -5
- package/components/ConsumptionGauge.vue +12 -4
- package/components/DynamicContent/DynamicContentIcon.vue +3 -2
- package/components/EmptyProductPage.vue +76 -0
- package/components/ExplorerProjectsNamespaces.vue +1 -4
- package/components/LazyImage.vue +2 -1
- package/components/Resource/Detail/Card/Scaler.vue +4 -4
- 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/Tabbed/index.vue +6 -0
- package/components/__tests__/ConsumptionGauge.test.ts +31 -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/ProjectMemberEditor.vue +0 -10
- 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.helper.ts +7 -79
- package/components/nav/TopLevelMenu.vue +15 -1
- package/components/nav/__tests__/TopLevelMenu.helper.test.ts +2 -53
- package/config/pagination-table-headers.js +8 -1
- package/config/private-label.js +2 -1
- package/config/product/apps.js +3 -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__/extension-manager-impl.test.js +187 -2
- 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 +34 -3
- package/core/plugin-helpers.ts +31 -0
- 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/__tests__/node.test.ts +83 -0
- package/detail/fleet.cattle.io.bundle.vue +21 -34
- package/detail/management.cattle.io.oidcclient.vue +2 -1
- package/detail/node.vue +1 -0
- 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/catalog.cattle.io.clusterrepo.vue +17 -3
- package/edit/cloudcredential.vue +2 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -6
- package/edit/provisioning.cattle.io.cluster/__tests__/Ingress.test.ts +176 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +5 -4
- package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -1
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -2
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +6 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +7 -2
- package/edit/secret/generic.vue +1 -0
- package/edit/secret/index.vue +2 -1
- package/edit/service.vue +2 -14
- package/list/management.cattle.io.feature.vue +7 -1
- package/list/provisioning.cattle.io.cluster.vue +0 -50
- package/list/workload.vue +11 -4
- package/mixins/brand.js +2 -1
- package/mixins/resource-fetch.js +12 -3
- package/models/catalog.cattle.io.clusterrepo.js +9 -0
- package/models/cluster.x-k8s.io.machinedeployment.js +8 -3
- package/models/management.cattle.io.authconfig.js +2 -1
- package/models/management.cattle.io.cluster.js +4 -3
- package/models/monitoring.coreos.com.receiver.js +11 -6
- package/models/pod.js +18 -0
- package/models/provisioning.cattle.io.cluster.js +2 -2
- package/models/workload.js +20 -2
- package/package.json +5 -6
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +0 -1
- package/pages/c/_cluster/apps/charts/index.vue +3 -8
- package/pages/c/_cluster/apps/charts/install.vue +8 -9
- package/pages/c/_cluster/istio/index.vue +4 -2
- package/pages/c/_cluster/longhorn/index.vue +2 -1
- package/pages/c/_cluster/monitoring/index.vue +2 -2
- package/pages/c/_cluster/neuvector/index.vue +2 -1
- package/pages/c/_cluster/settings/brand.vue +4 -4
- package/pages/c/_cluster/settings/performance.vue +0 -5
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +2 -1
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +231 -13
- package/pages/c/_cluster/uiplugins/index.vue +145 -38
- 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 +8 -2
- package/plugins/steve/subscribe.js +29 -4
- package/rancher-components/RcButton/RcButton.vue +3 -3
- package/rancher-components/RcButtonSplit/RcButtonSplit.test.ts +253 -0
- package/rancher-components/RcButtonSplit/RcButtonSplit.vue +158 -0
- package/rancher-components/RcButtonSplit/index.ts +1 -0
- package/scripts/test-plugins-build.sh +4 -4
- 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 -2908
- package/types/store/dashboard-store.types.ts +5 -0
- package/types/store/pagination.types.ts +6 -0
- package/utils/__tests__/require-asset.test.ts +98 -0
- package/utils/async.ts +1 -5
- package/utils/axios.js +1 -4
- package/utils/brand.ts +3 -1
- package/utils/dynamic-importer.js +3 -2
- package/utils/favicon.js +4 -3
- package/utils/pagination-utils.ts +1 -1
- package/utils/require-asset.ts +95 -0
- 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/vue.config.js +4 -3
- package/components/HarvesterServiceAddOnConfig.vue +0 -207
package/core/plugin.ts
CHANGED
|
@@ -19,12 +19,20 @@ import {
|
|
|
19
19
|
PaginationTableColumn,
|
|
20
20
|
ExtensionEnvironment,
|
|
21
21
|
ServerSidePaginationExtensionConfig,
|
|
22
|
-
TableAction
|
|
22
|
+
TableAction,
|
|
23
23
|
} from './types';
|
|
24
|
+
import {
|
|
25
|
+
ProductMetadata,
|
|
26
|
+
ProductSinglePage,
|
|
27
|
+
ProductChild,
|
|
28
|
+
StandardProductName,
|
|
29
|
+
RouteRecordRawWithParams
|
|
30
|
+
} from './plugin-types';
|
|
24
31
|
import coreStore, { coreStoreModule, coreStoreState } from '@shell/plugins/dashboard-store';
|
|
25
32
|
import { defineAsyncComponent, markRaw, Component } from 'vue';
|
|
26
33
|
import { getVersionData, CURRENT_RANCHER_VERSION } from '@shell/config/version';
|
|
27
34
|
import { ExtensionManagerTypes } from '@shell/types/extension-manager';
|
|
35
|
+
import { PluginProduct } from './plugin-products';
|
|
28
36
|
|
|
29
37
|
/** Registration IDs used for different extension points in the extensions catalog */
|
|
30
38
|
export const EXT_IDS = {
|
|
@@ -42,18 +50,20 @@ export type ProductFunction = (plugin: IPlugin, store: any) => void;
|
|
|
42
50
|
export class Plugin implements IPlugin {
|
|
43
51
|
public id: string;
|
|
44
52
|
public name: string;
|
|
53
|
+
public topLevelProduct = false;
|
|
45
54
|
public types: ExtensionManagerTypes = {};
|
|
46
55
|
public l10n: { [key: string]: Function[] } = {};
|
|
47
56
|
public modelExtensions: { [key: string]: Function[] } = {};
|
|
48
57
|
public locales: { locale: string, label: string}[] = [];
|
|
49
58
|
public products: ProductFunction[] = [];
|
|
50
59
|
public productNames: string[] = [];
|
|
51
|
-
public routes: { parent?: string, route: RouteRecordRaw }[] = [];
|
|
60
|
+
public routes: { parent?: string, route: RouteRecordRaw | RouteRecordRawWithParams }[] = [];
|
|
52
61
|
public stores: { storeName: string, register: RegisterStore, unregister: UnregisterStore }[] = [];
|
|
53
62
|
public onEnter: OnNavToPackage = () => Promise.resolve();
|
|
54
63
|
public onLeave: OnNavAwayFromPackage = () => Promise.resolve();
|
|
55
64
|
public _onLogOut: OnLogOut = () => Promise.resolve();
|
|
56
65
|
public onLogIn: OnLogIn = () => Promise.resolve();
|
|
66
|
+
public productConfigs: PluginProduct[] = [];
|
|
57
67
|
|
|
58
68
|
public uiConfig: { [key: string]: any } = {};
|
|
59
69
|
|
|
@@ -110,7 +120,12 @@ export class Plugin implements IPlugin {
|
|
|
110
120
|
this._validators = vals;
|
|
111
121
|
}
|
|
112
122
|
|
|
123
|
+
_registerTopLevelProduct() {
|
|
124
|
+
this.topLevelProduct = true;
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
// Track which products the plugin creates
|
|
128
|
+
// Legacy DSL method
|
|
114
129
|
DSL(store: any, productName: string) {
|
|
115
130
|
const storeDSL = STORE_DSL(store, productName);
|
|
116
131
|
|
|
@@ -119,8 +134,28 @@ export class Plugin implements IPlugin {
|
|
|
119
134
|
return storeDSL;
|
|
120
135
|
}
|
|
121
136
|
|
|
122
|
-
addProduct(product: ProductFunction): void {
|
|
123
|
-
|
|
137
|
+
addProduct(product: ProductFunction | ProductMetadata | ProductSinglePage | string, config?: ProductChild[]): void {
|
|
138
|
+
if (typeof product === 'string') {
|
|
139
|
+
this.productConfigs.push(PluginProduct.fromName(this, product));
|
|
140
|
+
} else if (product?.name) {
|
|
141
|
+
if (!config) {
|
|
142
|
+
const p = product as ProductSinglePage;
|
|
143
|
+
|
|
144
|
+
this.productConfigs.push(new PluginProduct(this, p, []));
|
|
145
|
+
} else {
|
|
146
|
+
const p = product as ProductMetadata;
|
|
147
|
+
|
|
148
|
+
this.productConfigs.push(new PluginProduct(this, p, config));
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
this.products.push(product as ProductFunction);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
extendProduct(product: StandardProductName | string, config: ProductChild[] | ProductChild): void {
|
|
156
|
+
const arrayConfig = Array.isArray(config) ? config : [config];
|
|
157
|
+
|
|
158
|
+
this.productConfigs.push(new PluginProduct(this, product, arrayConfig));
|
|
124
159
|
}
|
|
125
160
|
|
|
126
161
|
addLocale(locale: string, label: string): void {
|
|
@@ -131,8 +166,8 @@ export class Plugin implements IPlugin {
|
|
|
131
166
|
this.register('l10n', locale, fn);
|
|
132
167
|
}
|
|
133
168
|
|
|
134
|
-
addRoutes(routes: PluginRouteRecordRaw[] | RouteRecordRaw[]) {
|
|
135
|
-
routes.forEach((r: PluginRouteRecordRaw | RouteRecordRaw) => {
|
|
169
|
+
addRoutes(routes: PluginRouteRecordRaw[] | RouteRecordRawWithParams[] | RouteRecordRaw[]) {
|
|
170
|
+
routes.forEach((r: PluginRouteRecordRaw | RouteRecordRawWithParams | RouteRecordRaw) => {
|
|
136
171
|
if (Object.keys(r).includes('parent')) {
|
|
137
172
|
const pConfig = r as PluginRouteRecordRaw;
|
|
138
173
|
|
|
@@ -142,16 +177,16 @@ export class Plugin implements IPlugin {
|
|
|
142
177
|
this.addRoute(pConfig.route);
|
|
143
178
|
}
|
|
144
179
|
} else {
|
|
145
|
-
this.addRoute(r as RouteRecordRaw);
|
|
180
|
+
this.addRoute(r as RouteRecordRaw | RouteRecordRawWithParams);
|
|
146
181
|
}
|
|
147
182
|
});
|
|
148
183
|
}
|
|
149
184
|
|
|
150
|
-
addRoute(parentOrRoute: RouteRecordRaw | string, optionalRoute?: RouteRecordRaw): void {
|
|
185
|
+
addRoute(parentOrRoute: RouteRecordRaw | RouteRecordRawWithParams | string, optionalRoute?: RouteRecordRaw | RouteRecordRawWithParams): void {
|
|
151
186
|
// Always add the pkg name to the route metadata
|
|
152
187
|
const hasParent = typeof (parentOrRoute) === 'string';
|
|
153
188
|
const parent: string | undefined = hasParent ? parentOrRoute as string : undefined;
|
|
154
|
-
const route: RouteRecordRaw = hasParent ? optionalRoute as RouteRecordRaw : parentOrRoute as RouteRecordRaw;
|
|
189
|
+
const route: RouteRecordRaw | RouteRecordRawWithParams = hasParent ? (optionalRoute as RouteRecordRaw | RouteRecordRawWithParams) : parentOrRoute as RouteRecordRaw | RouteRecordRawWithParams;
|
|
155
190
|
|
|
156
191
|
let parentOverride;
|
|
157
192
|
|
|
@@ -390,7 +425,7 @@ export class Plugin implements IPlugin {
|
|
|
390
425
|
const allowPaths = ['models', 'image'];
|
|
391
426
|
const nparts = name.split('/');
|
|
392
427
|
|
|
393
|
-
// Support components in a sub-folder - component_name/index.vue (and ignore other
|
|
428
|
+
// Support components in a sub-folder - component_name/index.vue (and ignore other components in that folder)
|
|
394
429
|
// Allow store-scoped models via sub-folder - pkgname/models/storename/type will be registered as storename/type to avoid overwriting shell/models/type
|
|
395
430
|
if (nparts.length === 2 && !allowPaths.includes(type)) {
|
|
396
431
|
if (nparts[1] !== 'index') {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
export function DSLRegistrationsPerProduct(store, prodName) {
|
|
3
|
+
const parsedData = {};
|
|
4
|
+
const typeMapData = store._state.data['type-map'];
|
|
5
|
+
|
|
6
|
+
const relevantKeys = [
|
|
7
|
+
'basicGroupWeights',
|
|
8
|
+
'basicTypeWeights',
|
|
9
|
+
'groupDefaultTypes',
|
|
10
|
+
'groupLabels',
|
|
11
|
+
// 'headers',
|
|
12
|
+
// 'spoofedTypes',
|
|
13
|
+
// 'typeOptions',
|
|
14
|
+
// 'typeWeights'
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
Object.keys(typeMapData).forEach((dataType) => {
|
|
18
|
+
// prod reg
|
|
19
|
+
if (dataType === 'products' && typeMapData[dataType].filter((item) => item.name === prodName)) {
|
|
20
|
+
parsedData[dataType] = typeMapData[dataType].filter((item) => item.name === prodName);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// prod configureType
|
|
24
|
+
if (dataType === 'typeOptions' && typeMapData[dataType].filter((item) => item.customRoute && item.customRoute.name.includes(prodName))) {
|
|
25
|
+
parsedData['configureType'] = typeMapData[dataType].filter((item) => item.customRoute && item.customRoute.name.includes(prodName));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// other types which map with prodName
|
|
29
|
+
if (typeMapData[dataType]?.[prodName]) {
|
|
30
|
+
parsedData[dataType] = typeMapData[dataType]?.[prodName];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// other relevant data
|
|
34
|
+
if (relevantKeys.includes(dataType)) {
|
|
35
|
+
parsedData[dataType] = typeMapData[dataType];
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.error('*** PRODUCT DATA DEBUGGER **** DSLRegistrationsPerProduct', parsedData); // eslint-disable-line no-console
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function registeredRoutes(store, prodName) {
|
|
43
|
+
const routes = store.$router.getRoutes();
|
|
44
|
+
|
|
45
|
+
const parsedData = routes.filter((route) => route.path.includes(prodName));
|
|
46
|
+
|
|
47
|
+
console.error('*** PRODUCT DATA DEBUGGER **** registeredRoutes', parsedData); // eslint-disable-line no-console
|
|
48
|
+
}
|
package/core/types.ts
CHANGED
|
@@ -2,6 +2,11 @@ import { ProductFunction } from './plugin';
|
|
|
2
2
|
import { RouteRecordRaw } from 'vue-router';
|
|
3
3
|
import type { ExtensionManager } from '@shell/types/extension-manager';
|
|
4
4
|
import { PaginationSettingsStores } from '@shell/types/resources/settings';
|
|
5
|
+
import type {
|
|
6
|
+
ProductMetadata, ProductSinglePage,
|
|
7
|
+
StandardProductName, RouteRecordRawWithParams, ProductChildGroup,
|
|
8
|
+
ProductChildPage
|
|
9
|
+
} from './plugin-types';
|
|
5
10
|
|
|
6
11
|
// Cluster Provisioning types
|
|
7
12
|
export * from './types-provisioning';
|
|
@@ -21,6 +26,8 @@ export interface PackageMetadata {
|
|
|
21
26
|
// children: Route[];
|
|
22
27
|
// }
|
|
23
28
|
|
|
29
|
+
export type PluginRouteRecordRaw = { [key: string]: any }
|
|
30
|
+
|
|
24
31
|
export type VuexStoreObject = { [key: string]: any }
|
|
25
32
|
export type CoreStoreSpecifics = { state: () => VuexStoreObject, getters: VuexStoreObject, mutations: VuexStoreObject, actions: VuexStoreObject }
|
|
26
33
|
export type CoreStoreConfig = { namespace: string, baseUrl?: string, modelBaseClass?: string, supportsStream?: boolean, isClusterStore?: boolean }
|
|
@@ -28,8 +35,6 @@ export type CoreStoreInit = (store: any, ctx: any) => void;
|
|
|
28
35
|
export type RegisterStore = () => (store: any) => void
|
|
29
36
|
export type UnregisterStore = (store: any) => void
|
|
30
37
|
|
|
31
|
-
export type PluginRouteRecordRaw = { [key: string]: any }
|
|
32
|
-
|
|
33
38
|
export type OnEnterLeavePackageConfig = {
|
|
34
39
|
clusterId: string,
|
|
35
40
|
product: string,
|
|
@@ -265,6 +270,11 @@ export interface ProductOptions {
|
|
|
265
270
|
*/
|
|
266
271
|
showClusterSwitcher?: boolean;
|
|
267
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Indicates whether UI Extensions can add pages to this product
|
|
275
|
+
*/
|
|
276
|
+
extendable?: boolean;
|
|
277
|
+
|
|
268
278
|
/**
|
|
269
279
|
* Show the namespace filter in the header
|
|
270
280
|
*/
|
|
@@ -290,6 +300,18 @@ export interface ProductOptions {
|
|
|
290
300
|
*/
|
|
291
301
|
name?: string;
|
|
292
302
|
|
|
303
|
+
/**
|
|
304
|
+
*
|
|
305
|
+
*/
|
|
306
|
+
label?: string;
|
|
307
|
+
|
|
308
|
+
labelKey?: string;
|
|
309
|
+
|
|
310
|
+
iconHeader?: string;
|
|
311
|
+
|
|
312
|
+
// Do not use - internal use only
|
|
313
|
+
version?: number;
|
|
314
|
+
|
|
293
315
|
/**
|
|
294
316
|
* Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
|
|
295
317
|
*/
|
|
@@ -368,6 +390,17 @@ export interface HeaderOptions {
|
|
|
368
390
|
*/
|
|
369
391
|
export type PaginationHeaderOptions = Omit<HeaderOptions, 'getValue'>
|
|
370
392
|
|
|
393
|
+
export type ResourceTypeConfig = {
|
|
394
|
+
options?: {
|
|
395
|
+
isCreatable?: boolean;
|
|
396
|
+
isEditable?: boolean;
|
|
397
|
+
},
|
|
398
|
+
listHeaders?: {
|
|
399
|
+
legacy?: HeaderOptions[];
|
|
400
|
+
paginated?: PaginationHeaderOptions[];
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
371
404
|
/**
|
|
372
405
|
* External extension configuration for @HeaderOptions
|
|
373
406
|
*/
|
|
@@ -489,9 +522,11 @@ export interface ConfigureVirtualTypeOptions extends ConfigureTypeOptions {
|
|
|
489
522
|
name: string;
|
|
490
523
|
|
|
491
524
|
/**
|
|
492
|
-
* The route that this type should correspond to {@link PluginRouteRecordRaw} {@link RouteRecordRaw}
|
|
525
|
+
* The route that this type should correspond to {@link PluginRouteRecordRaw} {@link RouteRecordRaw} {@link RouteRecordRawWithParams}
|
|
493
526
|
*/
|
|
494
|
-
route: PluginRouteRecordRaw | RouteRecordRaw | Object;
|
|
527
|
+
route: PluginRouteRecordRaw | RouteRecordRaw | RouteRecordRawWithParams | Object;
|
|
528
|
+
|
|
529
|
+
weight?: number;
|
|
495
530
|
}
|
|
496
531
|
|
|
497
532
|
export interface DSLReturnType {
|
|
@@ -501,7 +536,7 @@ export interface DSLReturnType {
|
|
|
501
536
|
* @param group Conditionally a group you want to places all the types in
|
|
502
537
|
* @returns {@link void}
|
|
503
538
|
*/
|
|
504
|
-
basicType: (types: string[], group?: string) => void;
|
|
539
|
+
basicType: (types: string[] | string, group?: string) => void;
|
|
505
540
|
|
|
506
541
|
/**
|
|
507
542
|
* Configure a myriad of options for the specified type
|
|
@@ -572,6 +607,10 @@ export interface DSLReturnType {
|
|
|
572
607
|
// moveType: (match, group)
|
|
573
608
|
// setGroupDefaultType: (input, defaultType)
|
|
574
609
|
// spoofedType: (obj)
|
|
610
|
+
|
|
611
|
+
labelGroup: (group: string, label: string | undefined, labelKey?: string) => void;
|
|
612
|
+
|
|
613
|
+
setGroupDefaultType: (group: string, defaultType: string) => void;
|
|
575
614
|
}
|
|
576
615
|
|
|
577
616
|
/**
|
|
@@ -610,15 +649,53 @@ export type ModelExtensionContext = {
|
|
|
610
649
|
export type ModelExtensionConstructor = (context: ModelExtensionContext) => Object;
|
|
611
650
|
|
|
612
651
|
/**
|
|
613
|
-
* Interface for a
|
|
652
|
+
* Interface for a UI Extension
|
|
614
653
|
*/
|
|
615
|
-
export interface
|
|
654
|
+
export interface IExtension {
|
|
655
|
+
/**
|
|
656
|
+
* Register a top-level product as a flag on the plugin
|
|
657
|
+
* @internal - DO NOT USE - Internal API only
|
|
658
|
+
*/
|
|
659
|
+
_registerTopLevelProduct(): void;
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Add a product to the sidebar, with children and a side menu for navigation for internal pages
|
|
663
|
+
* @param name
|
|
664
|
+
* @param config
|
|
665
|
+
*/
|
|
666
|
+
addProduct(product: ProductMetadata, config: ProductChildGroup[]): void;
|
|
667
|
+
addProduct(product: ProductMetadata, config: ProductChildPage[]): void;
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Add a product to the sidebar, without children (no side menu, single page only)
|
|
671
|
+
* @param product
|
|
672
|
+
*/
|
|
673
|
+
addProduct(product: ProductSinglePage): void;
|
|
674
|
+
|
|
616
675
|
/**
|
|
617
|
-
* Add a product
|
|
676
|
+
* Add a product with just a name (convenience/bridge method for quick setup).
|
|
677
|
+
* Creates a basic product with an empty page component automatically.
|
|
678
|
+
* This is useful for getting started quickly - expand to the full API once you're ready to add custom pages.
|
|
679
|
+
* @param productName Simple product name - will be used as both the name and label
|
|
680
|
+
*/
|
|
681
|
+
addProduct(productName: string): void;
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Add a product to the sidebar (deprecated, use other signatures of addProduct instead)
|
|
685
|
+
* @deprecated Use other `addProduct` signatures instead
|
|
618
686
|
* @param importFn Function that will import the module containing a product definition
|
|
619
687
|
*/
|
|
620
688
|
addProduct(importFn: ProductFunction): void;
|
|
621
689
|
|
|
690
|
+
/**
|
|
691
|
+
* Extend an existing product in Rancher, with children and a side menu for navigation for internal pages
|
|
692
|
+
*
|
|
693
|
+
* @param product Product to be extended
|
|
694
|
+
* @param config Product extension configuration
|
|
695
|
+
*/
|
|
696
|
+
extendProduct(product: StandardProductName | string, config: ProductChildGroup[]): void;
|
|
697
|
+
extendProduct(product: StandardProductName | string, config: ProductChildPage[]): void;
|
|
698
|
+
|
|
622
699
|
/**
|
|
623
700
|
* Add a locale to the i18n store
|
|
624
701
|
* @param locale Locale id (e.g. en-us)
|
|
@@ -644,8 +721,8 @@ export interface IPlugin {
|
|
|
644
721
|
/**
|
|
645
722
|
* Add a route to the Vue Router
|
|
646
723
|
*/
|
|
647
|
-
addRoute(route: RouteRecordRaw): void;
|
|
648
|
-
addRoute(parent: string, route: RouteRecordRaw): void;
|
|
724
|
+
addRoute(route: RouteRecordRawWithParams | RouteRecordRaw): void;
|
|
725
|
+
addRoute(parent: string, route: RouteRecordRawWithParams | RouteRecordRaw): void;
|
|
649
726
|
|
|
650
727
|
/**
|
|
651
728
|
* Adds an action/button to the UI
|
|
@@ -698,7 +775,7 @@ export interface IPlugin {
|
|
|
698
775
|
/**
|
|
699
776
|
* Add routes to the Vue Router
|
|
700
777
|
*/
|
|
701
|
-
addRoutes(routes: PluginRouteRecordRaw[] | RouteRecordRaw[]): void;
|
|
778
|
+
addRoutes(routes: PluginRouteRecordRaw[] | RouteRecordRawWithParams[] | RouteRecordRaw[]): void;
|
|
702
779
|
|
|
703
780
|
/**
|
|
704
781
|
* Add a hook to be called when the plugin is uninstalled
|
|
@@ -755,6 +832,7 @@ export interface IPlugin {
|
|
|
755
832
|
|
|
756
833
|
/**
|
|
757
834
|
* Will return all of the configuration functions used for creating a new product.
|
|
835
|
+
* @deprecated Should use `addProduct` and `extendProduct` instead and avoid using this directly
|
|
758
836
|
* @param store The store that was passed to the function that's passed to `plugin.addProduct(function)`
|
|
759
837
|
* @param productName The name of the new product. This name is displayed in the navigation.
|
|
760
838
|
*/
|
|
@@ -766,6 +844,12 @@ export interface IPlugin {
|
|
|
766
844
|
get environment(): ExtensionEnvironment;
|
|
767
845
|
}
|
|
768
846
|
|
|
847
|
+
/**
|
|
848
|
+
* Legacy interface for a plugin, which is just an extension but with the `DSL` function.
|
|
849
|
+
* @deprecated Should use `IExtension` interface instead
|
|
850
|
+
*/
|
|
851
|
+
export type IPlugin = IExtension;
|
|
852
|
+
|
|
769
853
|
// Internal interface
|
|
770
854
|
// Built-in extensions may use this, but external extensions should not, as this is subject to change
|
|
771
855
|
// Defined as any for now
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`view: fleet.cattle.io.bundle should render resources when bundle deployments exist 1`] = `
|
|
4
|
+
<div class="tabbed-container resource-tabs view" data-testid="tabbed">
|
|
5
|
+
<ul role="tablist" class="tabs clearfix horizontal" data-testid="tabbed-block" tabindex="0">
|
|
6
|
+
<!-- This is the tabs link... tabs appear here because they are injected from the "Tab" component -->
|
|
7
|
+
<li id="resources" data-testid="resources" class="tab active"><a id="tab-resources" data-testid="btn-resources" aria-controls="resources" aria-selected="true" aria-label="Resources" role="tab" tabindex="0">
|
|
8
|
+
<!--v-if--><span>Resources</span>
|
|
9
|
+
<!--v-if-->
|
|
10
|
+
<!--v-if-->
|
|
11
|
+
</a></li>
|
|
12
|
+
<!--v-if-->
|
|
13
|
+
<!--v-if-->
|
|
14
|
+
</ul>
|
|
15
|
+
<div class="tab-container">
|
|
16
|
+
<!-- This is where "normal" tab content goes... -->
|
|
17
|
+
<section id="resources" aria-hidden="false" role="tabpanel" aria-labelledby="tab-resources" style="">
|
|
18
|
+
<!--v-if-->
|
|
19
|
+
<resource-table-stub rows="[object Object],[object Object]" loading="false" altloading="false" keyfield="tableKey" headers="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]" search="true" tableactions="false" paginglabel="sortableTable.paging.resource" rowactions="false" groupoptions="[object Object],[object Object]" groupdefault="namespace" grouptooltip="resourceTable.groupBy.namespace" overflowx="false" overflowy="false" ignorefilter="false" hasadvancedfiltering="false" advfilterhidelabelsascols="false" advfilterpreventfilteringlabels="false" usequeryparamsforsimplefiltering="false" forceupdateliveanddelayed="0" externalpaginationenabled="false" default-sort-by="state"></resource-table-stub>
|
|
20
|
+
</section>
|
|
21
|
+
<!--v-if-->
|
|
22
|
+
<!--v-if-->
|
|
23
|
+
<!--v-if-->
|
|
24
|
+
<!-- Extension tabs content goes here... -->
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
exports[`view: fleet.cattle.io.bundle should render the bundle detail page 1`] = `<resource-tabs-stub mode="view" needconditions="true" needevents="true" needrelated="false" usehash="true" value="[object Object]"></resource-tabs-stub>`;
|
|
30
|
+
|
|
31
|
+
exports[`view: fleet.cattle.io.bundle should render the bundle detail page with full mount 1`] = `
|
|
32
|
+
<div class="tabbed-container resource-tabs view" data-testid="tabbed">
|
|
33
|
+
<ul role="tablist" class="tabs clearfix horizontal" data-testid="tabbed-block" tabindex="0">
|
|
34
|
+
<!-- This is the tabs link... tabs appear here because they are injected from the "Tab" component -->
|
|
35
|
+
<!--v-if-->
|
|
36
|
+
<!--v-if-->
|
|
37
|
+
</ul>
|
|
38
|
+
<div class="">
|
|
39
|
+
<!-- This is where "normal" tab content goes... -->
|
|
40
|
+
<section id="resources" aria-hidden="true" role="tabpanel" aria-labelledby="tab-resources" style="display: none;">
|
|
41
|
+
<!--v-if-->
|
|
42
|
+
<resource-table-stub rows="" loading="false" altloading="false" keyfield="tableKey" headers="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]" search="true" tableactions="false" paginglabel="sortableTable.paging.resource" rowactions="false" groupoptions="[object Object],[object Object]" groupdefault="namespace" grouptooltip="resourceTable.groupBy.namespace" overflowx="false" overflowy="false" ignorefilter="false" hasadvancedfiltering="false" advfilterhidelabelsascols="false" advfilterpreventfilteringlabels="false" usequeryparamsforsimplefiltering="false" forceupdateliveanddelayed="0" externalpaginationenabled="false" default-sort-by="state"></resource-table-stub>
|
|
43
|
+
</section>
|
|
44
|
+
<!--v-if-->
|
|
45
|
+
<!--v-if-->
|
|
46
|
+
<!--v-if-->
|
|
47
|
+
<!-- Extension tabs content goes here... -->
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
exports[`view: fleet.cattle.io.bundle should show Loading when fetch is pending 1`] = `<loading-stub loading="true" mode="content" nodelay="false"></loading-stub>`;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { shallowMount, mount } from '@vue/test-utils';
|
|
2
|
+
import BundleDetail from '@shell/detail/fleet.cattle.io.bundle.vue';
|
|
3
|
+
|
|
4
|
+
describe('view: fleet.cattle.io.bundle', () => {
|
|
5
|
+
const mockStore = {
|
|
6
|
+
getters: {
|
|
7
|
+
'i18n/t': (text: string) => text,
|
|
8
|
+
'i18n/exists': jest.fn(),
|
|
9
|
+
currentStore: () => 'management',
|
|
10
|
+
'management/schemaFor': jest.fn(),
|
|
11
|
+
'management/all': () => [],
|
|
12
|
+
'management/pathExistsInSchema': () => false,
|
|
13
|
+
'type-map/optionsFor': () => ({}),
|
|
14
|
+
'type-map/headersFor': () => [],
|
|
15
|
+
'cluster/schemaFor': jest.fn(),
|
|
16
|
+
'resource-fetch/refreshFlag': () => false,
|
|
17
|
+
},
|
|
18
|
+
dispatch: jest.fn(),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const mockValue = {
|
|
22
|
+
id: 'fleet-default/test-bundle',
|
|
23
|
+
type: 'fleet.cattle.io.bundle',
|
|
24
|
+
metadata: {
|
|
25
|
+
name: 'test-bundle',
|
|
26
|
+
namespace: 'fleet-default',
|
|
27
|
+
labels: {},
|
|
28
|
+
},
|
|
29
|
+
spec: { targets: [] },
|
|
30
|
+
status: { conditions: [] },
|
|
31
|
+
targetClusters: [],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const mockValueWithResources = {
|
|
35
|
+
id: 'fleet-default/test-bundle',
|
|
36
|
+
type: 'fleet.cattle.io.bundle',
|
|
37
|
+
metadata: {
|
|
38
|
+
name: 'test-bundle',
|
|
39
|
+
namespace: 'fleet-default',
|
|
40
|
+
labels: {},
|
|
41
|
+
},
|
|
42
|
+
spec: { targets: [] },
|
|
43
|
+
status: {
|
|
44
|
+
conditions: [
|
|
45
|
+
{
|
|
46
|
+
type: 'Ready',
|
|
47
|
+
status: 'True',
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
targetClusters: [
|
|
52
|
+
{
|
|
53
|
+
id: 'fleet-default/cluster-1',
|
|
54
|
+
nameDisplay: 'cluster-1',
|
|
55
|
+
metadata: { labels: { 'management.cattle.io/cluster-name': 'c-m-abc123' } },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const mockBundleDeployments = [
|
|
61
|
+
{
|
|
62
|
+
metadata: {
|
|
63
|
+
labels: {
|
|
64
|
+
'fleet.cattle.io/bundle-namespace': 'fleet-default',
|
|
65
|
+
'fleet.cattle.io/bundle-name': 'test-bundle',
|
|
66
|
+
'fleet.cattle.io/cluster-namespace': 'fleet-default',
|
|
67
|
+
'fleet.cattle.io/cluster': 'cluster-1',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
status: {
|
|
71
|
+
resources: [
|
|
72
|
+
{
|
|
73
|
+
kind: 'Deployment',
|
|
74
|
+
apiVersion: 'apps/v1',
|
|
75
|
+
namespace: 'default',
|
|
76
|
+
name: 'nginx',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
kind: 'Service',
|
|
80
|
+
apiVersion: 'v1',
|
|
81
|
+
namespace: 'default',
|
|
82
|
+
name: 'nginx-svc',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const fullMountGlobal = {
|
|
90
|
+
mocks: {
|
|
91
|
+
$store: mockStore,
|
|
92
|
+
$fetchState: { pending: false },
|
|
93
|
+
$route: { query: {}, hash: '' },
|
|
94
|
+
$router: {
|
|
95
|
+
applyQuery: jest.fn(),
|
|
96
|
+
replace: jest.fn(),
|
|
97
|
+
currentRoute: { _value: { hash: '' } },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
stubs: {
|
|
101
|
+
ResourceTable: true,
|
|
102
|
+
teleport: true,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const createWrapper = (props = {}) => {
|
|
107
|
+
return shallowMount(BundleDetail, {
|
|
108
|
+
props: {
|
|
109
|
+
value: mockValue,
|
|
110
|
+
...props,
|
|
111
|
+
},
|
|
112
|
+
global: {
|
|
113
|
+
mocks: {
|
|
114
|
+
$store: mockStore,
|
|
115
|
+
$fetchState: { pending: false },
|
|
116
|
+
$route: { query: {} },
|
|
117
|
+
$router: { applyQuery: jest.fn() },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
it('should render the bundle detail page', () => {
|
|
124
|
+
const wrapper = createWrapper();
|
|
125
|
+
|
|
126
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should render the bundle detail page with full mount', () => {
|
|
130
|
+
const wrapper = mount(BundleDetail, {
|
|
131
|
+
props: { value: mockValue },
|
|
132
|
+
global: fullMountGlobal,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should show Loading when fetch is pending', () => {
|
|
139
|
+
const wrapper = shallowMount(BundleDetail, {
|
|
140
|
+
props: { value: mockValue },
|
|
141
|
+
global: {
|
|
142
|
+
mocks: {
|
|
143
|
+
$store: mockStore,
|
|
144
|
+
$fetchState: { pending: true },
|
|
145
|
+
$route: { query: {} },
|
|
146
|
+
$router: { applyQuery: jest.fn() },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should render resources when bundle deployments exist', async() => {
|
|
155
|
+
const wrapper = mount(BundleDetail, {
|
|
156
|
+
props: { value: mockValueWithResources },
|
|
157
|
+
global: fullMountGlobal,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Simulate fetch completion by setting allBundleDeployments
|
|
161
|
+
(wrapper.vm as any).allBundleDeployments = mockBundleDeployments;
|
|
162
|
+
await wrapper.vm.$nextTick();
|
|
163
|
+
|
|
164
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
165
|
+
|
|
166
|
+
// Verify computed bundleResources are populated
|
|
167
|
+
expect((wrapper.vm as any).bundleResources).toHaveLength(2);
|
|
168
|
+
expect((wrapper.vm as any).bundleResources[0].name).toBe('nginx');
|
|
169
|
+
expect((wrapper.vm as any).bundleResources[1].name).toBe('nginx-svc');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import node from '@shell/detail/node.vue';
|
|
3
|
+
import ConsumptionGauge from '@shell/components/ConsumptionGauge.vue';
|
|
4
|
+
|
|
5
|
+
describe('view: node detail', () => {
|
|
6
|
+
const mockStore = {
|
|
7
|
+
getters: {
|
|
8
|
+
'cluster/schemaFor': () => undefined,
|
|
9
|
+
'cluster/paginationEnabled': () => false,
|
|
10
|
+
'type-map/headersFor': jest.fn(),
|
|
11
|
+
'i18n/t': (key: string) => key,
|
|
12
|
+
currentCluster: { id: 'local' },
|
|
13
|
+
},
|
|
14
|
+
dispatch: jest.fn(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const mocks = {
|
|
18
|
+
$store: mockStore,
|
|
19
|
+
$fetchState: { pending: false },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const defaultNodeValue = {
|
|
23
|
+
metadata: { name: 'test-node' },
|
|
24
|
+
status: {
|
|
25
|
+
nodeInfo: {}, images: [], conditions: []
|
|
26
|
+
},
|
|
27
|
+
spec: { taints: [] },
|
|
28
|
+
pods: [],
|
|
29
|
+
cpuCapacity: 4,
|
|
30
|
+
cpuUsage: 2,
|
|
31
|
+
ramReserved: 8000,
|
|
32
|
+
ramUsage: 4000,
|
|
33
|
+
podCapacity: 110,
|
|
34
|
+
podConsumed: 5,
|
|
35
|
+
isPidPressureOk: true,
|
|
36
|
+
isDiskPressureOk: true,
|
|
37
|
+
isMemoryPressureOk: true,
|
|
38
|
+
isKubeletOk: true,
|
|
39
|
+
internalIp: '10.0.0.1',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function createWrapper(nodeOverrides = {}) {
|
|
43
|
+
return shallowMount(node, {
|
|
44
|
+
props: { value: { ...defaultNodeValue, ...nodeOverrides } },
|
|
45
|
+
global: { mocks },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function findGaugeByResource(wrapper: ReturnType<typeof createWrapper>, resourceKey: string) {
|
|
50
|
+
const gauges = wrapper.findAllComponents(ConsumptionGauge);
|
|
51
|
+
|
|
52
|
+
return gauges.find(
|
|
53
|
+
(g) => g.attributes('resourcename')?.includes(resourceKey)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
it('should pass the "running" translation key as usedlabel to the pods ConsumptionGauge', () => {
|
|
58
|
+
const wrapper = createWrapper();
|
|
59
|
+
|
|
60
|
+
const podsGauge = findGaugeByResource(wrapper, 'consumptionGauge.pods');
|
|
61
|
+
|
|
62
|
+
expect(podsGauge).toBeDefined();
|
|
63
|
+
expect(podsGauge!.attributes('usedlabel')).toContain('consumptionGauge.running');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should NOT pass a usedlabel to the CPU ConsumptionGauge', () => {
|
|
67
|
+
const wrapper = createWrapper();
|
|
68
|
+
|
|
69
|
+
const cpuGauge = findGaugeByResource(wrapper, 'consumptionGauge.cpu');
|
|
70
|
+
|
|
71
|
+
expect(cpuGauge).toBeDefined();
|
|
72
|
+
expect(cpuGauge!.attributes('usedlabel')).toBeUndefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should NOT pass a usedlabel to the Memory ConsumptionGauge', () => {
|
|
76
|
+
const wrapper = createWrapper();
|
|
77
|
+
|
|
78
|
+
const memoryGauge = findGaugeByResource(wrapper, 'consumptionGauge.memory');
|
|
79
|
+
|
|
80
|
+
expect(memoryGauge).toBeDefined();
|
|
81
|
+
expect(memoryGauge!.attributes('usedlabel')).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
});
|