Skip to content

Commit 9680bf2

Browse files
committed
fix(#2591): findComponent assign instance exposed properties to the vm
even when that instance does not have a templateRef?
1 parent a31afb7 commit 9680bf2

11 files changed

+350
-5
lines changed

src/vueWrapper.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@ import { ShapeFlags } from './utils/vueShared'
2222
*/
2323
function createVMProxy<T extends ComponentPublicInstance>(
2424
vm: T,
25-
setupState: Record<string, any>
25+
setupState: Record<string, any>,
26+
exposed: Record<string, any> | null
2627
): T {
2728
return new Proxy(vm, {
2829
get(vm, key, receiver) {
2930
if (vm.$.exposed && vm.$.exposeProxy && key in vm.$.exposeProxy) {
3031
// first if the key is exposed
3132
return Reflect.get(vm.$.exposeProxy, key, receiver)
33+
} else if (exposed && key in exposed) {
34+
// first if the key is exposed
35+
return Reflect.get(exposed, key, receiver)
3236
} else if (key in setupState) {
3337
// second if the key is acccessible from the setupState
3438
return Reflect.get(setupState, key, receiver)
@@ -107,11 +111,24 @@ export class VueWrapper<
107111
// if we return it as `vm`
108112
// This does not work for functional components though (as they have no vm)
109113
// or for components with a setup that returns a render function (as they have an empty proxy)
110-
// in both cases, we return `vm` directly instead
114+
// in both cases, we return `vm` directly instead.
115+
//
116+
// NOTE https://github.com/vuejs/test-utils/issues/2591
117+
// I'm sry i'm not entirely sure why, but exposed properties — via expose/defineExpose
118+
// are not assigned to the componentVM when the the `vm` argument provided
119+
// to this constructor comes from `findComponent` — as in, not the original instance
120+
// but already the proxied one. I first suspected that was by design of defineExpose
121+
// but that doesn't explain why it works when finding a .vue component or
122+
// vs it's bundled version, where the different is conversion of to a render
123+
// function. Also i've noticed that sometimes we can get some exceptions in
124+
// bundle code becuase render function is hoisted and exposed if properties
125+
// are returned to template, they also become available in th einstance.
126+
//
111127
if (hasSetupState(vm)) {
112-
this.componentVM = createVMProxy<T>(vm, vm.$.setupState)
128+
this.componentVM = createVMProxy<T>(vm, vm.$.setupState, vm.$.exposed)
113129
} else {
114130
this.componentVM = vm
131+
Object.assign(this.componentVM, vm.$.exposed)
115132
}
116133
this.__setProps = setProps
117134

tests/components/DefineExpose.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export default defineComponent({
6060
})
6161
6262
return {
63+
exposedMethod1,
6364
returnedState,
6465
}
6566
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// @ts-ignore
2+
import {
3+
defineComponent,
4+
ref,
5+
openBlock,
6+
createElementBlock,
7+
Fragment,
8+
createElementVNode,
9+
toDisplayString
10+
} from 'vue'
11+
const exposedState1 = 'exposedState1'
12+
const exposedState2 = 'exposedState2'
13+
const _sfc_main = /* @__PURE__ */ defineComponent({
14+
...{
15+
name: 'Hello'
16+
},
17+
__name: 'DefineExposeScriptSetup',
18+
setup(__props, { expose: __expose }) {
19+
const exposedState2Getter = () => {
20+
return exposedState2
21+
}
22+
const exposedRef = ref('exposedRef')
23+
const exposedRefGetter = () => {
24+
return exposedRef.value
25+
}
26+
const exposedMethod1 = () => {
27+
return 'result of exposedMethod1'
28+
}
29+
const exposedMethod2 = () => {
30+
return 'result of exposedMethod2'
31+
}
32+
const refNonExposed = ref('refNonExposed')
33+
const refNonExposedGetter = () => {
34+
return refNonExposed.value
35+
}
36+
const count = ref(0)
37+
const inc = () => {
38+
count.value++
39+
}
40+
const resetCount = () => {
41+
count.value = 0
42+
}
43+
__expose({
44+
exposeObjectLiteral: 'exposeObjectLiteral',
45+
exposedState1,
46+
exposedState2Alias: exposedState2,
47+
exposedState2Getter,
48+
exposedRef,
49+
exposedRefGetter,
50+
exposedMethod1,
51+
exposedMethod2Alias: exposedMethod2,
52+
count,
53+
resetCount,
54+
refNonExposedGetter
55+
})
56+
return (_ctx, _cache) => {
57+
return (
58+
openBlock(),
59+
createElementBlock(
60+
Fragment,
61+
null,
62+
[
63+
createElementVNode(
64+
'button',
65+
{ onClick: inc },
66+
toDisplayString(count.value),
67+
1
68+
),
69+
createElementVNode(
70+
'div',
71+
{ 'force-expose': exposedMethod1 },
72+
toDisplayString(refNonExposed.value),
73+
1
74+
)
75+
],
76+
64
77+
)
78+
)
79+
}
80+
}
81+
})
82+
export default _sfc_main

tests/components/DefineExposeWithRenderFunction.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default defineComponent({
3333
expose({
3434
/* ------ Common Test Case ------ */
3535
exposeObjectLiteral: 'exposeObjectLiteral',
36-
36+
3737
exposedState1,
3838
exposedState2Alias: exposedState2,
3939
exposedState2Getter,
@@ -46,7 +46,8 @@ export default defineComponent({
4646
/* ------ Common Test Case ------ */
4747
})
4848
49-
return () => [h('div', refUseByRenderFnButNotExposed.value)]
49+
return () => [
50+
h('div', refUseByRenderFnButNotExposed.value)]
5051
}
5152
})
5253
</script>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineComponent, h } from 'vue'
2+
3+
export default defineComponent({
4+
name: 'FindComponentExposeRenderFunction',
5+
props: {
6+
someProp: String
7+
},
8+
setup(_, { expose }) {
9+
const exposedFn = () => {
10+
return 'exposedFnReturn'
11+
}
12+
13+
expose({
14+
exposedFn
15+
})
16+
17+
return () => {
18+
return h('div', 'Example')
19+
}
20+
}
21+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<template>
2+
<div>Example</div>
3+
</template>
4+
5+
<script setup>
6+
const props = defineProps({
7+
someProp: String,
8+
});
9+
10+
const exposedFn = () => {
11+
return 'exposedFnReturn';
12+
};
13+
14+
defineExpose({
15+
exposedFn,
16+
});
17+
</script>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @ts-ignore
2+
import { openBlock, createElementBlock } from 'vue'
3+
const _sfc_main = {
4+
__name: 'FindComponentExposeScriptSetupBundled',
5+
props: {
6+
someProp: String
7+
},
8+
setup(__props, { expose: __expose }) {
9+
const exposedFn = () => {
10+
return 'exposedFnReturn'
11+
}
12+
__expose({
13+
exposedFn
14+
})
15+
return (_ctx, _cache) => {
16+
return openBlock(), createElementBlock('div', null, 'Example')
17+
}
18+
}
19+
}
20+
export default _sfc_main
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<div>Example</div>
3+
</template>
4+
5+
<script>
6+
import { defineComponent } from 'vue';
7+
8+
export default defineComponent({
9+
name: 'FindComponentExposeTemplate',
10+
props: {
11+
someProp: String,
12+
},
13+
setup(_, { expose }) {
14+
const exposedFn = () => {
15+
return 'exposedFnReturn';
16+
};
17+
18+
expose({
19+
exposedFn,
20+
});
21+
22+
return {
23+
oopsy: 1
24+
};
25+
}
26+
})
27+
</script>
28+

tests/components/ScriptSetup_Expose.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ defineExpose({
6060
resetCount,
6161
refNonExposedGetter,
6262
})
63+
64+
defineOptions({
65+
name: 'Hello',
66+
})
6367
</script>
6468

6569
<template>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy