@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.3
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/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import Queue from '@shell/utils/queue';
|
|
2
|
+
import {
|
|
3
|
+
allHash,
|
|
4
|
+
allHashSettled,
|
|
5
|
+
deferred,
|
|
6
|
+
eachLimit,
|
|
7
|
+
setPromiseResult,
|
|
8
|
+
} from '@shell/utils/promise';
|
|
9
|
+
|
|
10
|
+
describe('queue', () => {
|
|
11
|
+
describe('getLength', () => {
|
|
12
|
+
it.each([
|
|
13
|
+
{
|
|
14
|
+
desc: '0 for an empty queue', enqueueCount: 0, dequeueCount: 0, expected: 0
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
desc: '1 after one enqueue', enqueueCount: 1, dequeueCount: 0, expected: 1
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
desc: 'decrements after dequeue', enqueueCount: 2, dequeueCount: 1, expected: 1
|
|
21
|
+
},
|
|
22
|
+
])('returns $desc', ({ enqueueCount, dequeueCount, expected }) => {
|
|
23
|
+
const q = new Queue();
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < enqueueCount; i++) {
|
|
26
|
+
q.enqueue(`item-${ i }`);
|
|
27
|
+
}
|
|
28
|
+
for (let i = 0; i < dequeueCount; i++) {
|
|
29
|
+
q.dequeue();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(q.getLength()).toStrictEqual(expected);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('isEmpty', () => {
|
|
37
|
+
it.each([
|
|
38
|
+
{
|
|
39
|
+
desc: 'true when queue is empty', enqueueCount: 0, dequeueCount: 0, expected: true
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
desc: 'false after enqueueing an item', enqueueCount: 1, dequeueCount: 0, expected: false
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
desc: 'true after all items are dequeued', enqueueCount: 1, dequeueCount: 1, expected: true
|
|
46
|
+
},
|
|
47
|
+
])('returns $desc', ({ enqueueCount, dequeueCount, expected }) => {
|
|
48
|
+
const q = new Queue();
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < enqueueCount; i++) {
|
|
51
|
+
q.enqueue('x');
|
|
52
|
+
}
|
|
53
|
+
for (let i = 0; i < dequeueCount; i++) {
|
|
54
|
+
q.dequeue();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
expect(q.isEmpty()).toStrictEqual(expected);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('enqueue / dequeue', () => {
|
|
62
|
+
it('dequeues items in FIFO order', () => {
|
|
63
|
+
const q = new Queue();
|
|
64
|
+
|
|
65
|
+
q.enqueue('first');
|
|
66
|
+
q.enqueue('second');
|
|
67
|
+
q.enqueue('third');
|
|
68
|
+
|
|
69
|
+
expect(q.dequeue()).toStrictEqual('first');
|
|
70
|
+
expect(q.dequeue()).toStrictEqual('second');
|
|
71
|
+
expect(q.dequeue()).toStrictEqual('third');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('returns undefined when dequeuing from an empty queue', () => {
|
|
75
|
+
const q = new Queue();
|
|
76
|
+
|
|
77
|
+
expect(q.dequeue()).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('works with objects as items', () => {
|
|
81
|
+
const q = new Queue();
|
|
82
|
+
const item = { key: 'value' };
|
|
83
|
+
|
|
84
|
+
q.enqueue(item);
|
|
85
|
+
expect(q.dequeue()).toStrictEqual(item);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('peek', () => {
|
|
90
|
+
it.each([
|
|
91
|
+
{
|
|
92
|
+
desc: 'the front item without removing it', items: ['a', 'b'], expected: 'a'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
desc: 'undefined when queue is empty', items: [] as string[], expected: undefined
|
|
96
|
+
},
|
|
97
|
+
])('returns $desc', ({ items, expected }) => {
|
|
98
|
+
const q = new Queue();
|
|
99
|
+
|
|
100
|
+
items.forEach((item) => q.enqueue(item));
|
|
101
|
+
|
|
102
|
+
expect(q.peek()).toStrictEqual(expected);
|
|
103
|
+
expect(q.getLength()).toStrictEqual(items.length);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('clear', () => {
|
|
108
|
+
it('empties the queue', () => {
|
|
109
|
+
const q = new Queue();
|
|
110
|
+
|
|
111
|
+
q.enqueue('a');
|
|
112
|
+
q.enqueue('b');
|
|
113
|
+
q.clear();
|
|
114
|
+
|
|
115
|
+
expect(q.isEmpty()).toStrictEqual(true);
|
|
116
|
+
expect(q.getLength()).toStrictEqual(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('allows re-use after clear', () => {
|
|
120
|
+
const q = new Queue();
|
|
121
|
+
|
|
122
|
+
q.enqueue('a');
|
|
123
|
+
q.clear();
|
|
124
|
+
q.enqueue('b');
|
|
125
|
+
|
|
126
|
+
expect(q.dequeue()).toStrictEqual('b');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('allHash', () => {
|
|
132
|
+
it('resolves a hash of resolved promises', async() => {
|
|
133
|
+
const result = await allHash({
|
|
134
|
+
a: Promise.resolve(1),
|
|
135
|
+
b: Promise.resolve(2),
|
|
136
|
+
c: Promise.resolve(3),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(result).toStrictEqual({
|
|
140
|
+
a: 1,
|
|
141
|
+
b: 2,
|
|
142
|
+
c: 3,
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('rejects if any promise in the hash rejects', async() => {
|
|
147
|
+
await expect(
|
|
148
|
+
allHash({
|
|
149
|
+
a: Promise.resolve(1),
|
|
150
|
+
b: Promise.reject(new Error('fail')),
|
|
151
|
+
})
|
|
152
|
+
).rejects.toThrow('fail');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('returns an empty object for an empty hash', async() => {
|
|
156
|
+
const result = await allHash({});
|
|
157
|
+
|
|
158
|
+
expect(result).toStrictEqual({});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('preserves key ordering from the input hash', async() => {
|
|
162
|
+
const result = await allHash({
|
|
163
|
+
z: Promise.resolve('z-val'),
|
|
164
|
+
a: Promise.resolve('a-val'),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(Object.keys(result)).toStrictEqual(['z', 'a']);
|
|
168
|
+
expect(result).toStrictEqual({ z: 'z-val', a: 'a-val' });
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('resolves non-promise values in the hash', async() => {
|
|
172
|
+
const result = await allHash({ x: 42 });
|
|
173
|
+
|
|
174
|
+
expect(result).toStrictEqual({ x: 42 });
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('allHashSettled', () => {
|
|
179
|
+
it('resolves with fulfilled/rejected statuses for each key', async() => {
|
|
180
|
+
const result = await allHashSettled({
|
|
181
|
+
ok: Promise.resolve('success'),
|
|
182
|
+
err: Promise.reject(new Error('oops')),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(result.ok).toStrictEqual({ status: 'fulfilled', value: 'success' });
|
|
186
|
+
expect(result.err.status).toStrictEqual('rejected');
|
|
187
|
+
expect((result.err as PromiseRejectedResult).reason.message).toStrictEqual('oops');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('returns an empty object for an empty hash', async() => {
|
|
191
|
+
const result = await allHashSettled({});
|
|
192
|
+
|
|
193
|
+
expect(result).toStrictEqual({});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('never rejects — always resolves even when all promises fail', async() => {
|
|
197
|
+
await expect(
|
|
198
|
+
allHashSettled({
|
|
199
|
+
a: Promise.reject(new Error('a-fail')),
|
|
200
|
+
b: Promise.reject(new Error('b-fail')),
|
|
201
|
+
})
|
|
202
|
+
).resolves.toBeDefined();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('eachLimit', () => {
|
|
207
|
+
it('processes all items and returns results in index order', async() => {
|
|
208
|
+
const items = [1, 2, 3, 4, 5];
|
|
209
|
+
const result = await eachLimit(items, 2, (item: number) => Promise.resolve(item * 10));
|
|
210
|
+
|
|
211
|
+
expect(result).toStrictEqual([10, 20, 30, 40, 50]);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('returns an empty array for empty input', async() => {
|
|
215
|
+
const result = await eachLimit([], 3, () => Promise.resolve('x'));
|
|
216
|
+
|
|
217
|
+
expect(result).toStrictEqual([]);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('rejects if any iterator rejects', async() => {
|
|
221
|
+
await expect(
|
|
222
|
+
eachLimit([1, 2, 3], 2, (item: number) => {
|
|
223
|
+
if (item === 2) {
|
|
224
|
+
return Promise.reject(new Error('item-2-failed'));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return Promise.resolve(item);
|
|
228
|
+
})
|
|
229
|
+
).rejects.toThrow('item-2-failed');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('handles limit larger than the number of items', async() => {
|
|
233
|
+
const items = ['a', 'b'];
|
|
234
|
+
const result = await eachLimit(items, 100, (item: string) => Promise.resolve(`done-${ item }`));
|
|
235
|
+
|
|
236
|
+
expect(result).toStrictEqual(['done-a', 'done-b']);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('handles limit of 1 (sequential processing)', async() => {
|
|
240
|
+
const order: number[] = [];
|
|
241
|
+
const items = [1, 2, 3];
|
|
242
|
+
|
|
243
|
+
await eachLimit(items, 1, (item: number) => {
|
|
244
|
+
order.push(item);
|
|
245
|
+
|
|
246
|
+
return Promise.resolve(item);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expect(order).toStrictEqual([1, 2, 3]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('respects the concurrency limit', async() => {
|
|
253
|
+
let concurrent = 0;
|
|
254
|
+
let maxConcurrent = 0;
|
|
255
|
+
const limit = 2;
|
|
256
|
+
const items = [1, 2, 3, 4, 5];
|
|
257
|
+
|
|
258
|
+
await eachLimit(items, limit, (item: number) => {
|
|
259
|
+
concurrent++;
|
|
260
|
+
if (concurrent > maxConcurrent) {
|
|
261
|
+
maxConcurrent = concurrent;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return new Promise<number>((resolve) => {
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
concurrent--;
|
|
267
|
+
resolve(item);
|
|
268
|
+
}, 5);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
expect(maxConcurrent).toStrictEqual(limit);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('passes the item index as the second argument to the iterator', async() => {
|
|
276
|
+
const indices: number[] = [];
|
|
277
|
+
|
|
278
|
+
await eachLimit(['x', 'y', 'z'], 3, (_item: string, idx: number) => {
|
|
279
|
+
indices.push(idx);
|
|
280
|
+
|
|
281
|
+
return Promise.resolve(idx);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(indices).toStrictEqual([0, 1, 2]);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe('deferred', () => {
|
|
289
|
+
it('returns an object with promise, resolve, and reject', () => {
|
|
290
|
+
const d = deferred('test');
|
|
291
|
+
|
|
292
|
+
expect(d.promise).toBeInstanceOf(Promise);
|
|
293
|
+
expect(typeof d.resolve).toStrictEqual('function');
|
|
294
|
+
expect(typeof d.reject).toStrictEqual('function');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('resolves the promise when resolve is called', async() => {
|
|
298
|
+
const d = deferred('resolve-test');
|
|
299
|
+
|
|
300
|
+
d.resolve('resolved-value');
|
|
301
|
+
|
|
302
|
+
await expect(d.promise).resolves.toStrictEqual('resolved-value');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('rejects the promise when reject is called', async() => {
|
|
306
|
+
const d = deferred('reject-test');
|
|
307
|
+
|
|
308
|
+
d.reject(new Error('deferred-error'));
|
|
309
|
+
|
|
310
|
+
await expect(d.promise).rejects.toThrow('deferred-error');
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('setPromiseResult', () => {
|
|
315
|
+
it('sets the resolved value on the target object property', async() => {
|
|
316
|
+
const obj: { result?: string } = {};
|
|
317
|
+
|
|
318
|
+
setPromiseResult(Promise.resolve('hello'), obj, 'result', 'test label');
|
|
319
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
320
|
+
|
|
321
|
+
expect(obj.result).toStrictEqual('hello');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('does not throw when the promise rejects — logs a warning instead', async() => {
|
|
325
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
326
|
+
const obj: { value?: string } = {};
|
|
327
|
+
|
|
328
|
+
setPromiseResult(Promise.reject(new Error('boom')), obj, 'value', 'failing label');
|
|
329
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
330
|
+
|
|
331
|
+
expect(obj.value).toBeUndefined();
|
|
332
|
+
expect(warnSpy).toHaveBeenCalledWith('Failed to: ', 'failing label', expect.any(Error));
|
|
333
|
+
warnSpy.mockRestore();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('leaves the object property unchanged when the promise rejects', async() => {
|
|
337
|
+
jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
338
|
+
const obj: { data: string } = { data: 'original' };
|
|
339
|
+
|
|
340
|
+
setPromiseResult(Promise.reject(new Error('err')), obj, 'data', 'test');
|
|
341
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
342
|
+
|
|
343
|
+
expect(obj.data).toStrictEqual('original');
|
|
344
|
+
jest.restoreAllMocks();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { getPerformanceSetting, isProviderEnabled } from '../settings';
|
|
2
|
+
import { DEFAULT_PERF_SETTING, SETTING } from '@shell/config/settings';
|
|
3
|
+
import { MANAGEMENT } from '@shell/config/types';
|
|
4
|
+
import { ClusterProvisionerContext } from '@shell/core/types';
|
|
5
|
+
|
|
6
|
+
const makeMockRootGetters = (settingValue?: string) => {
|
|
7
|
+
return {
|
|
8
|
+
'management/byId': (type: string, id: string) => {
|
|
9
|
+
if (type === MANAGEMENT.SETTING && id === SETTING.UI_PERFORMANCE) {
|
|
10
|
+
return settingValue !== undefined ? { value: settingValue } : undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return undefined;
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const makeMockContext = (settingValue?: string): ClusterProvisionerContext => {
|
|
19
|
+
return {
|
|
20
|
+
getters: {
|
|
21
|
+
'management/byId': (type: string, id: string) => {
|
|
22
|
+
if (type === MANAGEMENT.SETTING && id === SETTING.KEV2_OPERATORS) {
|
|
23
|
+
return settingValue !== undefined ? { value: settingValue } : undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return undefined;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
dispatch: {},
|
|
30
|
+
axios: {},
|
|
31
|
+
$plugin: {},
|
|
32
|
+
$extension: {},
|
|
33
|
+
} as unknown as ClusterProvisionerContext;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe('getPerformanceSetting', () => {
|
|
37
|
+
it('returns DEFAULT_PERF_SETTING when no setting resource exists', () => {
|
|
38
|
+
const rootGetters = makeMockRootGetters();
|
|
39
|
+
|
|
40
|
+
const result = getPerformanceSetting(rootGetters);
|
|
41
|
+
|
|
42
|
+
expect(result).toStrictEqual(DEFAULT_PERF_SETTING);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('returns DEFAULT_PERF_SETTING when setting value is empty string', () => {
|
|
46
|
+
const rootGetters = makeMockRootGetters('');
|
|
47
|
+
|
|
48
|
+
const result = getPerformanceSetting(rootGetters);
|
|
49
|
+
|
|
50
|
+
expect(result).toStrictEqual(DEFAULT_PERF_SETTING);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('merges valid JSON setting value with defaults', () => {
|
|
54
|
+
const customSetting = { inactivity: { enabled: true, threshold: 300 } };
|
|
55
|
+
const rootGetters = makeMockRootGetters(JSON.stringify(customSetting));
|
|
56
|
+
|
|
57
|
+
const result = getPerformanceSetting(rootGetters);
|
|
58
|
+
|
|
59
|
+
expect(result.inactivity).toStrictEqual({ enabled: true, threshold: 300 });
|
|
60
|
+
// Other defaults are preserved
|
|
61
|
+
expect(result.incrementalLoading).toStrictEqual(DEFAULT_PERF_SETTING.incrementalLoading);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('returns DEFAULT_PERF_SETTING when setting value is invalid JSON', () => {
|
|
65
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
66
|
+
const rootGetters = makeMockRootGetters('not-valid-json{{{');
|
|
67
|
+
|
|
68
|
+
const result = getPerformanceSetting(rootGetters);
|
|
69
|
+
|
|
70
|
+
expect(result).toStrictEqual(DEFAULT_PERF_SETTING);
|
|
71
|
+
consoleSpy.mockRestore();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('warns when setting value is invalid JSON', () => {
|
|
75
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
76
|
+
const rootGetters = makeMockRootGetters('bad json');
|
|
77
|
+
|
|
78
|
+
getPerformanceSetting(rootGetters);
|
|
79
|
+
|
|
80
|
+
expect(consoleSpy).toHaveBeenCalledWith('ui-performance setting contains invalid data');
|
|
81
|
+
consoleSpy.mockRestore();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('applies partial overrides without losing other defaults', () => {
|
|
85
|
+
const customSetting = { manualRefresh: { enabled: true, threshold: 999 } };
|
|
86
|
+
const rootGetters = makeMockRootGetters(JSON.stringify(customSetting));
|
|
87
|
+
|
|
88
|
+
const result = getPerformanceSetting(rootGetters);
|
|
89
|
+
|
|
90
|
+
expect(result.manualRefresh).toStrictEqual({ enabled: true, threshold: 999 });
|
|
91
|
+
expect(result.inactivity).toStrictEqual(DEFAULT_PERF_SETTING.inactivity);
|
|
92
|
+
expect(result.disableWebsocketNotification).toStrictEqual(DEFAULT_PERF_SETTING.disableWebsocketNotification);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('isProviderEnabled', () => {
|
|
97
|
+
it('returns true when no providers setting exists (default enabled)', () => {
|
|
98
|
+
const context = makeMockContext();
|
|
99
|
+
|
|
100
|
+
expect(isProviderEnabled(context, 'someProvider')).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('returns true when providers list is empty', () => {
|
|
104
|
+
const context = makeMockContext(JSON.stringify([]));
|
|
105
|
+
|
|
106
|
+
expect(isProviderEnabled(context, 'someProvider')).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('returns true when provider is not in the list', () => {
|
|
110
|
+
const providers = [{ name: 'otherProvider', active: false }];
|
|
111
|
+
const context = makeMockContext(JSON.stringify(providers));
|
|
112
|
+
|
|
113
|
+
expect(isProviderEnabled(context, 'someProvider')).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it.each([
|
|
117
|
+
{
|
|
118
|
+
provider: 'eks', providers: [{ name: 'eks', active: true }], expected: true
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
provider: 'eks', providers: [{ name: 'eks', active: false }], expected: false
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
provider: 'gke', providers: [{ name: 'eks', active: true }, { name: 'gke', active: false }, { name: 'aks', active: true }], expected: false
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
provider: 'aks', providers: [{ name: 'eks', active: true }, { name: 'gke', active: false }, { name: 'aks', active: true }], expected: true
|
|
128
|
+
},
|
|
129
|
+
])('returns $expected for provider "$provider"', ({ provider, providers, expected }) => {
|
|
130
|
+
const context = makeMockContext(JSON.stringify(providers));
|
|
131
|
+
|
|
132
|
+
expect(isProviderEnabled(context, provider)).toBe(expected);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('throws when setting value is invalid JSON', () => {
|
|
136
|
+
const context = makeMockContext('not-valid-json{{{');
|
|
137
|
+
|
|
138
|
+
expect(() => isProviderEnabled(context, 'eks')).toThrow('Unexpected token');
|
|
139
|
+
});
|
|
140
|
+
});
|