@rancher/shell 3.0.9-rc.6 → 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.
Files changed (40) hide show
  1. package/components/IconOrSvg.vue +61 -42
  2. package/components/SortableTable/index.vue +2 -2
  3. package/components/form/BannerSettings.vue +2 -2
  4. package/components/form/NotificationSettings.vue +2 -2
  5. package/config/product/manager.js +0 -1
  6. package/detail/fleet.cattle.io.cluster.vue +1 -1
  7. package/dialog/FeatureFlagListDialog.vue +1 -1
  8. package/edit/catalog.cattle.io.clusterrepo.vue +1 -1
  9. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/auth.spec.ts +145 -0
  10. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/index.test.ts +202 -0
  11. package/edit/monitoring.coreos.com.alertmanagerconfig/__tests__/tls.spec.ts +226 -0
  12. package/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue +24 -21
  13. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/opsgenie.spec.ts +157 -0
  14. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/pagerduty.spec.ts +132 -0
  15. package/edit/monitoring.coreos.com.alertmanagerconfig/types/__tests__/slack.spec.ts +108 -0
  16. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +2 -1
  17. package/edit/monitoring.coreos.com.receiver/__tests__/auth.spec.ts +165 -0
  18. package/edit/monitoring.coreos.com.receiver/__tests__/index.test.ts +153 -0
  19. package/edit/monitoring.coreos.com.receiver/__tests__/tls.spec.ts +115 -0
  20. package/edit/monitoring.coreos.com.receiver/types/__tests__/email.spec.ts +86 -0
  21. package/edit/monitoring.coreos.com.receiver/types/__tests__/opsgenie.spec.ts +209 -0
  22. package/edit/monitoring.coreos.com.receiver/types/__tests__/pagerduty.spec.ts +105 -0
  23. package/edit/monitoring.coreos.com.receiver/types/__tests__/slack.spec.ts +92 -0
  24. package/edit/monitoring.coreos.com.receiver/types/__tests__/webhook.spec.ts +131 -0
  25. package/edit/provisioning.cattle.io.cluster/ingress/IngressCards.vue +14 -12
  26. package/edit/provisioning.cattle.io.cluster/rke2.vue +4 -5
  27. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +18 -3
  28. package/edit/provisioning.cattle.io.cluster/tabs/Ingress.vue +100 -76
  29. package/list/provisioning.cattle.io.cluster.vue +2 -2
  30. package/models/__tests__/chart.test.ts +2 -2
  31. package/models/chart.js +3 -3
  32. package/package.json +1 -1
  33. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +45 -18
  34. package/pages/c/_cluster/apps/charts/index.vue +1 -11
  35. package/pages/c/_cluster/explorer/tools/index.vue +1 -1
  36. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  37. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  38. package/rancher-components/RcItemCard/RcItemCard.vue +8 -1
  39. package/types/shell/index.d.ts +1 -0
  40. package/utils/svg-filter.js +4 -3
@@ -18,9 +18,9 @@
18
18
  */
19
19
  import { Solver } from '@shell/utils/svg-filter';
20
20
  import { colorToRgb, mapStandardColors, normalizeHex } from '@shell/utils/color';
21
+ import { mapGetters } from 'vuex';
21
22
 
22
23
  const filterCache = {};
23
- const cssCache = {};
24
24
 
