@rancher/shell 3.0.9-rc.5 → 3.0.9
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/images/providers/oci-open-containers.svg +22 -0
- package/assets/images/providers/traefik.png +0 -0
- package/assets/styles/themes/_dark.scss +2 -0
- package/assets/styles/themes/_light.scss +2 -0
- package/assets/styles/themes/_modern.scss +6 -0
- package/assets/translations/en-us.yaml +129 -25
- package/components/CruResource.vue +3 -1
- package/components/ExplorerProjectsNamespaces.vue +12 -12
- package/components/IconOrSvg.vue +61 -42
- package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +109 -0
- package/components/Resource/Detail/Card/StatusCard/index.vue +21 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +19 -2
- package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +19 -11
- package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +12 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +2 -0
- package/components/Resource/Detail/ResourceRow.vue +2 -2
- package/components/ResourceList/index.vue +7 -4
- package/components/SortableTable/index.vue +2 -2
- package/components/Window/ContainerLogs.vue +48 -37
- package/components/fleet/FleetClusterTargets/TargetsList.vue +2 -2
- package/components/fleet/FleetClusterTargets/index.vue +6 -1
- package/components/fleet/GitRepoAdvancedTab.vue +333 -0
- package/components/fleet/GitRepoMetadataTab.vue +43 -0
- package/components/fleet/GitRepoRepositoryTab.vue +101 -0
- package/components/fleet/GitRepoTargetTab.vue +77 -0
- package/components/fleet/HelmOpAdvancedTab.vue +247 -0
- package/components/fleet/HelmOpChartTab.vue +158 -0
- package/components/fleet/HelmOpMetadataTab.vue +46 -0
- package/components/fleet/HelmOpTargetTab.vue +84 -0
- package/components/fleet/HelmOpValuesTab.vue +147 -0
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +119 -70
- package/components/form/BannerSettings.vue +2 -2
- package/components/form/NodeScheduling.vue +81 -7
- package/components/form/NotificationSettings.vue +2 -2
- package/components/form/PodAffinity.vue +1 -36
- package/components/form/ResourceLabeledSelect.vue +8 -4
- package/components/form/ResourceQuota/Namespace.vue +30 -9
- package/components/form/ResourceQuota/NamespaceRow.vue +25 -7
- package/components/form/ResourceQuota/Project.vue +140 -82
- package/components/form/ResourceQuota/ResourceQuotaEntry.vue +145 -0
- package/components/form/ResourceQuota/__tests__/Namespace.test.ts +307 -0
- package/components/form/ResourceQuota/__tests__/NamespaceRow.test.ts +281 -0
- package/components/form/ResourceQuota/__tests__/Project.test.ts +274 -27
- package/components/form/ResourceQuota/__tests__/ResourceQuotaEntry.test.ts +215 -0
- package/components/form/SchedulingCustomization.vue +14 -6
- package/components/form/SelectOrCreateAuthSecret.vue +107 -18
- package/components/form/__tests__/NodeScheduling.test.ts +12 -9
- package/components/form/__tests__/PodAffinity.test.ts +21 -2
- package/components/form/__tests__/SchedulingCustomization.test.ts +240 -0
- package/components/formatter/ClusterLink.vue +8 -0
- package/components/formatter/SecretOrigin.vue +79 -0
- package/config/labels-annotations.js +7 -6
- package/config/pagination-table-headers.js +6 -4
- package/config/product/explorer.js +1 -11
- package/config/product/manager.js +0 -1
- package/config/query-params.js +3 -0
- package/config/settings.ts +15 -2
- package/config/table-headers.js +21 -17
- package/config/types.js +23 -8
- package/detail/fleet.cattle.io.cluster.vue +1 -1
- package/detail/workload/index.vue +11 -16
- package/dialog/DeactivateDriverDialog.vue +1 -1
- package/dialog/FeatureFlagListDialog.vue +1 -1
- package/dialog/Ipv6NetworkingDialog.vue +156 -0
- package/dialog/ScalePoolDownDialog.vue +2 -2
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +1 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -128
- package/edit/auth/oidc.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +155 -25
- package/edit/fleet.cattle.io.gitrepo.vue +153 -283
- package/edit/fleet.cattle.io.helmop.vue +190 -332
- package/edit/management.cattle.io.project.vue +5 -42
- package/edit/management.cattle.io.setting.vue +6 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
- package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
- package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
- package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +55 -24
- package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +1 -103
- package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2-fleet-cluster-agent.test.ts +283 -0
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +65 -49
- package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +114 -0
- package/edit/provisioning.cattle.io.cluster/ingress/IngressConfiguration.vue +158 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +167 -69
- package/edit/provisioning.cattle.io.cluster/shared.ts +36 -1
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +70 -7
- package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +343 -0
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/__tests__/S3Config.test.ts +13 -1
- package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +10 -44
- package/edit/secret/index.vue +1 -1
- package/edit/token.vue +68 -29
- package/edit/workload/__tests__/index.test.ts +2 -37
- package/edit/workload/index.vue +6 -2
- package/edit/workload/mixins/workload.js +0 -32
- package/list/__tests__/management.cattle.io.setting.test.ts +198 -0
- package/list/management.cattle.io.setting.vue +13 -0
- package/list/provisioning.cattle.io.cluster.vue +50 -1
- package/list/secret.vue +4 -9
- package/list/service.vue +6 -8
- package/machine-config/amazonec2.vue +11 -4
- package/machine-config/components/EC2Networking.vue +46 -30
- package/machine-config/components/__tests__/EC2Networking.test.ts +7 -7
- package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +0 -9
- package/machine-config/digitalocean.vue +3 -3
- package/models/__tests__/chart.test.ts +2 -2
- package/models/__tests__/namespace.test.ts +11 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +96 -0
- package/models/__tests__/workload.test.ts +42 -1
- package/models/catalog.cattle.io.clusterrepo.js +30 -4
- package/models/chart.js +3 -3
- package/models/ext.cattle.io.token.js +48 -0
- package/models/kontainerdriver.js +2 -2
- package/models/namespace.js +7 -1
- package/models/nodedriver.js +2 -2
- package/models/provisioning.cattle.io.cluster.js +28 -7
- package/models/secret.js +0 -17
- package/models/service.js +44 -1
- package/models/token.js +4 -0
- package/models/workload.js +12 -6
- package/package.json +1 -1
- package/pages/account/index.vue +96 -67
- package/pages/auth/setup.vue +5 -14
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
- package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +4 -1
- package/pages/c/_cluster/apps/charts/index.vue +82 -3
- package/pages/c/_cluster/apps/charts/install.vue +317 -42
- package/pages/c/_cluster/explorer/tools/index.vue +1 -1
- package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
- package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +5 -4
- package/pages/c/_cluster/settings/index.vue +3 -1
- package/pages/c/_cluster/uiplugins/index.vue +1 -1
- package/plugins/dashboard-store/__tests__/getters.test.ts +108 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +27 -0
- package/plugins/dashboard-store/actions.js +3 -8
- package/plugins/dashboard-store/getters.js +7 -5
- package/plugins/dashboard-store/mutations.js +4 -1
- package/plugins/dashboard-store/resource-class.js +3 -3
- package/plugins/steve/__tests__/steve-class.test.ts +102 -141
- package/plugins/steve/steve-class.js +12 -3
- package/plugins/steve/steve-pagination-utils.ts +6 -2
- package/rancher-components/RcIcon/types.ts +2 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +72 -20
- package/store/prefs.js +3 -0
- package/types/aws-sdk.d.ts +121 -0
- package/types/resources/node.ts +15 -0
- package/types/shell/index.d.ts +537 -506
- package/types/store/pagination.types.ts +5 -5
- package/utils/__tests__/array.test.ts +1 -29
- package/utils/__tests__/cluster-agent-configuration.test.ts +203 -0
- package/utils/array.ts +0 -11
- package/utils/aws.ts +21 -0
- package/utils/cluster.js +22 -2
- package/utils/selector-typed.ts +1 -1
- package/utils/svg-filter.js +4 -3
- package/components/__tests__/ProjectRow.test.ts +0 -206
- package/components/form/ResourceQuota/ProjectRow.vue +0 -277
|
@@ -14,50 +14,297 @@ describe('project', () => {
|
|
|
14
14
|
types: []
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
describe('created() spec parser', () => {
|
|
18
|
+
it('should initialise resourceQuotas as an empty array when spec limits are empty', () => {
|
|
19
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
expect(wrapper.vm.resourceQuotas).toStrictEqual([]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should parse a standard resource type from the spec into resourceQuotas', () => {
|
|
25
|
+
const wrapper: any = shallowMount(Project, {
|
|
26
|
+
props: {
|
|
27
|
+
...defaultProps,
|
|
28
|
+
value: {
|
|
29
|
+
spec: {
|
|
30
|
+
resourceQuota: { limit: { configMaps: '20' } },
|
|
31
|
+
namespaceDefaultResourceQuota: { limit: { configMaps: '10' } }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(wrapper.vm.resourceQuotas).toHaveLength(1);
|
|
38
|
+
expect(wrapper.vm.resourceQuotas[0]).toMatchObject({
|
|
39
|
+
resourceType: 'configMaps',
|
|
40
|
+
resourceIdentifier: 'configMaps',
|
|
41
|
+
projectLimit: '20',
|
|
42
|
+
namespaceDefaultLimit: '10',
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should parse an extended resource type from the spec into resourceQuotas', () => {
|
|
47
|
+
const wrapper: any = shallowMount(Project, {
|
|
48
|
+
props: {
|
|
49
|
+
...defaultProps,
|
|
50
|
+
value: {
|
|
51
|
+
spec: {
|
|
52
|
+
resourceQuota: { limit: { extended: { test1: '10' } } },
|
|
53
|
+
namespaceDefaultResourceQuota: { limit: { extended: { test1: '5' } } }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(wrapper.vm.resourceQuotas).toHaveLength(1);
|
|
60
|
+
expect(wrapper.vm.resourceQuotas[0]).toMatchObject({
|
|
61
|
+
resourceType: TYPES.EXTENDED,
|
|
62
|
+
resourceIdentifier: 'test1',
|
|
63
|
+
projectLimit: '10',
|
|
64
|
+
namespaceDefaultLimit: '5',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should initialise resourceQuotas as an empty array when resourceQuota exists but has no limit property', () => {
|
|
69
|
+
const wrapper: any = shallowMount(Project, {
|
|
70
|
+
props: {
|
|
71
|
+
...defaultProps,
|
|
72
|
+
value: {
|
|
73
|
+
spec: {
|
|
74
|
+
resourceQuota: {},
|
|
75
|
+
namespaceDefaultResourceQuota: { limit: {} }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(wrapper.vm.resourceQuotas).toStrictEqual([]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should parse project quotas and fall back to empty string for missing ns limits when namespaceDefaultResourceQuota has no limit property', () => {
|
|
85
|
+
const wrapper: any = shallowMount(Project, {
|
|
86
|
+
props: {
|
|
87
|
+
...defaultProps,
|
|
88
|
+
value: {
|
|
89
|
+
spec: {
|
|
90
|
+
resourceQuota: { limit: { configMaps: '20' } },
|
|
91
|
+
namespaceDefaultResourceQuota: {}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
21
96
|
|
|
22
|
-
|
|
23
|
-
|
|
97
|
+
expect(wrapper.vm.resourceQuotas).toHaveLength(1);
|
|
98
|
+
expect(wrapper.vm.resourceQuotas[0]).toMatchObject({
|
|
99
|
+
resourceType: 'configMaps',
|
|
100
|
+
projectLimit: '20',
|
|
101
|
+
namespaceDefaultLimit: '',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should parse both standard and extended resource types into resourceQuotas', () => {
|
|
106
|
+
const wrapper: any = shallowMount(Project, {
|
|
107
|
+
props: {
|
|
108
|
+
...defaultProps,
|
|
109
|
+
value: {
|
|
110
|
+
spec: {
|
|
111
|
+
resourceQuota: { limit: { configMaps: '20', extended: { test1: '10' } } },
|
|
112
|
+
namespaceDefaultResourceQuota: { limit: { configMaps: '15', extended: { test1: '8' } } }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(wrapper.vm.resourceQuotas).toHaveLength(2);
|
|
119
|
+
});
|
|
24
120
|
});
|
|
25
121
|
|
|
26
|
-
|
|
27
|
-
|
|
122
|
+
describe('specFromQuotas()', () => {
|
|
123
|
+
it('should convert a standard resourceQuota entry to projectLimit and nsLimit', () => {
|
|
124
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
125
|
+
|
|
126
|
+
wrapper.setData({
|
|
127
|
+
resourceQuotas: [{
|
|
128
|
+
id: '1',
|
|
129
|
+
resourceType: 'configMaps',
|
|
130
|
+
resourceIdentifier: 'configMaps',
|
|
131
|
+
projectLimit: '20',
|
|
132
|
+
namespaceDefaultLimit: '10',
|
|
133
|
+
}]
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(wrapper.vm.specFromQuotas()).toStrictEqual({
|
|
137
|
+
projectLimit: { configMaps: '20' },
|
|
138
|
+
nsLimit: { configMaps: '10' },
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should convert an extended resourceQuota entry to a nested extended object', () => {
|
|
143
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
144
|
+
|
|
145
|
+
wrapper.setData({
|
|
146
|
+
resourceQuotas: [{
|
|
147
|
+
id: '1',
|
|
148
|
+
resourceType: TYPES.EXTENDED,
|
|
149
|
+
resourceIdentifier: 'my-resource',
|
|
150
|
+
projectLimit: '5',
|
|
151
|
+
namespaceDefaultLimit: '3',
|
|
152
|
+
}]
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(wrapper.vm.specFromQuotas()).toStrictEqual({
|
|
156
|
+
projectLimit: { extended: { 'my-resource': '5' } },
|
|
157
|
+
nsLimit: { extended: { 'my-resource': '3' } },
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should omit an extended entry from the spec when resourceIdentifier is empty', () => {
|
|
162
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
28
163
|
|
|
29
|
-
|
|
164
|
+
wrapper.setData({
|
|
165
|
+
resourceQuotas: [{
|
|
166
|
+
id: '1',
|
|
167
|
+
resourceType: TYPES.EXTENDED,
|
|
168
|
+
resourceIdentifier: '',
|
|
169
|
+
projectLimit: '5',
|
|
170
|
+
namespaceDefaultLimit: '3',
|
|
171
|
+
}]
|
|
172
|
+
});
|
|
30
173
|
|
|
31
|
-
|
|
174
|
+
const { projectLimit } = wrapper.vm.specFromQuotas();
|
|
32
175
|
|
|
33
|
-
|
|
34
|
-
|
|
176
|
+
expect(projectLimit.extended).toBeUndefined();
|
|
177
|
+
});
|
|
35
178
|
});
|
|
36
179
|
|
|
37
|
-
|
|
38
|
-
const
|
|
180
|
+
describe('remainingTypes()', () => {
|
|
181
|
+
const typesProps = [
|
|
182
|
+
{
|
|
183
|
+
value: TYPES.EXTENDED,
|
|
184
|
+
inputExponent: 0,
|
|
185
|
+
baseUnit: '',
|
|
186
|
+
labelKey: 'resourceQuota.custom',
|
|
187
|
+
placeholderKey: 'resourceQuota.projectLimit.unitlessPlaceholder'
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
value: 'configMaps',
|
|
191
|
+
inputExponent: 0,
|
|
192
|
+
baseUnit: '',
|
|
193
|
+
labelKey: 'resourceQuota.configMaps',
|
|
194
|
+
placeholderKey: 'resourceQuota.projectLimit.unitlessPlaceholder'
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
value: 'pods',
|
|
198
|
+
inputExponent: 0,
|
|
199
|
+
baseUnit: '',
|
|
200
|
+
labelKey: 'resourceQuota.pods',
|
|
201
|
+
placeholderKey: 'resourceQuota.projectLimit.unitlessPlaceholder'
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
it('should exclude already-used non-extended types', () => {
|
|
206
|
+
const wrapper: any = shallowMount(Project, { props: { ...defaultProps, types: typesProps } });
|
|
207
|
+
|
|
208
|
+
wrapper.setData({
|
|
209
|
+
resourceQuotas: [{
|
|
210
|
+
id: '1',
|
|
211
|
+
resourceType: 'configMaps',
|
|
212
|
+
resourceIdentifier: 'configMaps',
|
|
213
|
+
projectLimit: '20',
|
|
214
|
+
namespaceDefaultLimit: '10',
|
|
215
|
+
}]
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const values = wrapper.vm.remainingTypes('pods').map((t: any) => t.value);
|
|
219
|
+
|
|
220
|
+
expect(values).not.toContain('configMaps');
|
|
221
|
+
expect(values).toContain('pods');
|
|
222
|
+
expect(values).toContain(TYPES.EXTENDED);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should include the currentType even if it is already used', () => {
|
|
226
|
+
const wrapper: any = shallowMount(Project, { props: { ...defaultProps, types: typesProps } });
|
|
227
|
+
|
|
228
|
+
wrapper.setData({
|
|
229
|
+
resourceQuotas: [{
|
|
230
|
+
id: '1',
|
|
231
|
+
resourceType: 'configMaps',
|
|
232
|
+
resourceIdentifier: 'configMaps',
|
|
233
|
+
projectLimit: '20',
|
|
234
|
+
namespaceDefaultLimit: '10',
|
|
235
|
+
}]
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const values = wrapper.vm.remainingTypes('configMaps').map((t: any) => t.value);
|
|
239
|
+
|
|
240
|
+
expect(values).toContain('configMaps');
|
|
241
|
+
});
|
|
39
242
|
|
|
40
|
-
|
|
243
|
+
it('should always include extended type', () => {
|
|
244
|
+
const wrapper: any = shallowMount(Project, { props: { ...defaultProps, types: typesProps } });
|
|
41
245
|
|
|
42
|
-
|
|
246
|
+
const values = wrapper.vm.remainingTypes('pods').map((t: any) => t.value);
|
|
43
247
|
|
|
44
|
-
|
|
45
|
-
|
|
248
|
+
expect(values).toContain(TYPES.EXTENDED);
|
|
249
|
+
});
|
|
46
250
|
});
|
|
47
251
|
|
|
48
|
-
|
|
49
|
-
|
|
252
|
+
describe('validationChanged watcher', () => {
|
|
253
|
+
it('should emit validationChanged with false when an extended quota has no resource identifier', async() => {
|
|
254
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
255
|
+
|
|
256
|
+
await wrapper.setData({
|
|
257
|
+
resourceQuotas: [{
|
|
258
|
+
id: '1',
|
|
259
|
+
resourceType: TYPES.EXTENDED,
|
|
260
|
+
resourceIdentifier: '',
|
|
261
|
+
projectLimit: '5',
|
|
262
|
+
namespaceDefaultLimit: '3',
|
|
263
|
+
}]
|
|
264
|
+
});
|
|
50
265
|
|
|
51
|
-
|
|
266
|
+
const emitted = wrapper.emitted('validationChanged');
|
|
52
267
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
customType: 'my-resource',
|
|
56
|
-
index: 0
|
|
268
|
+
expect(emitted).toBeTruthy();
|
|
269
|
+
expect(emitted[emitted.length - 1]).toStrictEqual([false]);
|
|
57
270
|
});
|
|
58
271
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
272
|
+
it('should emit validationChanged with true when all extended quotas have resource identifiers', async() => {
|
|
273
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
274
|
+
|
|
275
|
+
await wrapper.setData({
|
|
276
|
+
resourceQuotas: [{
|
|
277
|
+
id: '1',
|
|
278
|
+
resourceType: TYPES.EXTENDED,
|
|
279
|
+
resourceIdentifier: 'my-resource',
|
|
280
|
+
projectLimit: '5',
|
|
281
|
+
namespaceDefaultLimit: '3',
|
|
282
|
+
}]
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const emitted = wrapper.emitted('validationChanged');
|
|
286
|
+
|
|
287
|
+
expect(emitted).toBeTruthy();
|
|
288
|
+
expect(emitted[emitted.length - 1]).toStrictEqual([true]);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should emit validationChanged with true when resourceQuotas has no extended entries', async() => {
|
|
292
|
+
const wrapper: any = shallowMount(Project, { props: defaultProps });
|
|
293
|
+
|
|
294
|
+
await wrapper.setData({
|
|
295
|
+
resourceQuotas: [{
|
|
296
|
+
id: '1',
|
|
297
|
+
resourceType: 'configMaps',
|
|
298
|
+
resourceIdentifier: 'configMaps',
|
|
299
|
+
projectLimit: '20',
|
|
300
|
+
namespaceDefaultLimit: '10',
|
|
301
|
+
}]
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const emitted = wrapper.emitted('validationChanged');
|
|
305
|
+
|
|
306
|
+
expect(emitted).toBeTruthy();
|
|
307
|
+
expect(emitted[emitted.length - 1]).toStrictEqual([true]);
|
|
308
|
+
});
|
|
62
309
|
});
|
|
63
310
|
});
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { createStore } from 'vuex';
|
|
3
|
+
import ResourceQuotaEntry from '@shell/components/form/ResourceQuota/ResourceQuotaEntry.vue';
|
|
4
|
+
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
5
|
+
import { RcButton } from '@components/RcButton';
|
|
6
|
+
import { TYPES } from '@shell/components/form/ResourceQuota/shared';
|
|
7
|
+
|
|
8
|
+
describe('component: ResourceQuotaEntry', () => {
|
|
9
|
+
const store = createStore({});
|
|
10
|
+
|
|
11
|
+
const mockType = {
|
|
12
|
+
value: 'configMaps',
|
|
13
|
+
inputExponent: 0,
|
|
14
|
+
baseUnit: '',
|
|
15
|
+
placeholder: 'Enter count',
|
|
16
|
+
increment: 1,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const podsType = {
|
|
20
|
+
value: 'pods',
|
|
21
|
+
inputExponent: 0,
|
|
22
|
+
baseUnit: '',
|
|
23
|
+
placeholder: 'Enter count',
|
|
24
|
+
increment: 1,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const limitsCpuType = {
|
|
28
|
+
value: 'limitsCpu',
|
|
29
|
+
inputExponent: -1,
|
|
30
|
+
baseUnit: '',
|
|
31
|
+
placeholder: 'Enter CPU',
|
|
32
|
+
increment: 1,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const extendedType = {
|
|
36
|
+
value: TYPES.EXTENDED,
|
|
37
|
+
inputExponent: 0,
|
|
38
|
+
baseUnit: '',
|
|
39
|
+
placeholder: 'Enter value',
|
|
40
|
+
increment: 1,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const allTypes = [mockType, podsType, limitsCpuType, extendedType];
|
|
44
|
+
|
|
45
|
+
const defaultProps = {
|
|
46
|
+
id: '1',
|
|
47
|
+
mode: 'edit',
|
|
48
|
+
types: allTypes,
|
|
49
|
+
resourceType: 'configMaps',
|
|
50
|
+
resourceIdentifier: 'configMaps',
|
|
51
|
+
projectLimit: '20',
|
|
52
|
+
namespaceDefaultLimit: '10',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const createWrapper = (propsOverrides: Record<string, unknown> = {}) => {
|
|
56
|
+
return shallowMount(ResourceQuotaEntry, {
|
|
57
|
+
props: { ...defaultProps, ...propsOverrides },
|
|
58
|
+
global: { provide: { store } },
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
describe('rendering', () => {
|
|
63
|
+
it('should render all input fields with their data-testid attributes', () => {
|
|
64
|
+
const wrapper = createWrapper();
|
|
65
|
+
|
|
66
|
+
expect(wrapper.find('[data-testid="projectrow-type-input"]').exists()).toBe(true);
|
|
67
|
+
expect(wrapper.find('[data-testid="projectrow-custom-type-input"]').exists()).toBe(true);
|
|
68
|
+
expect(wrapper.find('[data-testid="projectrow-project-quota-input"]').exists()).toBe(true);
|
|
69
|
+
expect(wrapper.find('[data-testid="projectrow-namespace-quota-input"]').exists()).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('computed: typeOption', () => {
|
|
74
|
+
it('should return the matching type option for a given resourceType', () => {
|
|
75
|
+
const wrapper = createWrapper({ resourceType: 'configMaps' });
|
|
76
|
+
|
|
77
|
+
expect((wrapper.vm as any).typeOption).toStrictEqual(mockType);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return the extended type option when resourceType is TYPES.EXTENDED', () => {
|
|
81
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
82
|
+
|
|
83
|
+
expect((wrapper.vm as any).typeOption).toStrictEqual(extendedType);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('computed: isCustom', () => {
|
|
88
|
+
it('should return true when resourceType equals TYPES.EXTENDED', () => {
|
|
89
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
90
|
+
|
|
91
|
+
expect((wrapper.vm as any).isCustom).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it.each([
|
|
95
|
+
['configMaps'],
|
|
96
|
+
['pods'],
|
|
97
|
+
['limitsCpu'],
|
|
98
|
+
])('should return false when resourceType is "%s"', (resourceType) => {
|
|
99
|
+
const wrapper = createWrapper({ resourceType });
|
|
100
|
+
|
|
101
|
+
expect((wrapper.vm as any).isCustom).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('computed: customTypeRules', () => {
|
|
106
|
+
it('should return an empty array when resourceType is a standard type', () => {
|
|
107
|
+
const wrapper = createWrapper({ resourceType: 'configMaps' });
|
|
108
|
+
|
|
109
|
+
expect((wrapper.vm as any).customTypeRules).toStrictEqual([]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should return one validation rule when resourceType is TYPES.EXTENDED', () => {
|
|
113
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
114
|
+
|
|
115
|
+
expect((wrapper.vm as any).customTypeRules).toHaveLength(1);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('validation rule behavior for custom type', () => {
|
|
119
|
+
it.each([
|
|
120
|
+
['', 'resourceQuota.errors.customTypeRequired'],
|
|
121
|
+
[null, 'resourceQuota.errors.customTypeRequired'],
|
|
122
|
+
[undefined, 'resourceQuota.errors.customTypeRequired'],
|
|
123
|
+
])('should return an error message for falsy value "%s"', (value, expectedError) => {
|
|
124
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
125
|
+
const [rule] = (wrapper.vm as any).customTypeRules;
|
|
126
|
+
|
|
127
|
+
expect(rule(value)).toBe(expectedError);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it.each([
|
|
131
|
+
['my-resource'],
|
|
132
|
+
['cpu'],
|
|
133
|
+
['custom.resource/name'],
|
|
134
|
+
])('should return undefined for non-empty value "%s"', (value) => {
|
|
135
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
136
|
+
const [rule] = (wrapper.vm as any).customTypeRules;
|
|
137
|
+
|
|
138
|
+
expect(rule(value)).toBeUndefined();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('method: remove', () => {
|
|
144
|
+
it('should emit remove event with the resource quota id', () => {
|
|
145
|
+
const id = 'test-remove-id';
|
|
146
|
+
const wrapper = createWrapper({ id });
|
|
147
|
+
|
|
148
|
+
(wrapper.vm as any).remove(id);
|
|
149
|
+
|
|
150
|
+
expect(wrapper.emitted('remove')).toBeTruthy();
|
|
151
|
+
expect(wrapper.emitted('remove')![0]).toStrictEqual([id]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should emit remove when the Remove button is clicked', async() => {
|
|
155
|
+
const id = 'click-test-id';
|
|
156
|
+
const wrapper = createWrapper({ id });
|
|
157
|
+
|
|
158
|
+
await wrapper.findComponent(RcButton as any).trigger('click');
|
|
159
|
+
|
|
160
|
+
expect(wrapper.emitted('remove')).toBeTruthy();
|
|
161
|
+
expect(wrapper.emitted('remove')![0]).toStrictEqual([id]);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('method: updateResourceIdentifier', () => {
|
|
166
|
+
it('should emit update:resourceIdentifier with the new type for standard types', () => {
|
|
167
|
+
const wrapper = createWrapper({ resourceType: 'configMaps' });
|
|
168
|
+
|
|
169
|
+
(wrapper.vm as any).updateResourceIdentifier('pods');
|
|
170
|
+
|
|
171
|
+
expect(wrapper.emitted('update:resourceIdentifier')).toBeTruthy();
|
|
172
|
+
expect(wrapper.emitted('update:resourceIdentifier')![0]).toStrictEqual(['pods']);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should not emit update:resourceIdentifier when called with TYPES.EXTENDED', () => {
|
|
176
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
177
|
+
|
|
178
|
+
(wrapper.vm as any).updateResourceIdentifier(TYPES.EXTENDED);
|
|
179
|
+
|
|
180
|
+
expect(wrapper.emitted('update:resourceIdentifier')).toBeFalsy();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('template: LabeledInput disabled state', () => {
|
|
185
|
+
it('should have the identifier input disabled when resourceType is a standard type', () => {
|
|
186
|
+
const wrapper = createWrapper({ resourceType: 'configMaps' });
|
|
187
|
+
const input = wrapper.findComponent(LabeledInput as any);
|
|
188
|
+
|
|
189
|
+
expect(input.props('disabled')).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should not have the identifier input disabled when resourceType is TYPES.EXTENDED', () => {
|
|
193
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
194
|
+
const input = wrapper.findComponent(LabeledInput as any);
|
|
195
|
+
|
|
196
|
+
expect(input.props('disabled')).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('template: LabeledInput required state', () => {
|
|
201
|
+
it('should have the identifier input required when resourceType is TYPES.EXTENDED', () => {
|
|
202
|
+
const wrapper = createWrapper({ resourceType: TYPES.EXTENDED });
|
|
203
|
+
const input = wrapper.findComponent(LabeledInput as any);
|
|
204
|
+
|
|
205
|
+
expect(input.props('required')).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should not have the identifier input required when resourceType is a standard type', () => {
|
|
209
|
+
const wrapper = createWrapper({ resourceType: 'configMaps' });
|
|
210
|
+
const input = wrapper.findComponent(LabeledInput as any);
|
|
211
|
+
|
|
212
|
+
expect(input.props('required')).toBe(false);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -7,6 +7,10 @@ export default {
|
|
|
7
7
|
components: { Checkbox, Banner },
|
|
8
8
|
emits: ['scheduling-customization-changed'],
|
|
9
9
|
props: {
|
|
10
|
+
type: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true,
|
|
13
|
+
},
|
|
10
14
|
value: {
|
|
11
15
|
type: Object,
|
|
12
16
|
default: () => {},
|
|
@@ -26,6 +30,10 @@ export default {
|
|
|
26
30
|
defaultPDB: {
|
|
27
31
|
type: Object,
|
|
28
32
|
default: () => {},
|
|
33
|
+
},
|
|
34
|
+
checkboxWithOnlyAgentName: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false
|
|
29
37
|
}
|
|
30
38
|
},
|
|
31
39
|
data() {
|
|
@@ -59,10 +67,10 @@ export default {
|
|
|
59
67
|
<Checkbox
|
|
60
68
|
:value="enabled"
|
|
61
69
|
:mode="mode"
|
|
62
|
-
label
|
|
63
|
-
|
|
70
|
+
:label="checkboxWithOnlyAgentName ? t(`cluster.agentConfig.subGroups.agentsScheduling.${type}`) : t('cluster.agentConfig.subGroups.agentsScheduling.label', { agent: t(`cluster.agentConfig.subGroups.agentsScheduling.${type}`)})"
|
|
71
|
+
:description="checkboxWithOnlyAgentName ? '' :t('cluster.agentConfig.subGroups.agentsScheduling.description', { agent: t(`cluster.agentConfig.subGroups.agentsScheduling.${type}`)})"
|
|
64
72
|
data-testid="scheduling-customization-checkbox"
|
|
65
|
-
@update:value="$emit('scheduling-customization-changed', $event)"
|
|
73
|
+
@update:value="$emit('scheduling-customization-changed', { event: $event, agentType: type })"
|
|
66
74
|
>
|
|
67
75
|
<template
|
|
68
76
|
v-if="feature && isEdit && settingMissmatch"
|
|
@@ -71,13 +79,13 @@ export default {
|
|
|
71
79
|
<Banner
|
|
72
80
|
class="mt-10 mb-10"
|
|
73
81
|
color="info"
|
|
74
|
-
label
|
|
82
|
+
:label="t('cluster.agentConfig.subGroups.agentsScheduling.banner', { agent: t(`cluster.agentConfig.subGroups.agentsScheduling.${type}`)})"
|
|
75
83
|
/>
|
|
76
84
|
<Checkbox
|
|
77
85
|
:value="applyGlobal"
|
|
78
86
|
:mode="mode"
|
|
79
|
-
label
|
|
80
|
-
@update:value="$emit('scheduling-customization-changed',
|
|
87
|
+
:label="t('cluster.agentConfig.subGroups.agentsScheduling.innerCheckbox', { agent: t(`cluster.agentConfig.subGroups.agentsScheduling.${type}`)})"
|
|
88
|
+
@update:value="$emit('scheduling-customization-changed', { event: $event, agentType: type })"
|
|
81
89
|
/>
|
|
82
90
|
</template>
|
|
83
91
|
</Checkbox>
|