@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.
Files changed (89) hide show
  1. package/apis/intf/resources-api/resource-base.ts +42 -2
  2. package/apis/intf/resources-api/resource-instance.ts +101 -0
  3. package/apis/intf/resources-api/resources-api.ts +177 -23
  4. package/apis/intf/resources.ts +8 -6
  5. package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
  6. package/apis/resources/resources-api-class.ts +232 -43
  7. package/assets/images/providers/traefik.png +0 -0
  8. package/assets/translations/en-us.yaml +10 -4
  9. package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
  10. package/chart/rancher-monitoring-dashboards/index.vue +155 -0
  11. package/cloud-credential/__tests__/generic.test.ts +132 -0
  12. package/cloud-credential/generic.vue +33 -3
  13. package/components/EtcdInfoBanner.vue +2 -12
  14. package/components/GrafanaDashboard.vue +2 -8
  15. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
  16. package/components/Resource/Detail/Masthead/index.vue +8 -3
  17. package/components/form/Security.vue +1 -1
  18. package/components/formatter/Translate.vue +22 -1
  19. package/components/nav/Group.vue +5 -10
  20. package/components/nav/TopLevelMenu.vue +1 -30
  21. package/components/nav/__tests__/Group.test.ts +61 -0
  22. package/config/labels-annotations.js +1 -0
  23. package/config/table-headers.js +5 -2
  24. package/core/__tests__/plugin-products-apply.test.ts +787 -0
  25. package/core/__tests__/plugin-products-extend.test.ts +189 -0
  26. package/core/__tests__/plugin-products-helpers.test.ts +81 -78
  27. package/core/__tests__/plugin-products-new.test.ts +389 -0
  28. package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
  29. package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
  30. package/core/plugin-products-base.ts +201 -93
  31. package/core/plugin-products-extending.ts +1 -1
  32. package/core/plugin-products-external.ts +380 -0
  33. package/core/plugin-products-helpers.ts +28 -24
  34. package/core/plugin-products-internal.ts +207 -0
  35. package/core/plugin-products-top-level.ts +10 -5
  36. package/core/plugin-products-type-guards.ts +17 -6
  37. package/core/plugin-products.ts +5 -7
  38. package/core/plugin-types.ts +1 -389
  39. package/core/plugin.ts +10 -11
  40. package/core/types.ts +6 -202
  41. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
  42. package/detail/pod.vue +1 -1
  43. package/detail/provisioning.cattle.io.cluster.vue +0 -2
  44. package/edit/auth/AuthProviderWarningBanners.vue +12 -0
  45. package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
  46. package/edit/auth/__tests__/azuread.test.ts +1 -0
  47. package/edit/auth/__tests__/github.test.ts +1 -0
  48. package/edit/auth/__tests__/oidc.test.ts +1 -0
  49. package/edit/auth/__tests__/saml.test.ts +1 -0
  50. package/edit/auth/ldap/__tests__/index.test.ts +1 -0
  51. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  52. package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -1
  53. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
  54. package/list/management.cattle.io.user.vue +24 -5
  55. package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
  56. package/machine-config/__tests__/generic.test.ts +163 -0
  57. package/machine-config/components/EC2Networking.vue +31 -7
  58. package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
  59. package/machine-config/generic.vue +32 -5
  60. package/mixins/resource-fetch.js +1 -1
  61. package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
  62. package/models/pod.js +12 -10
  63. package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
  64. package/package.json +1 -1
  65. package/pages/auth/login.vue +1 -1
  66. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +110 -0
  67. package/pages/c/_cluster/apps/charts/install.vue +29 -0
  68. package/pages/c/_cluster/auth/config/index.vue +11 -4
  69. package/pages/c/_cluster/monitoring/index.vue +6 -1
  70. package/plugins/dashboard-store/resource-class.js +10 -3
  71. package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
  72. package/plugins/steve/steve-class.js +43 -0
  73. package/plugins/steve/subscribe.js +11 -0
  74. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
  75. package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
  76. package/store/__tests__/auth.test.ts +18 -0
  77. package/store/__tests__/notifications.test.ts +466 -1
  78. package/store/__tests__/ui-context.test.ts +255 -0
  79. package/store/auth.js +11 -1
  80. package/store/features.js +1 -0
  81. package/store/plugins.js +6 -0
  82. package/types/rancher/steve.api.ts +2 -2
  83. package/types/shell/index.d.ts +58 -0
  84. package/types/store/dashboard-store.types.ts +2 -2
  85. package/types/store/type-map.ts +262 -1
  86. package/utils/async.ts +2 -0
  87. package/utils/grafana.js +2 -17
  88. package/utils/monitoring.js +38 -1
  89. 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', weight: 10, children: []
19
+ name: 'a', label: 'A', component: {}, sideMenu: { weight: 10 },
18
20
  },
19
21
  {
20
- name: 'b', label: 'B', weight: 30, children: []
22
+ name: 'b', label: 'B', component: {}, sideMenu: { weight: 30 },
21
23
  },
22
24
  {
23
- name: 'c', label: 'C', weight: 20, children: []
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].name).toBe('b'); // 30
30
- expect(result[1].name).toBe('c'); // 20
31
- expect(result[2].name).toBe('a'); // 10
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', weight: 50, children: []
43
+ name: 'a', label: 'A', component: {}, sideMenu: { weight: 50 },
42
44
  },
43
45
  {
44
- name: 'b', label: 'B', children: []
46
+ name: 'b', label: 'B', component: {}
45
47
  },
46
48
  {
47
- name: 'c', label: 'C', children: []
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!).toBeLessThan(50); // 49
55
- expect(result[2].weight!).toBeLessThan(result[1].weight!); // 48
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', children: []
63
+ name: 'a', label: 'A', component: {}
62
64
  },
63
65
  {
64
- name: 'b', label: 'B', children: []
66
+ name: 'b', label: 'B', component: {}
65
67
  },
66
68
  {
67
- name: 'c', label: 'C', children: []
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
- weight: 50,
86
- children: [
87
- {
88
- name: 'nested-a', label: 'Nested A', weight: 20, children: []
89
- },
90
- {
91
- name: 'nested-b', label: 'Nested B', weight: 10, children: []
92
- },
93
- {
94
- name: 'nested-c', label: 'Nested C', children: []
95
- }, // no weight
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', children: []
121
+ name: 'first', label: 'First', component: {}
118
122
  },
119
123
  {
120
- name: 'second', label: 'Second', weight: 100, children: []
124
+ name: 'second', label: 'Second', component: {}, sideMenu: { weight: 100 },
121
125
  },
122
126
  {
123
- name: 'third', label: 'Third', children: []
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 page: Partial<ProductChildPage> = { name: 'clusters' };
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', weight: 10, children: []
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: 'resources',
401
- label: 'Resources',
402
- type: 'core.v1.pod',
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 as ProductChildGroup[]);
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] as ProductChildPage);
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 ProductChildPage);
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
- weight: 50,
433
- children: [
434
- {
435
- name: 'page1', label: 'Page 1', weight: 100, children: []
436
- },
437
- {
438
- name: 'page2', label: 'Page 2', children: []
439
- },
440
- {
441
- name: 'page3', label: 'Page 3', weight: 50, children: []
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
  });