@@ -22,13 +22,17 @@ import { ShapeFlags } from './utils/vueShared'
2222 */
2323function 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,31 @@ 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 suspect that is by design because according
121+ // to the defineExpose docs, the exposed properties become "available for the
122+ // parent component via templateRefs, which is the the case reported in the issue
123+ // (in fact using templateRefs and doing .findComponent({ ref: 'refName' }) works
124+ // as expected). But using "expose" via option or setup script does not keep
125+ // this consistenticy and dependending on how setup return is done, the exposed
126+ // might not be added to the vm even if exposed.
127+ // So this can be considered highjacking the vuiedesign.
128+ // It's up to the VTU to decide if it want to provide the convience of having
129+ // a single interface without
130+ // https://vuejs.org/api/sfc-script-setup.html#defineexpose
131+ // https://vuejs.org/api/composition-api-setup.html#exposing-public-properties
132+ // https://vuejs.org/api/options-state.html#expose
133+ //
111134 if ( hasSetupState ( vm ) ) {
112- this . componentVM = createVMProxy < T > ( vm , vm . $ . setupState )
135+ this . componentVM = createVMProxy < T > ( vm , vm . $ . setupState , vm . $ . exposed )
113136 } else {
114137 this . componentVM = vm
138+ Object . assign ( this . componentVM , vm . $ . exposed )
115139 }
116140 this . __setProps = setProps
117141
0 commit comments