25
25
  const colors = {
26
26
  header: {
@@ -33,7 +33,7 @@ const colors = {
33
33
  },
34
34
  primary: {
35
35
  color: '--on-tertiary',
36
- hover: '--link',
36
+ hover: '--tertiary-hover-app-bar',
37
37
  colorFallback: '--on-tertiary',
38
38
  hoverFallback: '--primary-hover-text',
39
39
  active: '--on-active',
@@ -63,7 +63,12 @@ export default {
63
63
  },
64
64
 
65
65
  data() {
66
- return { className: '' };
66
+ return {
67
+ className: '',
68
+ mainFilter: null,
69
+ hoverFilter: null,
70
+ activeFilter: null,
71
+ };
67
72
  },
68
73
 
69
74
  created() {
@@ -72,6 +77,18 @@ export default {
72
77
  }
73
78
  },
74
79
 
80
+ computed: {
81
+ ...mapGetters({
82
+ brand: 'management/brand',
83
+ theme: 'prefs/theme',
84
+ })
85
+ },
86
+
87
+ watch: {
88
+ brand: 'recomputeColor',
89
+ theme: 'recomputeColor',
90
+ },
91
+
75
92
  methods: {
76
93
  getComputedStyleFor(cssVar, fallback) {
77
94
  const value = window.getComputedStyle(document.body).getPropertyValue(cssVar).trim();
@@ -86,11 +103,11 @@ export default {
86
103
 
87
104
  const solver = new Solver(rgb);
88
105
  const res = solver.solve();
89
- const filter = res?.filter;
106
+ const filterVal = res?.filterVal;
90
107
 
91
- filterCache[cacheKey] = filter;
108
+ filterCache[cacheKey] = filterVal;
92
109
 
93
- return filter;
110
+ return filterVal;
94
111
  },
95
112
 
96
113
  setColor() {
@@ -111,43 +128,23 @@ export default {
111
128
 
112
129
  const className = `svg-icon-${ uiColorStr }-${ hoverColorStr }`;
113
130
 
114
- if (!cssCache[className]) {
115
- const hoverFilter = this.resolveColorFilter(hoverColor, hoverColorRGB);
116
- const mainFilter = this.resolveColorFilter(uiColor, uiColorRGB);
117
- const activeFilter = this.resolveColorFilter(activeColor, activeColorRGB);
118
-
119
- // Add stylesheet (added as global styles)
120
- const styles = `
121
- img.${ className } {
122
- ${ mainFilter };
123
- }
124
- img.${ className }:hover {
125
- ${ hoverFilter };
126
- }
127
- button:hover > img.${ className } {
128
- ${ hoverFilter };
129
- }
130
- li:hover > img.${ className } {
131
- ${ hoverFilter };
132
- }
133
- a.option:hover > img.${ className } {
134
- ${ hoverFilter };
135
- }
136
- a.option.active-menu-link > img.${ className } {
137
- ${ activeFilter };
138
- }
139
- `;
140
-
141
- const styleSheet = document.createElement('style');
142
-
143
- styleSheet.innerText = styles;
144
- document.head.appendChild(styleSheet);
145
-
146
- cssCache[className] = true;
147
- }
131
+ this.hoverFilter = this.resolveColorFilter(hoverColor, hoverColorRGB);
132
+ this.mainFilter = this.resolveColorFilter(uiColor, uiColorRGB);
133
+ this.activeFilter = this.resolveColorFilter(activeColor, activeColorRGB);
148
134
 
149
135
  this['className'] = className;
150
- }
136
+ },
137
+
138
+ recomputeColor() {
139
+ if (!this.src) {
140
+ return;
141
+ }
142
+
143
+ this.mainFilter = null;
144
+ this.hoverFilter = null;
145
+ this.activeFilter = null;
146
+ this.setColor();
147
+ },
151
148
  }
152
149
  };
153
150
  </script>
@@ -172,8 +169,30 @@ export default {
172
169
  </template>
173
170
 
174
171
  <style lang="scss" scoped>
175
- .svg-icon {
172
+ img.svg-icon {
173
+ filter: v-bind(mainFilter);
174
+ }
175
+
176
+ button:hover > img.svg-icon,
177
+ li:hover > img.svg-icon {
178
+ filter: v-bind(hoverFilter);
179
+ }
180
+
181
+ .side-menu .category div a > img.svg-icon {
176
182
  height: 24px;
177
183
  width: 24px;
184
+ filter: v-bind(mainFilter);
185
+ }
186
+
187
+ .side-menu .category div a:hover > img.svg-icon {
188
+ filter: v-bind(hoverFilter);
189
+ }
190
+
191
+ .side-menu .category div a.active-menu-link > img.svg-icon {
192
+ filter: v-bind(activeFilter);
193
+
194
+ &:hover {
195
+ filter: v-bind(activeFilter);
196
+ }
178
197
  }
179
198
  </style>
@@ -2099,13 +2099,13 @@ export default {
2099
2099
 
2100
2100
  $header-padding: 20px;
2101
2101
  .sub-header-row {
2102
- padding: 0 0 $header-padding / 2 0;
2102
+ padding: 0 0 calc($header-padding / 2) 0;
2103
2103
  }
2104
2104
 
2105
2105
  .fixed-header-actions {
2106
2106
  padding: 0 0 $header-padding 0;
2107
2107
  &.with-sub-header {
2108
- padding: 0 0 $header-padding / 4 0;
2108
+ padding: 0 0 calc($header-padding / 4) 0;
2109
2109
  }
2110
2110
 
2111
2111
  width: 100%;
@@ -9,7 +9,7 @@ import { ToggleSwitch } from '@components/Form/ToggleSwitch';
9
9
  import { TextAreaAutoGrow } from '@components/Form/TextArea';
10
10
  import { Banner } from '@components/Banner';
11
11
 
12
- export default ({
12
+ export default {
13
13
  name: 'BannerSettings',
14
14
 
15
15
  props: {
@@ -112,7 +112,7 @@ export default ({
112
112
  return this.bannerType === 'bannerConsent';
113
113
  }
114
114
  }
115
- });
115
+ };
116
116
  </script>
117
117
 
118
118
  <template>
@@ -3,7 +3,7 @@ import { LabeledInput } from '@components/Form/LabeledInput';
3
3
  import { Checkbox } from '@components/Form/Checkbox';
4
4
  import { _EDIT, _VIEW } from '@shell/config/query-params';
5
5
 
6
- export default ({
6
+ export default {
7
7
 
8
8
  name: 'NotificationSettings',
9
9
 
@@ -29,7 +29,7 @@ export default ({
29
29
  },
30
30
  },
31
31
 
32
- });
32
+ };
33
33
  </script>
34
34
 
35
35
  <template>
@@ -202,7 +202,6 @@ export function init(store) {
202
202
  ]);
203
203
 
204
204
  headers(EXT.KUBECONFIG, [
205
- STATE,
206
205
  {
207
206
  name: 'clusters',
208
207
  labelKey: 'tableHeaders.clusters',
@@ -6,7 +6,7 @@ import ResourceTabs from '@shell/components/form/ResourceTabs';
6
6
  import Tab from '@shell/components/Tabbed/Tab';
7
7
  import { MANAGEMENT, FLEET, SCHEMA } from '@shell/config/types';
8
8
  import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
9
- import { allHash } from 'utils/promise';
9
+ import { allHash } from '@shell/utils/promise';
10
10
 
11
11
  export default {
12
12
  name: 'FleetDetailCluster',
@@ -134,7 +134,7 @@ export default {
134
134
  this.waiting = false;
135
135
  this.close();
136
136
 
137
- await this.$store.dispatch('management/findAll', { type: this.resource, opt: { force: true } });
137
+ await this.$store.dispatch('management/findAll', { type: this.update.type, opt: { force: true } });
138
138
  }
139
139
  } catch (e) {}
140
140
 
@@ -228,7 +228,7 @@ export default {
228
228
  <template>
229
229
  <form>
230
230
  <h2>{{ t('catalog.repo.target.label') }}</h2>
231
- <div class="row mb-10">
231
+ <div class="row mb-20">
232
232
  <div class="col span-12 target-groups">
233
233
  <RcItemCard
234
234
  v-for="card in clusterRepoTargets"
@@ -0,0 +1,145 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import Auth from '@shell/edit/monitoring.coreos.com.alertmanagerconfig/auth.vue';
3
+ import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
4
+ import SimpleSecretSelector from '@shell/components/form/SimpleSecretSelector';
5
+
6
+ describe('component: Auth.vue', () => {
7
+ const defaultProps = {
8
+ mode: 'edit',
9
+ value: {},
10
+ namespace: 'test-namespace'
11
+ };
12
+
13
+ it('should render correctly with initial props', () => {
14
+ const wrapper = shallowMount(Auth, {
15
+ props: defaultProps,
16
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
17
+ });
18
+
19
+ expect(wrapper.find('h3').text()).toBe('%monitoringReceiver.auth.label%');
20
+ expect(wrapper.findComponent(LabeledSelect).exists()).toBe(true);
21
+ });
22
+
23
+ it('should initialize with basic auth type if present', () => {
24
+ const wrapper = shallowMount(Auth, {
25
+ props: { ...defaultProps, value: { basicAuth: { username: { name: 'test' } } } },
26
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
27
+ });
28
+
29
+ expect(wrapper.vm.authType).toBe('basicAuth');
30
+ expect(wrapper.findAllComponents(SimpleSecretSelector)).toHaveLength(2);
31
+ });
32
+
33
+ it('should initialize with bearer token auth type if present', () => {
34
+ const wrapper = shallowMount(Auth, {
35
+ props: { ...defaultProps, value: { bearerTokenSecret: { name: 'test' } } },
36
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
37
+ });
38
+
39
+ expect(wrapper.vm.authType).toBe('bearerTokenSecret');
40
+ expect(wrapper.findAllComponents(SimpleSecretSelector)).toHaveLength(1);
41
+ });
42
+
43
+ it('should switch to basic auth and clear other types', async() => {
44
+ const value = { bearerTokenSecret: { name: 'b', key: 'k' } };
45
+ const wrapper = shallowMount(Auth, {
46
+ props: { ...defaultProps, value },
47
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
48
+ });
49
+
50
+ await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'basicAuth');
51
+ expect(wrapper.vm.authType).toBe('basicAuth');
52
+ expect(wrapper.props('value').bearerTokenSecret).toBeUndefined();
53
+ expect(wrapper.props('value').basicAuth).toBeDefined();
54
+ });
55
+
56
+ it('should switch to bearer token and clear other types', async() => {
57
+ const value = { basicAuth: { username: { name: 'u' } } };
58
+ const wrapper = shallowMount(Auth, {
59
+ props: { ...defaultProps, value },
60
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
61
+ });
62
+
63
+ await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'bearerTokenSecret');
64
+ expect(wrapper.vm.authType).toBe('bearerTokenSecret');
65
+ expect(wrapper.props('value').basicAuth).toBeUndefined();
66
+ expect(wrapper.props('value').bearerTokenSecret).toBeDefined();
67
+ });
68
+
69
+ it('should switch to none and clear other types', async() => {
70
+ const value = { basicAuth: { username: { name: 'u' } } };
71
+ const wrapper = shallowMount(Auth, {
72
+ props: { ...defaultProps, value },
73
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
74
+ });
75
+
76
+ await wrapper.findComponent(LabeledSelect).vm.$emit('update:value', 'none');
77
+ expect(wrapper.vm.authType).toBe('none');
78
+ expect(wrapper.props('value').basicAuth).toBeUndefined();
79
+ });
80
+
81
+ it('should update basic auth username and password', async() => {
82
+ const wrapper = shallowMount(Auth, {
83
+ props: { ...defaultProps, value: { basicAuth: { username: { name: 'test' } } } },
84
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
85
+ });
86
+
87
+ await wrapper.vm.$nextTick();
88
+
89
+ const selectors = wrapper.findAllComponents(SimpleSecretSelector);
90
+
91
+ expect(selectors).toHaveLength(2);
92
+
93
+ const usernameSelector = selectors[0];
94
+ const passwordSelector = selectors[1];
95
+
96
+ await usernameSelector.vm.$emit('updateSecretName', 'user-secret');
97
+ await usernameSelector.vm.$emit('updateSecretKey', 'user-key');
98
+ await passwordSelector.vm.$emit('updateSecretName', 'pass-secret');
99
+ await passwordSelector.vm.$emit('updateSecretKey', 'pass-key');
100
+
101
+ const basicAuth = wrapper.props('value').basicAuth;
102
+
103
+ expect(basicAuth.username.name).toBe('user-secret');
104
+ expect(basicAuth.username.key).toBe('user-key');
105
+ expect(basicAuth.password.name).toBe('pass-secret');
106
+ expect(basicAuth.password.key).toBe('pass-key');
107
+ });
108
+
109
+ it('should update bearer token secret', async() => {
110
+ const wrapper = shallowMount(Auth, {
111
+ props: { ...defaultProps, value: { bearerTokenSecret: { name: 'test' } } },
112
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
113
+ });
114
+
115
+ await wrapper.vm.$nextTick();
116
+
117
+ const selector = wrapper.findComponent(SimpleSecretSelector);
118
+
119
+ expect(selector.exists()).toBe(true);
120
+
121
+ await selector.vm.$emit('updateSecretName', 'bearer-name');
122
+ await selector.vm.$emit('updateSecretKey', 'bearer-key');
123
+
124
+ const bearerToken = wrapper.props('value').bearerTokenSecret;
125
+
126
+ expect(bearerToken.name).toBe('bearer-name');
127
+ expect(bearerToken.key).toBe('bearer-key');
128
+ });
129
+
130
+ it('should render in view mode', () => {
131
+ const wrapper = shallowMount(Auth, {
132
+ props: {
133
+ mode: 'view', value: { basicAuth: {} }, namespace: 'ns'
134
+ },
135
+ global: { mocks: { $fetchState: { pending: false, error: null } } }
136
+ });
137
+
138
+ expect(wrapper.findComponent(LabeledSelect).attributes('disabled')).toBe('true');
139
+ const selectors = wrapper.findAllComponents(SimpleSecretSelector);
140
+
141
+ selectors.forEach((selector) => {
142
+ expect(selector.props('disabled')).toBe(true);
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,202 @@
1
+ /* eslint-disable import/first */
2
+ global.PointerEvent = class PointerEvent extends Event {};
3
+
4
+ import { shallowMount } from '@vue/test-utils';
5
+ import { EDITOR_MODES } from '@shell/components/YamlEditor.vue';
6
+ import { _EDIT, _VIEW } from '@shell/config/query-params';
7
+ import Index from '../index.vue';
8
+
9
+ describe('monitoring.coreos.com.alertmanagerconfig/index.vue', () => {
10
+ it('should render correctly', () => {
11
+ const valueMock = {
12
+ applyDefaults: jest.fn(),
13
+ spec: {
14
+ receivers: [
15
+ { name: 'receiver1', type: 'webhook' },
16
+ { name: 'receiver2', type: 'email' },
17
+ ],
18
+ },
19
+ getCreateReceiverRoute: jest.fn(() => ({ name: 'create-receiver' })),
20
+ getReceiverDetailLink: jest.fn((name) => `detail-${ name }`),
21
+ };
22
+
23
+ const wrapper = shallowMount(Index, {
24
+ propsData: {
25
+ value: valueMock,
26
+ mode: 'create',
27
+ },
28
+ global: {
29
+ mocks: {
30
+ $store: {
31
+ getters: { currentProduct: { inStore: 'test' } },
32
+ dispatch: jest.fn(() => Promise.resolve({ receiverSchema: {} })),
33
+ },
34
+ $router: { push: jest.fn() },
35
+ $route: { name: 'test-route' },
36
+ $fetchState: { pending: false },
37
+ }
38
+ },
39
+ stubs: {
40
+ CruResource: true,
41
+ NameNsDescription: true,
42
+ Tabbed: true,
43
+ Tab: true,
44
+ RouteConfig: true,
45
+ ResourceTable: true,
46
+ ActionMenu: true,
47
+ Loading: true,
48
+ }
49
+ });
50
+
51
+ // Ensure the component is rendered
52
+ expect(wrapper.exists()).toBe(true);
53
+
54
+ // Assert that applyDefaults is called
55
+ expect(valueMock.applyDefaults).toHaveBeenCalledTimes(1);
56
+
57
+ // Assert receiverOptions are correctly populated from value.spec.receivers
58
+ expect(wrapper.vm.receiverOptions).toStrictEqual(['receiver1', 'receiver2']);
59
+
60
+ // Assert createReceiverLink is correctly set
61
+ expect(wrapper.vm.createReceiverLink).toStrictEqual({ name: 'create-receiver' });
62
+
63
+ // Assert that receiverTableHeaders has the expected structure (basic check)
64
+ expect(wrapper.vm.receiverTableHeaders.length).toBeGreaterThan(0);
65
+ expect(wrapper.vm.receiverTableHeaders[0].name).toBe('name');
66
+ expect(wrapper.vm.receiverTableHeaders[1].name).toBe('type');
67
+ });
68
+
69
+ // Additional test for editorMode computed property
70
+ it('editorMode should return VIEW_CODE when mode is _VIEW', () => {
71
+ const valueMock = {
72
+ applyDefaults: jest.fn(),
73
+ spec: { receivers: [] },
74
+ getCreateReceiverRoute: jest.fn(),
75
+ getReceiverDetailLink: jest.fn(),
76
+ };
77
+
78
+ const wrapper = shallowMount(Index, {
79
+ propsData: {
80
+ value: valueMock,
81
+ mode: _VIEW, // Set mode to _VIEW
82
+ },
83
+ global: {
84
+ mocks: {
85
+ $store: {
86
+ getters: { currentProduct: { inStore: 'test' } },
87
+ dispatch: jest.fn(() => Promise.resolve({ receiverSchema: {} })),
88
+ },
89
+ $router: { push: jest.fn() },
90
+ $route: { name: 'test-route' },
91
+ $fetchState: { pending: false },
92
+ },
93
+ },
94
+ stubs: {
95
+ CruResource: true,
96
+ NameNsDescription: true,
97
+ Tabbed: true,
98
+ Tab: true,
99
+ RouteConfig: true,
100
+ ResourceTable: true,
101
+ ActionMenu: true,
102
+ Loading: true,
103
+ }
104
+ });
105
+
106
+ expect(wrapper.vm.editorMode).toBe(EDITOR_MODES.VIEW_CODE);
107
+ });
108
+
109
+ it('editorMode should return EDIT_CODE when mode is not _VIEW', () => {
110
+ const valueMock = {
111
+ applyDefaults: jest.fn(),
112
+ spec: { receivers: [] },
113
+ getCreateReceiverRoute: jest.fn(),
114
+ getReceiverDetailLink: jest.fn(),
115
+ };
116
+
117
+ const wrapper = shallowMount(Index, {
118
+ propsData: {
119
+ value: valueMock,
120
+ mode: _EDIT, // Set mode to _EDIT
121
+ },
122
+ global: {
123
+ mocks: {
124
+ $store: {
125
+ getters: { currentProduct: { inStore: 'test' } },
126
+ dispatch: jest.fn(() => Promise.resolve({ receiverSchema: {} })),
127
+ },
128
+ $router: { push: jest.fn() },
129
+ $route: { name: 'test-route' },
130
+ $fetchState: { pending: false },
131
+ },
132
+ },
133
+ stubs: {
134
+ CruResource: true,
135
+ NameNsDescription: true,
136
+ Tabbed: true,
137
+ Tab: true,
138
+ RouteConfig: true,
139
+ ResourceTable: true,
140
+ ActionMenu: true,
141
+ Loading: true,
142
+ }
143
+ });
144
+
145
+ expect(wrapper.vm.editorMode).toBe(EDITOR_MODES.EDIT_CODE);
146
+ });
147
+
148
+ it('translateReceiverTypes method translates receiver types', () => {
149
+ const valueMock = {
150
+ applyDefaults: jest.fn(),
151
+ spec: { receivers: [] },
152
+ getCreateReceiverRoute: jest.fn(),
153
+ getReceiverDetailLink: jest.fn(),
154
+ };
155
+
156
+ const wrapper = shallowMount(Index, {
157
+ propsData: {
158
+ value: valueMock,
159
+ mode: 'create',
160
+ },
161
+ global: {
162
+ mocks: {
163
+ $store: {
164
+ getters: { currentProduct: { inStore: 'test' } },
165
+ dispatch: jest.fn(() => Promise.resolve({ receiverSchema: {} })),
166
+ },
167
+ $router: { push: jest.fn() },
168
+ $route: { name: 'test-route' },
169
+ $fetchState: { pending: false },
170
+ },
171
+ },
172
+ stubs: {
173
+ CruResource: true,
174
+ NameNsDescription: true,
175
+ Tabbed: true,
176
+ Tab: true,
177
+ RouteConfig: true,
178
+ ResourceTable: true,
179
+ ActionMenu: true,
180
+ Loading: true,
181
+ }
182
+ });
183
+
184
+ // Ensure receiverTypes is initialized, then call the method
185
+ const originalReceiverTypes = [
186
+ { label: 'alertmanagerConfigReceiver.alerta', value: 'alerta' },
187
+ { label: 'alertmanagerConfigReceiver.dingtalk', value: 'dingtalk' }
188
+ ];
189
+
190
+ // Manually set receiverTypes for the test (it's normally populated from an import)
191
+ // In a real scenario, you'd mock the import or ensure it's loaded before the test.
192
+ // For this test, we'll directly manipulate the vm.
193
+ wrapper.setData({ receiverTypes: originalReceiverTypes });
194
+
195
+ const translatedTypes = wrapper.vm.translateReceiverTypes();
196
+
197
+ expect(translatedTypes).toStrictEqual([
198
+ { label: '%alertmanagerConfigReceiver.alerta%', value: 'alerta' },
199
+ { label: '%alertmanagerConfigReceiver.dingtalk%', value: 'dingtalk' }
200
+ ]);
201
+ });
202
+ });