@rancher/shell 3.0.4 → 3.0.5-rc.1
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/styles/base/_basic.scss +6 -0
- package/assets/styles/global/_button.scss +1 -0
- package/assets/translations/en-us.yaml +37 -3
- package/cloud-credential/aws.vue +2 -0
- package/components/AssignTo.vue +25 -11
- package/components/AsyncButton.vue +24 -7
- package/components/BannerGraphic.vue +1 -0
- package/components/CommunityLinks.vue +3 -3
- package/components/CopyToClipboardText.vue +2 -1
- package/components/DetailText.vue +5 -0
- package/components/DisableAuthProviderModal.vue +1 -0
- package/components/ExplorerMembers.vue +1 -1
- package/components/ExplorerProjectsNamespaces.vue +56 -14
- package/components/LandingPagePreference.vue +5 -3
- package/components/LocaleSelector.vue +38 -94
- package/components/ModalWithCard.vue +1 -0
- package/components/MoveModal.vue +1 -0
- package/components/PromptRemove.vue +1 -0
- package/components/PromptRestore.vue +1 -0
- package/components/ResourceCancelModal.vue +1 -0
- package/components/SortableTable/index.vue +10 -11
- package/components/__tests__/AsyncButton.test.ts +2 -2
- package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
- package/components/form/ArrayList.vue +66 -54
- package/components/form/Command.vue +6 -15
- package/components/form/EnvVars.vue +15 -8
- package/components/form/HealthCheck.vue +3 -3
- package/components/form/HookOption.vue +11 -16
- package/components/form/LabeledSelect.vue +2 -1
- package/components/form/LifecycleHooks.vue +3 -3
- package/components/form/MatchExpressions.vue +10 -7
- package/components/form/NameNsDescription.vue +123 -103
- package/components/form/Networking.vue +20 -12
- package/components/form/NodeAffinity.vue +31 -23
- package/components/form/NodeScheduling.vue +13 -3
- package/components/form/PodAffinity.vue +43 -43
- package/components/form/Probe.vue +67 -66
- package/components/form/ResourceQuota/Project.vue +5 -1
- package/components/form/ResourceSelector.vue +7 -9
- package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +6 -3
- package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +12 -1
- package/components/form/SSHKnownHosts/index.vue +16 -2
- package/components/form/Security.vue +54 -56
- package/components/form/Select.vue +31 -6
- package/components/form/ShellInput.vue +5 -1
- package/components/form/Tolerations.vue +5 -1
- package/components/form/ValueFromResource.vue +134 -121
- package/components/form/WorkloadPorts.vue +18 -18
- package/components/form/__tests__/ArrayList.test.ts +3 -0
- package/components/form/__tests__/MatchExpressions.test.ts +12 -12
- package/components/form/__tests__/NameNsDescription.test.ts +115 -14
- package/components/form/__tests__/Probe.test.ts +12 -8
- package/components/form/__tests__/SSHKnownHosts.test.ts +11 -0
- package/components/form/__tests__/Select.test.ts +37 -0
- package/components/formatter/InternalExternalIP.vue +2 -0
- package/components/formatter/SecretData.vue +20 -7
- package/components/nav/Group.vue +15 -1
- package/components/nav/Header.vue +1 -0
- package/components/nav/Type.vue +12 -1
- package/components/templates/blank.vue +4 -1
- package/components/templates/default.vue +2 -0
- package/components/templates/home.vue +4 -1
- package/components/templates/plain.vue +4 -1
- package/composables/useRuntimeFlag.ts +29 -0
- package/config/router/routes.js +20 -13
- package/core/types.ts +5 -0
- package/dialog/AddCustomBadgeDialog.vue +1 -0
- package/dialog/DeactivateDriverDialog.vue +1 -0
- package/dialog/ForceMachineRemoveDialog.vue +4 -1
- package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
- package/edit/auth/__tests__/oidc.test.ts +152 -109
- package/edit/auth/azuread.vue +1 -0
- package/edit/auth/googleoauth.vue +4 -0
- package/edit/auth/oidc.vue +37 -4
- package/edit/cloudcredential.vue +1 -0
- package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
- package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
- package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
- package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +25 -34
- package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +29 -1
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
- package/edit/token.vue +2 -0
- package/edit/workload/index.vue +1 -0
- package/edit/workload/mixins/workload.js +0 -2
- package/list/management.cattle.io.feature.vue +1 -0
- package/list/provisioning.cattle.io.cluster.vue +20 -12
- package/models/__tests__/namespace.test.ts +25 -1
- package/models/cloudcredential.js +5 -0
- package/models/kontainerdriver.js +6 -3
- package/models/management.cattle.io.node.js +3 -3
- package/models/namespace.js +4 -5
- package/models/nodedriver.js +6 -3
- package/models/workload.js +4 -1
- package/package.json +3 -3
- package/pages/account/index.vue +4 -1
- package/pages/auth/login.vue +11 -3
- package/pages/auth/logout.vue +4 -1
- package/pages/auth/setup.vue +1 -0
- package/pages/auth/verify.vue +4 -1
- package/pages/c/_cluster/apps/charts/chart.vue +1 -1
- package/pages/diagnostic.vue +47 -2
- package/pages/fail-whale.vue +6 -3
- package/pages/home.vue +24 -18
- package/pages/support/index.vue +4 -1
- package/rancher-components/Form/Radio/RadioGroup.vue +25 -23
- package/rancher-components/RcDropdown/RcDropdown.vue +3 -2
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
- package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
- package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
- package/scripts/extension/publish +1 -0
- package/server/har-file.js +25 -3
- package/store/features.js +2 -1
- package/store/type-map.js +4 -0
- package/types/shell/index.d.ts +8 -1
- package/utils/cluster.js +35 -0
- package/utils/validators/machine-pool.ts +20 -0
- package/components/formatter/ExtensionCache.vue +0 -74
- package/components/formatter/Port.vue +0 -24
- package/components/formatter/SecretType.vue +0 -41
|
@@ -27,4 +27,41 @@ describe('select.vue', () => {
|
|
|
27
27
|
// eslint-disable-next-line no-console
|
|
28
28
|
expect(console.warn).not.toHaveBeenCalled();
|
|
29
29
|
});
|
|
30
|
+
|
|
31
|
+
it('a11y: adding ARIA props should correctly fill out the appropriate fields on the component', async() => {
|
|
32
|
+
const label = 'Foo';
|
|
33
|
+
const value = 'foo';
|
|
34
|
+
const ariaDescribedById = 'some-described-by-id';
|
|
35
|
+
const ariaLabelText = 'some-aria-label';
|
|
36
|
+
|
|
37
|
+
const wrapper = shallowMount(SelectComponent, {
|
|
38
|
+
props: {
|
|
39
|
+
value,
|
|
40
|
+
options: [
|
|
41
|
+
{ label, value },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
attrs: {
|
|
45
|
+
'aria-describedby': ariaDescribedById,
|
|
46
|
+
'aria-label': ariaLabelText,
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const labeledSelectContainer = wrapper.find('.unlabeled-select');
|
|
51
|
+
const ariaExpanded = labeledSelectContainer.attributes('aria-expanded');
|
|
52
|
+
const ariaDescribedBy = labeledSelectContainer.attributes('aria-describedby');
|
|
53
|
+
const ariaLabel = labeledSelectContainer.attributes('aria-label');
|
|
54
|
+
|
|
55
|
+
const vSelectInput = wrapper.find('.inline');
|
|
56
|
+
|
|
57
|
+
expect(ariaExpanded).toBe('false');
|
|
58
|
+
expect(ariaDescribedBy).toBe(ariaDescribedById);
|
|
59
|
+
expect(ariaLabel).toBe(ariaLabelText);
|
|
60
|
+
|
|
61
|
+
// make sure it's hardcoded to a "neutral" value so that
|
|
62
|
+
// in the current architecture of the component
|
|
63
|
+
// screen readers won't pick up the default "Select option" aria-label
|
|
64
|
+
// from the library
|
|
65
|
+
expect(vSelectInput.attributes('aria-label')).toBe('-');
|
|
66
|
+
});
|
|
30
67
|
});
|
|
@@ -28,6 +28,7 @@ export default {
|
|
|
28
28
|
<span>
|
|
29
29
|
<template v-if="isIp(row.externalIp)">
|
|
30
30
|
{{ row.externalIp }} <CopyToClipboard
|
|
31
|
+
:aria-label="t('internalExternalIP.copyExternalIp')"
|
|
31
32
|
label-as="tooltip"
|
|
32
33
|
:text="row.externalIp"
|
|
33
34
|
class="icon-btn"
|
|
@@ -43,6 +44,7 @@ export default {
|
|
|
43
44
|
</template>
|
|
44
45
|
<template v-else-if="isIp(row.internalIp)">
|
|
45
46
|
{{ row.internalIp }}<CopyToClipboard
|
|
47
|
+
:aria-label="t('internalExternalIP.copyInternalIp')"
|
|
46
48
|
label-as="tooltip"
|
|
47
49
|
:text="row.internalIp"
|
|
48
50
|
class="icon-btn"
|
|
@@ -13,17 +13,25 @@ export default {
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
beforeMount() {
|
|
17
17
|
if (this.value.issuer) {
|
|
18
18
|
const { cn, notAfter, sans = [] } = this.value;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return { isTLS: false };
|
|
20
|
+
this.expiration = notAfter;
|
|
21
|
+
this.sans = sans;
|
|
22
|
+
this.cn = cn;
|
|
23
|
+
this.isTLS = true;
|
|
25
24
|
}
|
|
26
25
|
},
|
|
26
|
+
|
|
27
|
+
data() {
|
|
28
|
+
return {
|
|
29
|
+
isTLS: false,
|
|
30
|
+
cn: null,
|
|
31
|
+
sans: [],
|
|
32
|
+
expiration: null,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
27
35
|
computed: {
|
|
28
36
|
// use 'text-warning' or 'text-error' classes if the cert is <8 days from expiring or expired respectively
|
|
29
37
|
dateClass() {
|
|
@@ -43,7 +51,12 @@ export default {
|
|
|
43
51
|
|
|
44
52
|
<template>
|
|
45
53
|
<div v-if="isTLS">
|
|
46
|
-
<t k="secret.certificate.cn" />
|
|
54
|
+
<t k="secret.certificate.cn" />
|
|
55
|
+
{{ cn }}
|
|
56
|
+
<span v-if="row.unrepeatedSans && row.unrepeatedSans.length">
|
|
57
|
+
{{ t('secret.certificate.plusMore', {n:row.unrepeatedSans.length}) }}
|
|
58
|
+
</span>
|
|
59
|
+
<br>
|
|
47
60
|
<t k="secret.certificate.expires" />: <DateComponent
|
|
48
61
|
:class="dateClass"
|
|
49
62
|
:value="expiration"
|
package/components/nav/Group.vue
CHANGED
|
@@ -158,6 +158,17 @@ export default {
|
|
|
158
158
|
items = this.group;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
let parentPath = '';
|
|
162
|
+
const cluster = this.$route.params?.cluster;
|
|
163
|
+
|
|
164
|
+
// Where we use nested route configuration, consider the parent route when trying to identify the nav location
|
|
165
|
+
if (this.$route.matched.length > 1) {
|
|
166
|
+
const parentRoute = this.$route.matched[this.$route.matched.length - 2];
|
|
167
|
+
|
|
168
|
+
parentPath = parentRoute.path.replace(':cluster', cluster);
|
|
169
|
+
parentPath = parentPath === '/' ? undefined : parentPath;
|
|
170
|
+
}
|
|
171
|
+
|
|
161
172
|
for (const item of items.children) {
|
|
162
173
|
if (item.children && this.hasActiveRoute(item)) {
|
|
163
174
|
return true;
|
|
@@ -166,8 +177,11 @@ export default {
|
|
|
166
177
|
const matchesNavLevel = navLevels.filter((param) => !this.$route.params[param] || this.$route.params[param] !== item.route.params[param]).length === 0;
|
|
167
178
|
const withoutHash = this.$route.hash ? this.$route.fullPath.slice(0, this.$route.fullPath.indexOf(this.$route.hash)) : this.$route.fullPath;
|
|
168
179
|
const withoutQuery = withoutHash.split('?')[0];
|
|
180
|
+
const itemFullPath = this.$router.resolve(item.route).fullPath;
|
|
169
181
|
|
|
170
|
-
if (matchesNavLevel ||
|
|
182
|
+
if (matchesNavLevel || itemFullPath === withoutQuery) {
|
|
183
|
+
return true;
|
|
184
|
+
} else if (parentPath && itemFullPath === parentPath) {
|
|
171
185
|
return true;
|
|
172
186
|
}
|
|
173
187
|
}
|
package/components/nav/Type.vue
CHANGED
|
@@ -58,7 +58,18 @@ export default {
|
|
|
58
58
|
|
|
59
59
|
isActive() {
|
|
60
60
|
const typeFullPath = this.$router.resolve(this.type.route)?.fullPath.toLowerCase();
|
|
61
|
-
const pageFullPath = this.$route.fullPath?.toLowerCase();
|
|
61
|
+
const pageFullPath = this.$route.fullPath?.toLowerCase().split('#')[0]; // Ignore the shebang when comparing routes
|
|
62
|
+
const routeMetaNav = this.$route.meta?.nav;
|
|
63
|
+
|
|
64
|
+
// If the route explicitly declares the nav path that should be highlighted, then use that
|
|
65
|
+
if (routeMetaNav) {
|
|
66
|
+
const cluster = this.$route.params?.cluster;
|
|
67
|
+
const navPath = routeMetaNav.replace(':cluster', cluster);
|
|
68
|
+
|
|
69
|
+
if (navPath === typeFullPath) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
62
73
|
|
|
63
74
|
if ( !this.type.exact) {
|
|
64
75
|
const typeSplit = typeFullPath.split('/');
|
|
@@ -198,6 +198,7 @@ export default {
|
|
|
198
198
|
<main
|
|
199
199
|
v-if="clusterAndRouteReady"
|
|
200
200
|
class="main-layout"
|
|
201
|
+
:aria-label="t('layouts.default')"
|
|
201
202
|
>
|
|
202
203
|
<router-view
|
|
203
204
|
:key="$route.path"
|
|
@@ -235,6 +236,7 @@ export default {
|
|
|
235
236
|
<main
|
|
236
237
|
v-else-if="unmatchedRoute"
|
|
237
238
|
class="main-layout"
|
|
239
|
+
:aria-label="t('layouts.default')"
|
|
238
240
|
>
|
|
239
241
|
<router-view
|
|
240
242
|
:key="$route.path"
|
|
@@ -68,7 +68,10 @@ export default {
|
|
|
68
68
|
:class="{'dashboard-padding-left': showTopLevelMenu}"
|
|
69
69
|
>
|
|
70
70
|
<Header :simple="true" />
|
|
71
|
-
<main
|
|
71
|
+
<main
|
|
72
|
+
class="main-layout"
|
|
73
|
+
:aria-label="t('layouts.plain')"
|
|
74
|
+
>
|
|
72
75
|
<IndentedPanel class="pt-20">
|
|
73
76
|
<router-view
|
|
74
77
|
:key="$route.path"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { Store } from 'vuex';
|
|
3
|
+
import semver from 'semver';
|
|
4
|
+
|
|
5
|
+
import { getVersionInfo } from '@shell/utils/version';
|
|
6
|
+
|
|
7
|
+
let store: Store<any>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initializes runtime flags.
|
|
11
|
+
* @param vuexStore The Vuex store instance
|
|
12
|
+
*/
|
|
13
|
+
export const useRuntimeFlag = (vuexStore: Store<any>) => {
|
|
14
|
+
store = vuexStore;
|
|
15
|
+
|
|
16
|
+
return { featureDropdownMenu };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if the dropdown menu feature is enabled
|
|
21
|
+
* @returns A boolean indicating whether the dropdownMenu feature is enabled.
|
|
22
|
+
*/
|
|
23
|
+
const featureDropdownMenu = computed(() => {
|
|
24
|
+
const { fullVersion } = getVersionInfo(store);
|
|
25
|
+
|
|
26
|
+
const coerced = semver.coerce(fullVersion) || { version: '0.0.0' };
|
|
27
|
+
|
|
28
|
+
return semver.gte(coerced.version, '2.11.0');
|
|
29
|
+
});
|
package/config/router/routes.js
CHANGED
|
@@ -276,14 +276,24 @@ export default [
|
|
|
276
276
|
name: 'c-cluster-neuvector',
|
|
277
277
|
meta: { ...installRedirectRouteMeta(NEUVECTOR_NAME, NEUVECTOR_CHART_NAME, undefined, false) }
|
|
278
278
|
}, {
|
|
279
|
-
path:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
279
|
+
path: '/c/:cluster/apps/charts',
|
|
280
|
+
children: [
|
|
281
|
+
{
|
|
282
|
+
path: '',
|
|
283
|
+
component: () => interopDefault(import('@shell/pages/c/_cluster/apps/charts/index.vue')),
|
|
284
|
+
name: 'c-cluster-apps-charts'
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
path: 'chart',
|
|
288
|
+
component: () => interopDefault(import('@shell/pages/c/_cluster/apps/charts/chart.vue')),
|
|
289
|
+
name: 'c-cluster-apps-charts-chart',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
path: 'install',
|
|
293
|
+
component: () => interopDefault(import('@shell/pages/c/_cluster/apps/charts/install.vue')),
|
|
294
|
+
name: 'c-cluster-apps-charts-install',
|
|
295
|
+
},
|
|
296
|
+
]
|
|
287
297
|
},
|
|
288
298
|
{
|
|
289
299
|
path: '/c/:cluster/auth/config',
|
|
@@ -353,10 +363,6 @@ export default [
|
|
|
353
363
|
path: '/c/:cluster/settings/performance',
|
|
354
364
|
component: () => interopDefault(import('@shell/pages/c/_cluster/settings/performance.vue')),
|
|
355
365
|
name: 'c-cluster-settings-performance'
|
|
356
|
-
}, {
|
|
357
|
-
path: '/c/:cluster/apps/charts/chart',
|
|
358
|
-
component: () => interopDefault(import('@shell/pages/c/_cluster/apps/charts/chart.vue')),
|
|
359
|
-
name: 'c-cluster-apps-charts-chart'
|
|
360
366
|
}, {
|
|
361
367
|
path: '/c/:cluster/auth/group.principal/assign-edit',
|
|
362
368
|
component: () => interopDefault(import('@shell/pages/c/_cluster/auth/group.principal/assign-edit.vue')),
|
|
@@ -364,7 +370,8 @@ export default [
|
|
|
364
370
|
}, {
|
|
365
371
|
path: '/c/:cluster/auth/user.retention',
|
|
366
372
|
component: () => interopDefault(import('@shell/pages/c/_cluster/auth/user.retention/index.vue')),
|
|
367
|
-
name: 'c-cluster-auth-user.retention'
|
|
373
|
+
name: 'c-cluster-auth-user.retention',
|
|
374
|
+
meta: { nav: '/c/:cluster/auth/management.cattle.io.user' }
|
|
368
375
|
}, {
|
|
369
376
|
path: '/c/:cluster/manager/cloudCredential/create',
|
|
370
377
|
component: () => interopDefault(import('@shell/pages/c/_cluster/manager/cloudCredential/create.vue')),
|
package/core/types.ts
CHANGED
|
@@ -373,6 +373,11 @@ export interface ConfigureTypeOptions {
|
|
|
373
373
|
*/
|
|
374
374
|
customRoute?: Object;
|
|
375
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Custom options vary pre resource type
|
|
378
|
+
*/
|
|
379
|
+
custom?: any;
|
|
380
|
+
|
|
376
381
|
/**
|
|
377
382
|
* Leaving these here for completeness but I don't think these should be advertised as useable to plugin creators.
|
|
378
383
|
*/
|
|
@@ -88,7 +88,10 @@ export default {
|
|
|
88
88
|
{{ t('promptForceRemove.confirmName') }}
|
|
89
89
|
</div>
|
|
90
90
|
<div class="mb-10">
|
|
91
|
-
<CopyToClipboardText
|
|
91
|
+
<CopyToClipboardText
|
|
92
|
+
:aria-label="t('promptForceRemove.ariaLabel')"
|
|
93
|
+
:text="nameToMatch"
|
|
94
|
+
/>
|
|
92
95
|
</div>
|
|
93
96
|
<input
|
|
94
97
|
id="confirm"
|
|
@@ -2,14 +2,23 @@ import { mount } from '@vue/test-utils';
|
|
|
2
2
|
import FormValidation from '@shell/mixins/form-validation';
|
|
3
3
|
import Monitoring from '@shell/edit/monitoring.coreos.com.prometheusrule/index.vue';
|
|
4
4
|
import { _EDIT } from '@shell/config/query-params';
|
|
5
|
+
import { createStore } from 'vuex';
|
|
5
6
|
|
|
6
7
|
describe('edit: management.cattle.io.setting should', () => {
|
|
7
8
|
const MOCKED_ERRORS = ['error1', 'error2', 'error3', 'error4', 'error5'];
|
|
8
9
|
const ERROR_BANNER_SELECTOR = '[data-testid="banner-close"]';
|
|
10
|
+
const store = createStore({
|
|
11
|
+
getters: {
|
|
12
|
+
namespaces: () => () => ({}),
|
|
13
|
+
currentStore: () => () => 'current_store',
|
|
14
|
+
'current_store/schemaFor': () => jest.fn()
|
|
15
|
+
}
|
|
16
|
+
});
|
|
9
17
|
const requiredSetup = () => ({
|
|
10
18
|
// Remove all these mocks after migration to Vue 2.7/3 due mixin logic
|
|
11
19
|
global: {
|
|
12
|
-
|
|
20
|
+
provide: { store },
|
|
21
|
+
mocks: {
|
|
13
22
|
$store: {
|
|
14
23
|
dispatch: jest.fn(),
|
|
15
24
|
getters: {
|
|
@@ -35,8 +44,12 @@ describe('edit: management.cattle.io.setting should', () => {
|
|
|
35
44
|
canYaml: false,
|
|
36
45
|
mode: _EDIT,
|
|
37
46
|
resource: {},
|
|
38
|
-
value: {
|
|
39
|
-
|
|
47
|
+
value: {
|
|
48
|
+
setAnnotation: jest.fn(),
|
|
49
|
+
value: 'anything',
|
|
50
|
+
metadata: {},
|
|
51
|
+
},
|
|
52
|
+
name: ''
|
|
40
53
|
},
|
|
41
54
|
...requiredSetup()
|
|
42
55
|
});
|