@rancher/shell 3.0.12-rc.5 → 3.0.12-rc.6
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/apis/intf/resources-api/resource-base.ts +42 -2
- package/apis/intf/resources-api/resource-instance.ts +101 -0
- package/apis/intf/resources-api/resources-api.ts +177 -23
- package/apis/intf/resources.ts +8 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
- package/apis/resources/resources-api-class.ts +232 -43
- package/assets/images/providers/traefik.png +0 -0
- package/assets/translations/en-us.yaml +10 -4
- package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
- package/chart/rancher-monitoring-dashboards/index.vue +155 -0
- package/cloud-credential/__tests__/generic.test.ts +132 -0
- package/cloud-credential/generic.vue +33 -3
- package/components/EtcdInfoBanner.vue +2 -12
- package/components/GrafanaDashboard.vue +2 -8
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
- package/components/Resource/Detail/Masthead/index.vue +8 -3
- package/components/form/Security.vue +1 -1
- package/components/formatter/Translate.vue +22 -1
- package/components/nav/Group.vue +5 -10
- package/components/nav/TopLevelMenu.vue +1 -30
- package/components/nav/__tests__/Group.test.ts +61 -0
- package/config/labels-annotations.js +1 -0
- package/config/table-headers.js +5 -2
- package/core/__tests__/plugin-products-apply.test.ts +787 -0
- package/core/__tests__/plugin-products-extend.test.ts +189 -0
- package/core/__tests__/plugin-products-helpers.test.ts +81 -78
- package/core/__tests__/plugin-products-new.test.ts +389 -0
- package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
- package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
- package/core/plugin-products-base.ts +201 -93
- package/core/plugin-products-extending.ts +1 -1
- package/core/plugin-products-external.ts +380 -0
- package/core/plugin-products-helpers.ts +28 -24
- package/core/plugin-products-internal.ts +207 -0
- package/core/plugin-products-top-level.ts +10 -5
- package/core/plugin-products-type-guards.ts +17 -6
- package/core/plugin-products.ts +5 -7
- package/core/plugin-types.ts +1 -389
- package/core/plugin.ts +10 -11
- package/core/types.ts +6 -202
- package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +0 -2
- package/edit/auth/AuthProviderWarningBanners.vue +12 -0
- package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
- package/edit/auth/__tests__/azuread.test.ts +1 -0
- package/edit/auth/__tests__/github.test.ts +1 -0
- package/edit/auth/__tests__/oidc.test.ts +1 -0
- package/edit/auth/__tests__/saml.test.ts +1 -0
- package/edit/auth/ldap/__tests__/index.test.ts +1 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
- package/list/management.cattle.io.user.vue +24 -5
- package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
- package/machine-config/__tests__/generic.test.ts +163 -0
- package/machine-config/components/EC2Networking.vue +31 -7
- package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
- package/machine-config/generic.vue +32 -5
- package/mixins/resource-fetch.js +1 -1
- package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
- package/models/pod.js +12 -10
- package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
- package/package.json +1 -1
- package/pages/auth/login.vue +1 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +110 -0
- package/pages/c/_cluster/apps/charts/install.vue +29 -0
- package/pages/c/_cluster/auth/config/index.vue +11 -4
- package/pages/c/_cluster/monitoring/index.vue +6 -1
- package/plugins/dashboard-store/resource-class.js +10 -3
- package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
- package/plugins/steve/steve-class.js +43 -0
- package/plugins/steve/subscribe.js +11 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
- package/store/__tests__/auth.test.ts +18 -0
- package/store/__tests__/notifications.test.ts +466 -1
- package/store/__tests__/ui-context.test.ts +255 -0
- package/store/auth.js +11 -1
- package/store/features.js +1 -0
- package/store/plugins.js +6 -0
- package/types/rancher/steve.api.ts +2 -2
- package/types/shell/index.d.ts +58 -0
- package/types/store/dashboard-store.types.ts +2 -2
- package/types/store/type-map.ts +262 -1
- package/utils/async.ts +2 -0
- package/utils/grafana.js +2 -17
- package/utils/monitoring.js +38 -1
- package/core/__tests__/plugin-products.test.ts +0 -4694
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { PluginProduct } from '@shell/core/plugin-products';
|
|
2
|
+
import { IExtension } from '@shell/core/types';
|
|
3
|
+
import { ProductChildPage, StandardProductNames } from '@shell/core/plugin-products-external';
|
|
4
|
+
|
|
5
|
+
// Mock the helper functions
|
|
6
|
+
jest.mock('@shell/core/plugin-products-helpers', () => ({
|
|
7
|
+
gatherChildrenOrdering: jest.fn((config) => config),
|
|
8
|
+
generateTopLevelExtensionSimpleBaseRoute: jest.fn((name, opts) => ({
|
|
9
|
+
name: `${ name }-simple`,
|
|
10
|
+
path: opts?.omitPath ? '' : `/${ name }`,
|
|
11
|
+
component: opts?.component,
|
|
12
|
+
})),
|
|
13
|
+
generateVirtualTypeRoute: jest.fn((parentName, childName, opts) => ({
|
|
14
|
+
name: childName ? `${ parentName }-${ childName }` : `${ parentName }-group`,
|
|
15
|
+
path: opts?.omitPath ? '' : `/${ parentName }/${ childName || 'group' }`,
|
|
16
|
+
component: opts?.component,
|
|
17
|
+
})),
|
|
18
|
+
generateConfigureTypeRoute: jest.fn((parentName, page, opts) => {
|
|
19
|
+
const routeName = opts?.extendProduct ? `c-cluster-${ parentName }-resource` : `${ parentName }-c-cluster-resource`;
|
|
20
|
+
const routePath = opts?.extendProduct ? `c/:cluster/${ parentName }/:resource` : `${ parentName }/c/:cluster/:resource`;
|
|
21
|
+
const cluster = opts?.extendProduct ? undefined : '__BLANK_CLUSTER__';
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
name: routeName,
|
|
25
|
+
path: opts?.omitPath ? '' : routePath,
|
|
26
|
+
params: cluster ? {
|
|
27
|
+
product: parentName.replace(/-/g, ''),
|
|
28
|
+
cluster,
|
|
29
|
+
resource: page?.type,
|
|
30
|
+
} : {
|
|
31
|
+
product: parentName,
|
|
32
|
+
resource: page?.type,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
generateResourceRoutes: jest.fn((parentName, child) => [
|
|
37
|
+
{
|
|
38
|
+
name: `${ parentName }-${ child.type }-list`,
|
|
39
|
+
path: `/${ parentName }/${ child.type }`,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: `${ parentName }-${ child.type }-detail`,
|
|
43
|
+
path: `/${ parentName }/${ child.type }/:id`,
|
|
44
|
+
},
|
|
45
|
+
]),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
jest.mock('@shell/core/productDebugger', () => ({
|
|
49
|
+
DSLRegistrationsPerProduct: jest.fn(),
|
|
50
|
+
registeredRoutes: jest.fn(),
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
// Create mock factories
|
|
54
|
+
function createMockPlugin(): IExtension {
|
|
55
|
+
return {
|
|
56
|
+
_registerTopLevelProduct: jest.fn(),
|
|
57
|
+
addRoute: jest.fn(),
|
|
58
|
+
enableServerSidePagination: jest.fn(),
|
|
59
|
+
DSL: jest.fn(),
|
|
60
|
+
} as any as IExtension;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createMockStore(extendableProducts: string[] = Object.values(StandardProductNames), managementSchemas: string[] = []): any {
|
|
64
|
+
return {
|
|
65
|
+
getters: {
|
|
66
|
+
'type-map/productByName': (productName: string) => (extendableProducts.includes(productName) ? { name: productName, extendable: true } : undefined),
|
|
67
|
+
'management/schemaFor': (type: string) => (managementSchemas.includes(type) ? { id: type } : undefined),
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('pluginProduct', () => {
|
|
73
|
+
describe('extending standard product', () => {
|
|
74
|
+
it('should extend an existing standard product with valid name', () => {
|
|
75
|
+
const mockPlugin = createMockPlugin();
|
|
76
|
+
const config: ProductChildPage[] = [
|
|
77
|
+
{
|
|
78
|
+
name: 'custom-section',
|
|
79
|
+
label: 'Custom',
|
|
80
|
+
component: { name: 'CustomComponent' },
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const validStandardProduct = StandardProductNames.EXPLORER;
|
|
85
|
+
|
|
86
|
+
const pluginProduct = new PluginProduct(mockPlugin, validStandardProduct, config);
|
|
87
|
+
|
|
88
|
+
expect(pluginProduct.newProduct).toBe(false);
|
|
89
|
+
expect(mockPlugin.addRoute).toHaveBeenCalledTimes(1);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should accept any string as product name when extending', () => {
|
|
93
|
+
const mockPlugin = createMockPlugin();
|
|
94
|
+
const customProduct = 'custom-product';
|
|
95
|
+
|
|
96
|
+
const pluginProduct = new PluginProduct(mockPlugin, customProduct, []);
|
|
97
|
+
|
|
98
|
+
expect(pluginProduct.newProduct).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should throw error during apply when extending a product that is not registered', () => {
|
|
102
|
+
const mockPlugin = createMockPlugin();
|
|
103
|
+
const mockStore = createMockStore([]);
|
|
104
|
+
const mockDSL = {
|
|
105
|
+
product: jest.fn(),
|
|
106
|
+
basicType: jest.fn(),
|
|
107
|
+
labelGroup: jest.fn(),
|
|
108
|
+
setGroupDefaultType: jest.fn(),
|
|
109
|
+
weightGroup: jest.fn(),
|
|
110
|
+
virtualType: jest.fn(),
|
|
111
|
+
configureType: jest.fn(),
|
|
112
|
+
weightType: jest.fn(),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
116
|
+
|
|
117
|
+
const pluginProduct = new PluginProduct(mockPlugin, 'non-existent-product', []);
|
|
118
|
+
|
|
119
|
+
expect(() => {
|
|
120
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
121
|
+
}).toThrow('is not extendable');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should apply successfully when extending an extendable product', () => {
|
|
125
|
+
const mockPlugin = createMockPlugin();
|
|
126
|
+
const mockStore = createMockStore(['my-custom-builtin-product']);
|
|
127
|
+
const mockDSL = {
|
|
128
|
+
product: jest.fn(),
|
|
129
|
+
basicType: jest.fn(),
|
|
130
|
+
labelGroup: jest.fn(),
|
|
131
|
+
setGroupDefaultType: jest.fn(),
|
|
132
|
+
weightGroup: jest.fn(),
|
|
133
|
+
virtualType: jest.fn(),
|
|
134
|
+
configureType: jest.fn(),
|
|
135
|
+
weightType: jest.fn(),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
139
|
+
|
|
140
|
+
const pluginProduct = new PluginProduct(mockPlugin, 'my-custom-builtin-product', []);
|
|
141
|
+
|
|
142
|
+
expect(() => {
|
|
143
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
144
|
+
}).not.toThrow();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should throw error during apply when extending a registered product that is not extendable', () => {
|
|
148
|
+
const mockPlugin = createMockPlugin();
|
|
149
|
+
const mockStore = {
|
|
150
|
+
getters: {
|
|
151
|
+
'type-map/productByName': (productName: string) => {
|
|
152
|
+
if (productName === 'other-extension-product') {
|
|
153
|
+
return { name: productName, extendable: false };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return undefined;
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const mockDSL = {
|
|
161
|
+
product: jest.fn(),
|
|
162
|
+
basicType: jest.fn(),
|
|
163
|
+
labelGroup: jest.fn(),
|
|
164
|
+
setGroupDefaultType: jest.fn(),
|
|
165
|
+
weightGroup: jest.fn(),
|
|
166
|
+
virtualType: jest.fn(),
|
|
167
|
+
configureType: jest.fn(),
|
|
168
|
+
weightType: jest.fn(),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
(mockPlugin.DSL as jest.Mock).mockReturnValue(mockDSL);
|
|
172
|
+
|
|
173
|
+
const pluginProduct = new PluginProduct(mockPlugin, 'other-extension-product', []);
|
|
174
|
+
|
|
175
|
+
expect(() => {
|
|
176
|
+
pluginProduct.apply(mockPlugin, mockStore);
|
|
177
|
+
}).toThrow('is not extendable');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should not register new product when extending standard product', () => {
|
|
181
|
+
const mockPlugin = createMockPlugin();
|
|
182
|
+
const validStandardProduct = StandardProductNames.EXPLORER;
|
|
183
|
+
|
|
184
|
+
new PluginProduct(mockPlugin, validStandardProduct, []);
|
|
185
|
+
|
|
186
|
+
expect(mockPlugin._registerTopLevelProduct).not.toHaveBeenCalled();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductChild, ProductChildGroup, ProductChildCustomPage, ProductChildResourcePage, ProductChildPage
|
|
3
|
+
} from '@shell/core/plugin-products-external';
|
|
1
4
|
import pluginProductsHelpers from '@shell/core/plugin-products-helpers';
|
|
2
5
|
import { BLANK_CLUSTER } from '@shell/store/store-types';
|
|
3
|
-
import { ProductChildPage, ProductChildGroup } from '@shell/core/plugin-types';
|
|
4
6
|
|
|
5
7
|
const gatherChildrenOrdering = pluginProductsHelpers.gatherChildrenOrdering.bind(pluginProductsHelpers);
|
|
6
8
|
const generateTopLevelExtensionSimpleBaseRoute = pluginProductsHelpers.generateTopLevelExtensionSimpleBaseRoute.bind(pluginProductsHelpers);
|
|
@@ -12,23 +14,23 @@ describe('plugin-products-helpers', () => {
|
|
|
12
14
|
// ============= gatherChildrenOrdering tests =============
|
|
13
15
|
describe('gatherChildrenOrdering', () => {
|
|
14
16
|
it('should sort children by weight descending', () => {
|
|
15
|
-
const children = [
|
|
17
|
+
const children: ProductChildCustomPage[] = [
|
|
16
18
|
{
|
|
17
|
-
name: 'a', label: 'A',
|
|
19
|
+
name: 'a', label: 'A', component: {}, sideMenu: { weight: 10 },
|
|
18
20
|
},
|
|
19
21
|
{
|
|
20
|
-
name: 'b', label: 'B',
|
|
22
|
+
name: 'b', label: 'B', component: {}, sideMenu: { weight: 30 },
|
|
21
23
|
},
|
|
22
24
|
{
|
|
23
|
-
name: 'c', label: 'C',
|
|
25
|
+
name: 'c', label: 'C', component: {}, sideMenu: { weight: 20 },
|
|
24
26
|
},
|
|
25
27
|
];
|
|
26
28
|
|
|
27
29
|
const result = gatherChildrenOrdering(children);
|
|
28
30
|
|
|
29
|
-
expect(result[0].
|
|
30
|
-
expect(result[1].
|
|
31
|
-
expect(result[2].
|
|
31
|
+
expect(result[0].label).toBe('B'); // 30
|
|
32
|
+
expect(result[1].label).toBe('C'); // 20
|
|
33
|
+
expect(result[2].label).toBe('A'); // 10
|
|
32
34
|
// Verify original array is not mutated
|
|
33
35
|
expect(children[0].name).toBe('a');
|
|
34
36
|
expect(children[1].name).toBe('b');
|
|
@@ -36,35 +38,35 @@ describe('plugin-products-helpers', () => {
|
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
it('should assign weights to items missing them', () => {
|
|
39
|
-
const children = [
|
|
41
|
+
const children: ProductChild[] = [
|
|
40
42
|
{
|
|
41
|
-
name: 'a', label: 'A',
|
|
43
|
+
name: 'a', label: 'A', component: {}, sideMenu: { weight: 50 },
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
|
-
name: 'b', label: 'B',
|
|
46
|
+
name: 'b', label: 'B', component: {}
|
|
45
47
|
},
|
|
46
48
|
{
|
|
47
|
-
name: 'c', label: 'C',
|
|
49
|
+
name: 'c', label: 'C', component: {}
|
|
48
50
|
},
|
|
49
51
|
];
|
|
50
52
|
|
|
51
53
|
const result = gatherChildrenOrdering(children);
|
|
52
54
|
|
|
53
|
-
expect(result[0].weight).toBe(50);
|
|
54
|
-
expect(result[1].weight
|
|
55
|
-
expect(result[2].weight
|
|
55
|
+
expect(result[0].sideMenu?.weight).toBe(50);
|
|
56
|
+
expect(result[1].sideMenu?.weight).toBeLessThan(50); // 49
|
|
57
|
+
expect(result[2].sideMenu?.weight).toBeLessThan(result[1].sideMenu?.weight); // 48
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
it('should use 999 as minWeight when no explicit weights are provided', () => {
|
|
59
|
-
const children = [
|
|
61
|
+
const children: ProductChildPage[] = [
|
|
60
62
|
{
|
|
61
|
-
name: 'a', label: 'A',
|
|
63
|
+
name: 'a', label: 'A', component: {}
|
|
62
64
|
},
|
|
63
65
|
{
|
|
64
|
-
name: 'b', label: 'B',
|
|
66
|
+
name: 'b', label: 'B', component: {}
|
|
65
67
|
},
|
|
66
68
|
{
|
|
67
|
-
name: 'c', label: 'C',
|
|
69
|
+
name: 'c', label: 'C', component: {}
|
|
68
70
|
},
|
|
69
71
|
];
|
|
70
72
|
|
|
@@ -72,36 +74,38 @@ describe('plugin-products-helpers', () => {
|
|
|
72
74
|
|
|
73
75
|
// minWeight starts at 999, then each item gets minWeight - (index + 1)
|
|
74
76
|
// so: 999 - 1 = 998, 999 - 2 = 997, 999 - 3 = 996
|
|
75
|
-
expect(result[0].weight).toBe(998);
|
|
76
|
-
expect(result[1].weight).toBe(997);
|
|
77
|
-
expect(result[2].weight).toBe(996);
|
|
77
|
+
expect(result[0].sideMenu?.weight).toBe(998);
|
|
78
|
+
expect(result[1].sideMenu?.weight).toBe(997);
|
|
79
|
+
expect(result[2].sideMenu?.weight).toBe(996);
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
it('should recursively apply ordering to nested children', () => {
|
|
81
|
-
const children = [
|
|
83
|
+
const children: ProductChildGroup[] = [
|
|
82
84
|
{
|
|
83
85
|
name: 'group',
|
|
84
86
|
label: 'Group',
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
sideMenu: {
|
|
88
|
+
weight: 50,
|
|
89
|
+
children: [
|
|
90
|
+
{
|
|
91
|
+
name: 'nested-a', label: 'Nested A', component: {}, sideMenu: { weight: 20 }
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'nested-b', label: 'Nested B', component: {}, sideMenu: { weight: 10 }
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'nested-c', label: 'Nested C', component: {}
|
|
98
|
+
}, // no weight
|
|
99
|
+
],
|
|
100
|
+
}
|
|
97
101
|
},
|
|
98
102
|
];
|
|
99
103
|
|
|
100
104
|
const result = gatherChildrenOrdering(children);
|
|
101
105
|
|
|
102
|
-
expect(result[0].children[0].name).toBe('nested-a'); // 20
|
|
103
|
-
expect(result[0].children[1].name).toBe('nested-b'); // 10
|
|
104
|
-
expect(result[0].children[2].name).toBe('nested-c'); // auto-assigned 9
|
|
106
|
+
expect(result[0].sideMenu.children[0].name).toBe('nested-a'); // 20
|
|
107
|
+
expect(result[0].sideMenu.children[1].name).toBe('nested-b'); // 10
|
|
108
|
+
expect(result[0].sideMenu.children[2].name).toBe('nested-c'); // auto-assigned 9
|
|
105
109
|
});
|
|
106
110
|
|
|
107
111
|
it('should handle empty children array', () => {
|
|
@@ -112,15 +116,15 @@ describe('plugin-products-helpers', () => {
|
|
|
112
116
|
});
|
|
113
117
|
|
|
114
118
|
it('should not mutate order when weights are not provided for all items', () => {
|
|
115
|
-
const children = [
|
|
119
|
+
const children: ProductChildPage[] = [
|
|
116
120
|
{
|
|
117
|
-
name: 'first', label: 'First',
|
|
121
|
+
name: 'first', label: 'First', component: {}
|
|
118
122
|
},
|
|
119
123
|
{
|
|
120
|
-
name: 'second', label: 'Second',
|
|
124
|
+
name: 'second', label: 'Second', component: {}, sideMenu: { weight: 100 },
|
|
121
125
|
},
|
|
122
126
|
{
|
|
123
|
-
name: 'third', label: 'Third',
|
|
127
|
+
name: 'third', label: 'Third', component: {}
|
|
124
128
|
},
|
|
125
129
|
];
|
|
126
130
|
|
|
@@ -180,7 +184,7 @@ describe('plugin-products-helpers', () => {
|
|
|
180
184
|
component: () => Promise.resolve({ default: {} }),
|
|
181
185
|
};
|
|
182
186
|
|
|
183
|
-
const route = generateVirtualTypeRoute('my-product', page);
|
|
187
|
+
const route = generateVirtualTypeRoute('my-product', page.name);
|
|
184
188
|
|
|
185
189
|
expect(route.name).toBe('my-product-c-cluster-overview');
|
|
186
190
|
expect(route.path).toBe('my-product/c/:cluster/overview');
|
|
@@ -201,7 +205,7 @@ describe('plugin-products-helpers', () => {
|
|
|
201
205
|
component: () => Promise.resolve({ default: {} }),
|
|
202
206
|
};
|
|
203
207
|
|
|
204
|
-
const route = generateVirtualTypeRoute('my-product', page, { extendProduct: true });
|
|
208
|
+
const route = generateVirtualTypeRoute('my-product', page.name, { extendProduct: true });
|
|
205
209
|
|
|
206
210
|
expect(route.name).toBe('c-cluster-my-product-overview');
|
|
207
211
|
expect(route.path).toBe('c/:cluster/my-product/overview');
|
|
@@ -223,7 +227,7 @@ describe('plugin-products-helpers', () => {
|
|
|
223
227
|
component: () => Promise.resolve({ default: {} }),
|
|
224
228
|
};
|
|
225
229
|
|
|
226
|
-
const route = generateVirtualTypeRoute('my-product', page, { omitPath: true });
|
|
230
|
+
const route = generateVirtualTypeRoute('my-product', page.name, { omitPath: true });
|
|
227
231
|
|
|
228
232
|
expect(route.path).toBeUndefined();
|
|
229
233
|
expect(route.name).toBe('my-product-c-cluster-settings');
|
|
@@ -276,9 +280,7 @@ describe('plugin-products-helpers', () => {
|
|
|
276
280
|
});
|
|
277
281
|
|
|
278
282
|
it('should handle pages without a type gracefully', () => {
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
const route = generateConfigureTypeRoute('my-product', page as ProductChildPage);
|
|
283
|
+
const route = generateConfigureTypeRoute('my-product', undefined);
|
|
282
284
|
|
|
283
285
|
expect(route.name).toBe('my-product-c-cluster-resource');
|
|
284
286
|
expect(route.params?.resource).toBeUndefined();
|
|
@@ -389,66 +391,67 @@ describe('plugin-products-helpers', () => {
|
|
|
389
391
|
// ============= Integration-like tests =============
|
|
390
392
|
describe('integration: complex product structure', () => {
|
|
391
393
|
it('should handle a complete product config ordering and route generation', () => {
|
|
392
|
-
const config = [
|
|
394
|
+
const config: ProductChild[] = [
|
|
393
395
|
{
|
|
394
|
-
name: 'overview', label: 'Overview',
|
|
396
|
+
name: 'overview', label: 'Overview', component: {}, sideMenu: { weight: 10 },
|
|
395
397
|
},
|
|
396
398
|
{
|
|
397
|
-
name: 'settings', label: 'Settings', weight: 5, type: 'core.v1.configmap'
|
|
399
|
+
name: 'settings', label: 'Settings', sideMenu: { weight: 5 }, type: 'core.v1.configmap'
|
|
398
400
|
},
|
|
399
401
|
{
|
|
400
|
-
name:
|
|
401
|
-
label:
|
|
402
|
-
type:
|
|
403
|
-
weight: 15,
|
|
402
|
+
name: 'resources',
|
|
403
|
+
label: 'Resources',
|
|
404
|
+
type: 'core.v1.pod',
|
|
405
|
+
sideMenu: { weight: 15 },
|
|
404
406
|
},
|
|
405
407
|
];
|
|
406
408
|
|
|
407
|
-
const ordered = gatherChildrenOrdering(config
|
|
409
|
+
const ordered = gatherChildrenOrdering(config);
|
|
408
410
|
|
|
409
411
|
// Should be sorted by weight descending: 15, 10, 5
|
|
410
|
-
expect(ordered[0].weight).toBe(15);
|
|
412
|
+
expect(ordered[0].sideMenu?.weight).toBe(15);
|
|
411
413
|
expect(ordered[0].name).toBe('resources');
|
|
412
|
-
expect(ordered[1].weight).toBe(10);
|
|
414
|
+
expect(ordered[1].sideMenu?.weight).toBe(10);
|
|
413
415
|
expect(ordered[1].name).toBe('overview');
|
|
414
|
-
expect(ordered[2].weight).toBe(5);
|
|
416
|
+
expect(ordered[2].sideMenu?.weight).toBe(5);
|
|
415
417
|
expect(ordered[2].name).toBe('settings');
|
|
416
418
|
|
|
417
419
|
// Test route generation for each
|
|
418
|
-
const overviewRoute = generateVirtualTypeRoute('my-product', ordered[1]
|
|
420
|
+
const overviewRoute = generateVirtualTypeRoute('my-product', ordered[1].name);
|
|
419
421
|
|
|
420
422
|
expect(overviewRoute.name).toContain('overview');
|
|
421
423
|
|
|
422
|
-
const resourceRoute = generateConfigureTypeRoute('my-product', ordered[0] as
|
|
424
|
+
const resourceRoute = generateConfigureTypeRoute('my-product', ordered[0] as ProductChildResourcePage);
|
|
423
425
|
|
|
424
426
|
expect(resourceRoute.path).toContain('resource');
|
|
425
427
|
});
|
|
426
428
|
|
|
427
429
|
it('should handle nested group ordering', () => {
|
|
428
|
-
const config = [
|
|
430
|
+
const config: ProductChildGroup[] = [
|
|
429
431
|
{
|
|
430
432
|
name: 'main',
|
|
431
433
|
label: 'Main',
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
434
|
+
sideMenu: {
|
|
435
|
+
weight: 50,
|
|
436
|
+
children: [
|
|
437
|
+
{
|
|
438
|
+
name: 'page1', label: 'Page 1', component: {}, sideMenu: { weight: 100 }
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: 'page2', label: 'Page 2', component: {}
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: 'page3', label: 'Page 3', component: {}, sideMenu: { weight: 50 }
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
}
|
|
444
448
|
},
|
|
445
449
|
];
|
|
446
|
-
|
|
447
450
|
const ordered = gatherChildrenOrdering(config);
|
|
448
451
|
|
|
449
|
-
expect(ordered[0].children[0].name).toBe('page1'); // 100
|
|
450
|
-
expect(ordered[0].children[1].name).toBe('page3'); // 50
|
|
451
|
-
expect(ordered[0].children[2].name).toBe('page2'); // auto 49
|
|
452
|
+
expect(ordered[0].sideMenu?.children[0].name).toBe('page1'); // 100
|
|
453
|
+
expect(ordered[0].sideMenu?.children[1].name).toBe('page3'); // 50
|
|
454
|
+
expect(ordered[0].sideMenu?.children[2].name).toBe('page2'); // auto 49
|
|
452
455
|
});
|
|
453
456
|
});
|
|
454
457
|
});
|