@rancher/shell 3.0.7 → 3.0.8-rc.2
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/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/global/_layout.scss +21 -35
- package/assets/styles/themes/_modern.scss +5 -5
- package/assets/translations/en-us.yaml +102 -17
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/EmberPage.vue +1 -1
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/Resource/Detail/CopyToClipboard.vue +1 -1
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
- package/components/Resource/Detail/TitleBar/index.vue +10 -6
- package/components/ResourceDetail/index.vue +4 -1
- package/components/SortableTable/index.vue +18 -2
- package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
- package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
- package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
- package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Header.vue +34 -13
- package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
- package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
- package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
- package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
- package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
- package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
- package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
- package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
- package/components/nav/WindowManager/constants.ts +23 -0
- package/components/nav/WindowManager/index.vue +61 -575
- package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
- package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
- package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
- package/components/templates/default.vue +4 -40
- package/components/templates/home.vue +31 -5
- package/config/product/auth.js +1 -0
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/store.js +4 -2
- package/config/types.js +2 -0
- package/detail/pod.vue +1 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/directives/ui-context.ts +97 -0
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/initialize/install-directives.js +2 -0
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/chart.js +1 -1
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/home.vue +313 -12
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +17 -2
- package/plugins/steve/steve-pagination-utils.ts +2 -2
- package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
- package/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +9 -3
- package/store/prefs.js +6 -0
- package/store/ui-context.ts +86 -0
- package/store/wm.ts +244 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +59 -0
- package/types/window-manager.ts +22 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +263 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/dynamic-importer.js +2 -2
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +19 -4
- package/utils/release-notes.ts +1 -1
- package/assets/images/icons/document.svg +0 -3
- package/store/wm.js +0 -95
- /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
- /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
- /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
- /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
import { CONFIG_MAP } from '@shell/config/types';
|
|
5
|
+
import { PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
6
|
+
import ResourceLabeledSelect from '@shell/components/form/ResourceLabeledSelect.vue';
|
|
7
|
+
|
|
8
|
+
interface ConfigMap {
|
|
9
|
+
id?: string;
|
|
10
|
+
name: string;
|
|
11
|
+
namespace: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
value: {
|
|
16
|
+
type: Object,
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
namespace: {
|
|
20
|
+
type: String,
|
|
21
|
+
required: true,
|
|
22
|
+
},
|
|
23
|
+
inStore: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: 'management',
|
|
26
|
+
},
|
|
27
|
+
mode: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: _EDIT
|
|
30
|
+
},
|
|
31
|
+
label: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: '',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const emit = defineEmits(['update:value']);
|
|
38
|
+
|
|
39
|
+
const configMaps = ref<ConfigMap[]>([]);
|
|
40
|
+
|
|
41
|
+
const allConfigMapsSettings = {
|
|
42
|
+
updateResources: (configMapsList: ConfigMap[]) => {
|
|
43
|
+
const allConfigMapsInNamespace = configMapsList.filter((configMap) => configMap.namespace === props.namespace);
|
|
44
|
+
const mappedConfigMaps = mapConfigMaps(allConfigMapsInNamespace.sort((a, b) => a.name.localeCompare(b.name)));
|
|
45
|
+
|
|
46
|
+
configMaps.value = allConfigMapsInNamespace;
|
|
47
|
+
|
|
48
|
+
return mappedConfigMaps;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const paginateConfigMapsSetting = {
|
|
53
|
+
requestSettings: paginatePageOptions,
|
|
54
|
+
updateResources: (configMapsList: ConfigMap[]) => {
|
|
55
|
+
const mappedConfigMaps = mapConfigMaps(configMapsList);
|
|
56
|
+
|
|
57
|
+
configMaps.value = configMapsList;
|
|
58
|
+
|
|
59
|
+
return mappedConfigMaps;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function mapConfigMaps(configMapsList: ConfigMap[]) {
|
|
64
|
+
return configMapsList.reduce<{ label: string; value: string }[]>((res, c) => {
|
|
65
|
+
if (c.id) {
|
|
66
|
+
res.push({ label: c.name, value: c.name });
|
|
67
|
+
} else {
|
|
68
|
+
res.push(c as any);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return res;
|
|
72
|
+
}, []);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function paginatePageOptions(opts: any) {
|
|
76
|
+
const { opts: { filter } } = opts;
|
|
77
|
+
|
|
78
|
+
const filters = !!filter ? [PaginationParamFilter.createSingleField({
|
|
79
|
+
field: 'metadata.name', value: filter, exact: false, equals: true
|
|
80
|
+
})] : [];
|
|
81
|
+
|
|
82
|
+
filters.push(
|
|
83
|
+
PaginationParamFilter.createSingleField({ field: 'metadata.namespace', value: props.namespace }),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
...opts,
|
|
88
|
+
filters,
|
|
89
|
+
groupByNamespace: false,
|
|
90
|
+
classify: true,
|
|
91
|
+
sort: [{ asc: true, field: 'metadata.name' }],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function update(value: any) {
|
|
96
|
+
emit('update:value', value);
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<template>
|
|
101
|
+
<ResourceLabeledSelect
|
|
102
|
+
:key="namespace"
|
|
103
|
+
:value="value"
|
|
104
|
+
:label="label || t('fleet.configMaps.label')"
|
|
105
|
+
:mode="mode"
|
|
106
|
+
:resource-type="CONFIG_MAP"
|
|
107
|
+
:loading="$fetchState.pending"
|
|
108
|
+
:in-store="inStore"
|
|
109
|
+
:paginated-resource-settings="paginateConfigMapsSetting"
|
|
110
|
+
:all-resources-settings="allConfigMapsSettings"
|
|
111
|
+
:multiple="true"
|
|
112
|
+
@update:value="update"
|
|
113
|
+
/>
|
|
114
|
+
</template>
|
|
115
|
+
|
|
116
|
+
<style lang="scss" scoped>
|
|
117
|
+
</style>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, computed, defineProps, defineEmits } from 'vue';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
import { TYPES } from '@shell/models/secret';
|
|
5
|
+
import { SECRET } from '@shell/config/types';
|
|
6
|
+
import { PaginationParamFilter } from '@shell/types/store/pagination.types';
|
|
7
|
+
import ResourceLabeledSelect from '@shell/components/form/ResourceLabeledSelect.vue';
|
|
8
|
+
|
|
9
|
+
interface Secret {
|
|
10
|
+
id?: string;
|
|
11
|
+
name: string;
|
|
12
|
+
namespace: string;
|
|
13
|
+
_type: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
value: {
|
|
18
|
+
type: Object,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
namespace: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
inStore: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: 'management',
|
|
28
|
+
},
|
|
29
|
+
mode: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: _EDIT
|
|
32
|
+
},
|
|
33
|
+
label: {
|
|
34
|
+
type: String,
|
|
35
|
+
default: '',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const emit = defineEmits(['update:value']);
|
|
40
|
+
|
|
41
|
+
const types = computed<string[]>(() => Object.values(TYPES));
|
|
42
|
+
|
|
43
|
+
const secrets = ref<Secret[]>([]);
|
|
44
|
+
|
|
45
|
+
const allSecretsSettings = {
|
|
46
|
+
updateResources: (secretsList: Secret[]) => {
|
|
47
|
+
const allSecretsInNamespace = secretsList.filter((secret) => types.value.includes(secret._type) && secret.namespace === props.namespace);
|
|
48
|
+
const mappedSecrets = mapSecrets(allSecretsInNamespace.sort((a, b) => a.name.localeCompare(b.name)));
|
|
49
|
+
|
|
50
|
+
secrets.value = allSecretsInNamespace;
|
|
51
|
+
|
|
52
|
+
return mappedSecrets;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const paginateSecretsSetting = {
|
|
57
|
+
requestSettings: paginatePageOptions,
|
|
58
|
+
updateResources: (secretsList: Secret[]) => {
|
|
59
|
+
const mappedSecrets = mapSecrets(secretsList);
|
|
60
|
+
|
|
61
|
+
secrets.value = secretsList;
|
|
62
|
+
|
|
63
|
+
return mappedSecrets;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function mapSecrets(secretsList: Secret[]) {
|
|
68
|
+
return secretsList.reduce<{ label: string; value: string }[]>((res, s) => {
|
|
69
|
+
if (s.id) {
|
|
70
|
+
res.push({ label: s.name, value: s.name });
|
|
71
|
+
} else {
|
|
72
|
+
res.push(s as any);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return res;
|
|
76
|
+
}, []);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function update(value: any) {
|
|
80
|
+
emit('update:value', value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function paginatePageOptions(opts: any) {
|
|
84
|
+
const { opts: { filter } } = opts;
|
|
85
|
+
|
|
86
|
+
const filters = !!filter ? [PaginationParamFilter.createSingleField({
|
|
87
|
+
field: 'metadata.name', value: filter, exact: false, equals: true
|
|
88
|
+
})] : [];
|
|
89
|
+
|
|
90
|
+
filters.push(
|
|
91
|
+
PaginationParamFilter.createSingleField({ field: 'metadata.namespace', value: props.namespace }),
|
|
92
|
+
PaginationParamFilter.createMultipleFields(types.value.map((t) => ({
|
|
93
|
+
field: 'metadata.fields.1',
|
|
94
|
+
equals: true,
|
|
95
|
+
exact: true,
|
|
96
|
+
value: t
|
|
97
|
+
})))
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...opts,
|
|
102
|
+
filters,
|
|
103
|
+
groupByNamespace: false,
|
|
104
|
+
classify: true,
|
|
105
|
+
sort: [{ asc: true, field: 'metadata.name' }],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<template>
|
|
111
|
+
<ResourceLabeledSelect
|
|
112
|
+
:key="namespace"
|
|
113
|
+
:value="value"
|
|
114
|
+
:label="label || t('fleet.secrets.label')"
|
|
115
|
+
:mode="mode"
|
|
116
|
+
:resource-type="SECRET"
|
|
117
|
+
:loading="$fetchState.pending"
|
|
118
|
+
:in-store="inStore"
|
|
119
|
+
:paginated-resource-settings="paginateSecretsSetting"
|
|
120
|
+
:all-resources-settings="allSecretsSettings"
|
|
121
|
+
:multiple="true"
|
|
122
|
+
@update:value="update"
|
|
123
|
+
/>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<style lang="scss" scoped>
|
|
127
|
+
</style>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
3
|
+
import FleetConfigMapSelector from '@shell/components/fleet/FleetConfigMapSelector.vue';
|
|
4
|
+
import ResourceLabeledSelect from '@shell/components/form/ResourceLabeledSelect.vue';
|
|
5
|
+
|
|
6
|
+
describe('fleetConfigMapSelector.vue', () => {
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
value: {},
|
|
9
|
+
namespace: 'fleet-default',
|
|
10
|
+
inStore: 'management',
|
|
11
|
+
mode: _EDIT,
|
|
12
|
+
label: 'Config Map',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const global = {
|
|
16
|
+
stubs: { ResourceLabeledSelect },
|
|
17
|
+
mocks: {
|
|
18
|
+
$fetchState: { pending: false },
|
|
19
|
+
$store: { getters: { 'management/all': () => [] } },
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
it('should emit update:value when update is called', async() => {
|
|
24
|
+
const wrapper = shallowMount(FleetConfigMapSelector, {
|
|
25
|
+
props: defaultProps,
|
|
26
|
+
global,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const vm = wrapper.vm as any;
|
|
30
|
+
|
|
31
|
+
await vm.update('cm1');
|
|
32
|
+
|
|
33
|
+
expect(wrapper.emitted('update:value')).toBeTruthy();
|
|
34
|
+
expect(wrapper.emitted('update:value')?.[0]).toStrictEqual(['cm1']);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should correctly map configMaps', () => {
|
|
38
|
+
const wrapper = shallowMount(FleetConfigMapSelector, {
|
|
39
|
+
props: defaultProps,
|
|
40
|
+
global
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const configMapsList = [
|
|
44
|
+
{
|
|
45
|
+
id: '1', name: 'cm1', namespace: 'fleet-default'
|
|
46
|
+
},
|
|
47
|
+
{ name: 'cm2', namespace: 'fleet-default' }
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const vm = wrapper.vm as any;
|
|
51
|
+
const result = vm.mapConfigMaps(configMapsList);
|
|
52
|
+
|
|
53
|
+
expect(result).toStrictEqual([
|
|
54
|
+
{ label: 'cm1', value: 'cm1' },
|
|
55
|
+
{ name: 'cm2', namespace: 'fleet-default' }
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should return correct filter options from paginatePageOptions', () => {
|
|
60
|
+
const wrapper = shallowMount(FleetConfigMapSelector, {
|
|
61
|
+
props: defaultProps,
|
|
62
|
+
global
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const opts = { opts: { filter: 'test' } };
|
|
66
|
+
|
|
67
|
+
const vm = wrapper.vm as any;
|
|
68
|
+
const result = vm.paginatePageOptions(opts);
|
|
69
|
+
|
|
70
|
+
expect(result.filters).toHaveLength(2);
|
|
71
|
+
expect(result.groupByNamespace).toStrictEqual(false);
|
|
72
|
+
expect(result.classify).toStrictEqual(true);
|
|
73
|
+
expect(result.sort).toStrictEqual([{ asc: true, field: 'metadata.name' }]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should correctly filter and map configMaps in allConfigMapsSettings.updateResources', () => {
|
|
77
|
+
const wrapper = shallowMount(FleetConfigMapSelector, {
|
|
78
|
+
props: defaultProps,
|
|
79
|
+
global
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const configMapsList = [
|
|
83
|
+
{
|
|
84
|
+
id: '1', name: 'cm1', namespace: 'fleet-default'
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: '2', name: 'cm2', namespace: 'other'
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const vm = wrapper.vm as any;
|
|
92
|
+
|
|
93
|
+
const result = vm.allConfigMapsSettings.updateResources(configMapsList);
|
|
94
|
+
|
|
95
|
+
expect(result).toStrictEqual([{ label: 'cm1', value: 'cm1' }]);
|
|
96
|
+
expect(vm.configMaps).toStrictEqual([{
|
|
97
|
+
id: '1', name: 'cm1', namespace: 'fleet-default'
|
|
98
|
+
}]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should correctly map configMaps in paginateConfigMapsSetting.updateResources', () => {
|
|
102
|
+
const wrapper = shallowMount(FleetConfigMapSelector, {
|
|
103
|
+
props: defaultProps,
|
|
104
|
+
global
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const configMapsList = [
|
|
108
|
+
{
|
|
109
|
+
id: '1', name: 'cm1', namespace: 'fleet-default'
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: '2', name: 'cm2', namespace: 'fleet-default'
|
|
113
|
+
}
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const vm = wrapper.vm as any;
|
|
117
|
+
const result = vm.paginateConfigMapsSetting.updateResources(configMapsList);
|
|
118
|
+
|
|
119
|
+
expect(result).toStrictEqual([
|
|
120
|
+
{ label: 'cm1', value: 'cm1' },
|
|
121
|
+
{ label: 'cm2', value: 'cm2' }
|
|
122
|
+
]);
|
|
123
|
+
expect(vm.configMaps).toStrictEqual(configMapsList);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import FleetSecretSelector from '@shell/components/fleet/FleetSecretSelector.vue';
|
|
3
|
+
import { _EDIT } from '@shell/config/query-params';
|
|
4
|
+
|
|
5
|
+
describe('component: FleetSecretSelector.vue', () => {
|
|
6
|
+
const secretsMock = [
|
|
7
|
+
{
|
|
8
|
+
id: '1', name: 'secret1', namespace: 'fleet-default', _type: 'Opaque'
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: '2', name: 'secret2', namespace: 'fleet-default', _type: 'Opaque'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: '3', name: 'secret3', namespace: 'other', _type: 'Opaque'
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const props = {
|
|
19
|
+
value: {},
|
|
20
|
+
namespace: 'fleet-default',
|
|
21
|
+
inStore: 'management',
|
|
22
|
+
mode: _EDIT,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const global = { mocks: { $fetchState: { pending: false, error: false } } };
|
|
26
|
+
|
|
27
|
+
it('should emit update:value when update is called', async() => {
|
|
28
|
+
const wrapper = shallowMount(FleetSecretSelector, { props, global });
|
|
29
|
+
|
|
30
|
+
await (wrapper.vm as any).update('secret1');
|
|
31
|
+
|
|
32
|
+
expect(wrapper.emitted('update:value')).toBeTruthy();
|
|
33
|
+
expect(wrapper.emitted('update:value')?.[0]).toStrictEqual(['secret1']);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should correctly map secrets', () => {
|
|
37
|
+
const wrapper = shallowMount(FleetSecretSelector, { props, global });
|
|
38
|
+
const mapped = (wrapper.vm as any).mapSecrets(secretsMock);
|
|
39
|
+
|
|
40
|
+
expect(mapped).toStrictEqual([
|
|
41
|
+
{ label: 'secret1', value: 'secret1' },
|
|
42
|
+
{ label: 'secret2', value: 'secret2' },
|
|
43
|
+
{ label: 'secret3', value: 'secret3' }
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should filter and sort secrets by namespace and type', () => {
|
|
48
|
+
const wrapper = shallowMount(FleetSecretSelector, { props, global });
|
|
49
|
+
|
|
50
|
+
const result = (wrapper.vm as any).allSecretsSettings.updateResources(secretsMock);
|
|
51
|
+
|
|
52
|
+
expect(result).toStrictEqual([
|
|
53
|
+
{ label: 'secret1', value: 'secret1' },
|
|
54
|
+
{ label: 'secret2', value: 'secret2' }
|
|
55
|
+
]);
|
|
56
|
+
expect((wrapper.vm as any).secrets).toStrictEqual([
|
|
57
|
+
{
|
|
58
|
+
id: '1', name: 'secret1', namespace: 'fleet-default', _type: 'Opaque'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: '2', name: 'secret2', namespace: 'fleet-default', _type: 'Opaque'
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return correct filter structure from paginatePageOptions', () => {
|
|
67
|
+
const wrapper = shallowMount(FleetSecretSelector, { props, global });
|
|
68
|
+
|
|
69
|
+
const opts = { opts: { filter: 'secret' } };
|
|
70
|
+
const result = (wrapper.vm as any).paginatePageOptions(opts);
|
|
71
|
+
|
|
72
|
+
expect(result.filters).toStrictEqual(
|
|
73
|
+
expect.arrayContaining([
|
|
74
|
+
expect.objectContaining({ fields: expect.arrayContaining([expect.objectContaining({ field: 'metadata.name' })]) }),
|
|
75
|
+
expect.objectContaining({ fields: expect.arrayContaining([expect.objectContaining({ field: 'metadata.namespace' })]) }),
|
|
76
|
+
expect.objectContaining({ fields: expect.arrayContaining([expect.objectContaining({ field: 'metadata.fields.1' })]) }),
|
|
77
|
+
])
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(result.sort).toStrictEqual([{ asc: true, field: 'metadata.name' }]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -7,7 +7,8 @@ export default {
|
|
|
7
7
|
emits: ['update:value', 'error'],
|
|
8
8
|
|
|
9
9
|
components: { FileSelector, LazyImage },
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
props: {
|
|
11
12
|
value: {
|
|
12
13
|
type: String,
|
|
13
14
|
default: null,
|
|
@@ -36,12 +37,20 @@ export default {
|
|
|
36
37
|
accept: {
|
|
37
38
|
type: String,
|
|
38
39
|
default: 'image/*'
|
|
39
|
-
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
class: {
|
|
43
|
+
type: [String, Array],
|
|
44
|
+
default: 'role-primary',
|
|
45
|
+
},
|
|
40
46
|
},
|
|
41
47
|
computed: {
|
|
42
48
|
isView() {
|
|
43
49
|
return this.mode === _VIEW;
|
|
44
|
-
}
|
|
50
|
+
},
|
|
51
|
+
customClass() {
|
|
52
|
+
return [...(Array.isArray(this.class) ? this.class : [this.class])];
|
|
53
|
+
},
|
|
45
54
|
},
|
|
46
55
|
methods: {
|
|
47
56
|
/**
|
|
@@ -62,7 +71,7 @@ export default {
|
|
|
62
71
|
<FileSelector
|
|
63
72
|
v-if="!value && !isView"
|
|
64
73
|
:value="value"
|
|
65
|
-
class="
|
|
74
|
+
:class="customClass"
|
|
66
75
|
:mode="mode"
|
|
67
76
|
:read-as-data-url="true"
|
|
68
77
|
:byte-limit="byteLimit"
|
|
@@ -67,12 +67,21 @@ export default {
|
|
|
67
67
|
default: '*'
|
|
68
68
|
},
|
|
69
69
|
|
|
70
|
+
class: {
|
|
71
|
+
type: [String, Array],
|
|
72
|
+
default: () => [],
|
|
73
|
+
}
|
|
74
|
+
|
|
70
75
|
},
|
|
71
76
|
|
|
72
77
|
computed: {
|
|
73
78
|
isView() {
|
|
74
79
|
return this.mode === _VIEW;
|
|
75
|
-
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
customClass() {
|
|
83
|
+
return ['file-selector', 'btn', ...(Array.isArray(this.class) ? this.class : [this.class])];
|
|
84
|
+
},
|
|
76
85
|
},
|
|
77
86
|
|
|
78
87
|
methods: {
|
|
@@ -151,7 +160,7 @@ export default {
|
|
|
151
160
|
:aria-label="label"
|
|
152
161
|
type="button"
|
|
153
162
|
role="button"
|
|
154
|
-
class="
|
|
163
|
+
:class="customClass"
|
|
155
164
|
data-testid="file-selector__uploader-button"
|
|
156
165
|
@click="selectFile"
|
|
157
166
|
>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import ResourceLabeledSelect from '@shell/components/form/ResourceLabeledSelect.vue';
|
|
3
|
+
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
|
|
4
|
+
import { RESOURCE_LABEL_SELECT_MODE } from '@shell/types/components/resourceLabeledSelect';
|
|
5
|
+
|
|
6
|
+
const mockStore = {
|
|
7
|
+
getters: {
|
|
8
|
+
currentStore: jest.fn().mockReturnValue('cluster'),
|
|
9
|
+
'cluster/paginationEnabled': jest.fn().mockReturnValue(true),
|
|
10
|
+
'cluster/all': jest.fn().mockReturnValue([{ id: 'foo', name: 'Foo' }]),
|
|
11
|
+
},
|
|
12
|
+
dispatch: jest.fn().mockResolvedValue(undefined),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const requiredSetup = () => {
|
|
16
|
+
return {
|
|
17
|
+
global: {
|
|
18
|
+
components: { LabeledSelect },
|
|
19
|
+
mocks: { $store: mockStore, $fetchState: {} }
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe('component: ResourceLabeledSelect.vue', () => {
|
|
25
|
+
it('should render LabeledSelect', async() => {
|
|
26
|
+
const wrapper = shallowMount(ResourceLabeledSelect, {
|
|
27
|
+
...requiredSetup(),
|
|
28
|
+
props: { resourceType: 'testResource' }
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(wrapper.findComponent(LabeledSelect).exists()).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should call paginateType with overrideRequest if provided', async() => {
|
|
35
|
+
const overrideRequest = jest.fn().mockResolvedValue({ page: [{ id: 'bar', name: 'Bar' }], total: 1 });
|
|
36
|
+
const wrapper = shallowMount(ResourceLabeledSelect, {
|
|
37
|
+
...requiredSetup(),
|
|
38
|
+
props: {
|
|
39
|
+
resourceType: 'testResource',
|
|
40
|
+
paginateMode: RESOURCE_LABEL_SELECT_MODE.DYNAMIC,
|
|
41
|
+
paginatedResourceSettings: { overrideRequest }
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const result = await wrapper.vm.paginateType({
|
|
46
|
+
filter: 'bar',
|
|
47
|
+
page: 1,
|
|
48
|
+
pageSize: 10,
|
|
49
|
+
pageContent: [],
|
|
50
|
+
resetPage: false
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(overrideRequest).toHaveBeenCalledWith({
|
|
54
|
+
filter: 'bar',
|
|
55
|
+
page: 1,
|
|
56
|
+
pageSize: 10,
|
|
57
|
+
pageContent: [],
|
|
58
|
+
resetPage: false
|
|
59
|
+
});
|
|
60
|
+
expect(result.page[0].name).toBe('Bar');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should emit update:value when LabeledSelect emits update:value', async() => {
|
|
64
|
+
const wrapper = shallowMount(ResourceLabeledSelect, {
|
|
65
|
+
...requiredSetup(),
|
|
66
|
+
props: { resourceType: 'testResource' }
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'baz');
|
|
70
|
+
|
|
71
|
+
expect(wrapper.emitted('update:value')).toBeTruthy();
|
|
72
|
+
expect(wrapper.emitted('update:value')?.[0]).toStrictEqual(['baz']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should pass correct props and attrs to LabeledSelect', async() => {
|
|
76
|
+
const wrapper = shallowMount(ResourceLabeledSelect, {
|
|
77
|
+
...requiredSetup(),
|
|
78
|
+
props: {
|
|
79
|
+
resourceType: 'testResource',
|
|
80
|
+
allResourcesSettings: { labelSelectOptions: { placeholder: 'Select a resource' } }
|
|
81
|
+
},
|
|
82
|
+
attrs: { multiple: true }
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const labeledSelect = wrapper.findComponent(LabeledSelect);
|
|
86
|
+
|
|
87
|
+
expect(labeledSelect.attributes('placeholder')).toBe('Select a resource');
|
|
88
|
+
expect(labeledSelect.attributes('multiple')).toBe('true');
|
|
89
|
+
});
|
|
90
|
+
});
|