Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#2591): findComponent assign instance exposed properties to vm? #2592

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions src/vueWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ import { ShapeFlags } from './utils/vueShared'
*/
function createVMProxy<T extends ComponentPublicInstance>(
vm: T,
setupState: Record<string, any>
setupState: Record<string, any>,
exposed: Record<string, any> | null
): T {
return new Proxy(vm, {
get(vm, key, receiver) {
if (vm.$.exposed && vm.$.exposeProxy && key in vm.$.exposeProxy) {
// first if the key is exposed
return Reflect.get(vm.$.exposeProxy, key, receiver)
} else if (exposed && key in exposed) {
// first if the key is exposed
return Reflect.get(exposed, key, receiver)
} else if (key in setupState) {
// second if the key is acccessible from the setupState
return Reflect.get(setupState, key, receiver)
Expand Down Expand Up @@ -107,11 +111,24 @@ export class VueWrapper<
// if we return it as `vm`
// This does not work for functional components though (as they have no vm)
// or for components with a setup that returns a render function (as they have an empty proxy)
// in both cases, we return `vm` directly instead
// in both cases, we return `vm` directly instead.
//
// NOTE https://github.com/vuejs/test-utils/issues/2591
// I'm sry i'm not entirely sure why, but exposed properties — via expose/defineExpose
// are not assigned to the componentVM when the the `vm` argument provided
// to this constructor comes from `findComponent` — as in, not the original instance
// but already the proxied one. I first suspected that was by design of defineExpose
// but that doesn't explain why it works when finding a .vue component or
// vs it's bundled version, where the different is conversion of to a render
// function. Also i've noticed that sometimes we can get some exceptions in
// bundle code becuase render function is hoisted and exposed if properties
// are returned to template, they also become available in th einstance.
//
if (hasSetupState(vm)) {
this.componentVM = createVMProxy<T>(vm, vm.$.setupState)
this.componentVM = createVMProxy<T>(vm, vm.$.setupState, vm.$.exposed)
} else {
this.componentVM = vm
Object.assign(this.componentVM, vm.$.exposed)
}
this.__setProps = setProps

Expand Down
1 change: 1 addition & 0 deletions tests/components/DefineExpose.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default defineComponent({
})

return {
exposedMethod1,
returnedState,
}
}
Expand Down
82 changes: 82 additions & 0 deletions tests/components/DefineExposeBundled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// @ts-ignore
import {
defineComponent,
ref,
openBlock,
createElementBlock,
Fragment,
createElementVNode,
toDisplayString
} from 'vue'
const exposedState1 = 'exposedState1'
const exposedState2 = 'exposedState2'
const _sfc_main = /* @__PURE__ */ defineComponent({
...{
name: 'Hello'
},
__name: 'DefineExposeScriptSetup',
setup(__props, { expose: __expose }) {
const exposedState2Getter = () => {
return exposedState2
}
const exposedRef = ref('exposedRef')
const exposedRefGetter = () => {
return exposedRef.value
}
const exposedMethod1 = () => {
return 'result of exposedMethod1'
}
const exposedMethod2 = () => {
return 'result of exposedMethod2'
}
const refNonExposed = ref('refNonExposed')
const refNonExposedGetter = () => {
return refNonExposed.value
}
const count = ref(0)
const inc = () => {
count.value++
}
const resetCount = () => {
count.value = 0
}
__expose({
exposeObjectLiteral: 'exposeObjectLiteral',
exposedState1,
exposedState2Alias: exposedState2,
exposedState2Getter,
exposedRef,
exposedRefGetter,
exposedMethod1,
exposedMethod2Alias: exposedMethod2,
count,
resetCount,
refNonExposedGetter
})
return (_ctx, _cache) => {

Check failure on line 56 in tests/components/DefineExposeBundled.ts

View workflow job for this annotation

GitHub Actions / build (18)

Parameter '_ctx' implicitly has an 'any' type.

Check failure on line 56 in tests/components/DefineExposeBundled.ts

View workflow job for this annotation

GitHub Actions / build (18)

Parameter '_cache' implicitly has an 'any' type.

Check failure on line 56 in tests/components/DefineExposeBundled.ts

View workflow job for this annotation

GitHub Actions / build (20)

Parameter '_ctx' implicitly has an 'any' type.

Check failure on line 56 in tests/components/DefineExposeBundled.ts

View workflow job for this annotation

GitHub Actions / build (20)

Parameter '_cache' implicitly has an 'any' type.

Check failure on line 56 in tests/components/DefineExposeBundled.ts

View workflow job for this annotation

GitHub Actions / build (22)

Parameter '_ctx' implicitly has an 'any' type.

Check failure on line 56 in tests/components/DefineExposeBundled.ts

View workflow job for this annotation

GitHub Actions / build (22)

Parameter '_cache' implicitly has an 'any' type.
return (
openBlock(),
createElementBlock(
Fragment,
null,
[
createElementVNode(
'button',
{ onClick: inc },
toDisplayString(count.value),
1
),
createElementVNode(
'div',
{ 'force-expose': exposedMethod1 },
toDisplayString(refNonExposed.value),
1
)
],
64
)
)
}
}
})
export default _sfc_main
5 changes: 3 additions & 2 deletions tests/components/DefineExposeWithRenderFunction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default defineComponent({
expose({
/* ------ Common Test Case ------ */
exposeObjectLiteral: 'exposeObjectLiteral',

exposedState1,
exposedState2Alias: exposedState2,
exposedState2Getter,
Expand All @@ -46,7 +46,8 @@ export default defineComponent({
/* ------ Common Test Case ------ */
})

return () => [h('div', refUseByRenderFnButNotExposed.value)]
return () => [
h('div', refUseByRenderFnButNotExposed.value)]
}
})
</script>
21 changes: 21 additions & 0 deletions tests/components/FindComponentExposeRenderFunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineComponent, h } from 'vue'

export default defineComponent({
name: 'FindComponentExposeRenderFunction',
props: {
someProp: String
},
setup(_, { expose }) {
const exposedFn = () => {
return 'exposedFnReturn'
}

expose({
exposedFn
})

return () => {
return h('div', 'Example')
}
}
})
17 changes: 17 additions & 0 deletions tests/components/FindComponentExposeScriptSetup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<div>Example</div>
</template>

<script setup>
const props = defineProps({
someProp: String,
});

const exposedFn = () => {
return 'exposedFnReturn';
};

defineExpose({
exposedFn,
});
</script>
20 changes: 20 additions & 0 deletions tests/components/FindComponentExposeScriptSetupBundled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @ts-ignore
import { openBlock, createElementBlock } from 'vue'
const _sfc_main = {
__name: 'FindComponentExposeScriptSetupBundled',
props: {
someProp: String
},
setup(__props, { expose: __expose }) {

Check failure on line 8 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (18)

Parameter '__props' implicitly has an 'any' type.

Check failure on line 8 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (18)

Binding element '__expose' implicitly has an 'any' type.

Check failure on line 8 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (20)

Parameter '__props' implicitly has an 'any' type.

Check failure on line 8 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (20)

Binding element '__expose' implicitly has an 'any' type.

Check failure on line 8 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (22)

Parameter '__props' implicitly has an 'any' type.

Check failure on line 8 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (22)

Binding element '__expose' implicitly has an 'any' type.
const exposedFn = () => {
return 'exposedFnReturn'
}
__expose({
exposedFn
})
return (_ctx, _cache) => {

Check failure on line 15 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (18)

Parameter '_ctx' implicitly has an 'any' type.

Check failure on line 15 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (18)

Parameter '_cache' implicitly has an 'any' type.

Check failure on line 15 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (20)

Parameter '_ctx' implicitly has an 'any' type.

Check failure on line 15 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (20)

Parameter '_cache' implicitly has an 'any' type.

Check failure on line 15 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (22)

Parameter '_ctx' implicitly has an 'any' type.

Check failure on line 15 in tests/components/FindComponentExposeScriptSetupBundled.ts

View workflow job for this annotation

GitHub Actions / build (22)

Parameter '_cache' implicitly has an 'any' type.
return openBlock(), createElementBlock('div', null, 'Example')
}
}
}
export default _sfc_main
28 changes: 28 additions & 0 deletions tests/components/FindComponentExposeTemplate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<div>Example</div>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({
name: 'FindComponentExposeTemplate',
props: {
someProp: String,
},
setup(_, { expose }) {
const exposedFn = () => {
return 'exposedFnReturn';
};

expose({
exposedFn,
});

return {
oopsy: 1
};
}
})
</script>

4 changes: 4 additions & 0 deletions tests/components/ScriptSetup_Expose.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ defineExpose({
resetCount,
refNonExposedGetter,
})

defineOptions({
name: 'Hello',
})
</script>

<template>
Expand Down
Loading
Loading