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

Bug: defineExpose does not work with functions #1731

Closed
marina-mosti opened this issue Aug 22, 2022 · 8 comments
Closed

Bug: defineExpose does not work with functions #1731

marina-mosti opened this issue Aug 22, 2022 · 8 comments
Labels
bug Something isn't working

Comments

@marina-mosti
Copy link
Contributor

marina-mosti commented Aug 22, 2022

Describe the bug

When using script setup + defineExpose, exposed functions are not found within VTU .vm

To Reproduce

Modify ScriptSetup_Expose.vue component as follows:

<script setup lang="ts">
// imported components are also directly usable in template
import { ref } from 'vue'
import Hello from './Hello.vue'

const count = ref(0)
const inc = () => {
  count.value++
}
const resetCount = () => {
  count.value = 0
}

defineExpose({
  count,
  resetCount
})
</script>

<template>
  <button @click="inc">{{ count }}</button>
  <Hello />
</template>

Within expose.spec.ts append to the test:

it('access vm with <script setup> even without defineExpose()', async () => {
    const wrapper = mount(ScriptSetup)

    await wrapper.find('button').trigger('click')
    expect(wrapper.html()).toContain('1')
    // can access `count` even if it is _not_ exposed
    // @ts-ignore we need better types here, see https://github.com/vuejs/test-utils/issues/972
    expect(wrapper.vm.count).toBe(1)

    wrapper.vm.resetCount()

    expect(wrapper.vm.count).toBe(0)
  })

Expected behavior

The test should pass, instead we get

 FAIL  tests/expose.spec.ts > expose > access vm with <script setup> even without defineExpose()
TypeError: wrapper.vm.resetCount is not a function
 ❯ tests/expose.spec.ts:54:15
     52|     expect(wrapper.vm.count).toBe(1)
     53| 
     54|     wrapper.vm.resetCount()
       |               ^
     55| 
     56|     expect(wrapper.vm.count).toBe(0)

Test Files  1 failed | 51 passed (52)
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

     Tests  1 failed | 403 passed | 1 todo (405)
  Start at  14:13:35
  Duration  7.76s

Related information:

  • @vue/test-utils version: 2.x.x
  • Vue version: 3.x.x
  • node version:
  • npm (or yarn) version: Ran with pnpm on top of main branch

Additional context

SFC of component shows correct binding of function to expose call https://sfc.vuejs.org/#eNptUcFuwjAM/RUrF0BAwxkVtGmatI/IpQSXhaVOlKRsU9V/n9PQscNufvbzs/08iGfvq1uPYi/qqIPxCSKm3oNt6HJQIkUljoqkBNN5FxKeQTuOCClFaAJCY6ODswmok/2GPjYni2AIEnbeNgkVlU4YIGALI7TBdbDgmYvf0hta6+6FSk4oL8UERdpRTDy0pwSHLLHcreasIc255QoORxgUQaFVt8b2uF4rGmdiQL7q5a7xP58Lu6lD0RlbQ/j65V3E5YO3ydFDicm8SC2LbWwSg/loRgD1qU/JETxpa/QHm8nrspvDcL9mHGtZKIVeXJAMavlHSGxEsWnbNb66Rkf8rGkrVpwK/KN9uSfn2LiMlXhPyce9lLHV2c1rrFy4SI6qwONNhxXGbnsK7jNiYGElsgQ7MIrxBzIhu4w=

@marina-mosti marina-mosti added the bug Something isn't working label Aug 22, 2022
@cexbrayat
Copy link
Member

Hey@marina-mosti

Thanks for the repro. I'm off work right now, so I'll take a deeper look in a few weeks, but that does not surprise me: we "cheat" to have access to the functions if they are not exposed, by using the internal proxy of what is available in the template. As resetCount is not used in the template, it is not available on the VM object.

Can you check that's what I suspect? If resetCount is used in the template, the test should succeed.

@marina-mosti
Copy link
Contributor Author

marina-mosti commented Aug 22, 2022

Hey @cexbrayat thanks for the reply, sadly no - it still fails the same way.

<script setup lang="ts">
// imported components are also directly usable in template
import { ref } from 'vue'
import Hello from './Hello.vue'

const count = ref(0)
const inc = () => {
  count.value++
}
const resetCount = () => {
  count.value = 0
}

defineExpose({
  count,
  resetCount
})
</script>

<template>
  <button @click="inc" @hover="resetCount">{{ count }}</button>
  <Hello />
</template>

I saw your "cheat" with the vm.proxy, but its not getting populated 😓
The console.log(vm.$.proxy) is still outputting {} even with the function in the template

@cexbrayat
Copy link
Member

Hmm interesting, I'll need to take a deeper look then (probably not until one or two weeks, so if someone else is up to it, feel free)

@lmiller1990
Copy link
Member

lmiller1990 commented Aug 23, 2022

I verified this does work in production - Test Utils only bug, I guess.

Where do we "cheat" @cexbrayat? I forgot how expose works in here 🤔

Somewhere around here I guess? https://github.com/vuejs/test-utils/blob/main/src/vueWrapper.ts#L47-L62. I'm not entirely clear what expose does under the hood, maybe time to dive in to core and have a look around.

@freakzlike
Copy link
Collaborator

freakzlike commented Aug 25, 2022

@marina-mosti I have added your reproduction code to the tests and run it successfully #1735. I think you updated ScriptSetup_Expose.vue but used ScriptSetup.vue within your test 😉

If this wasn't the case and it's still failing then please reopen this issue

@marina-mosti
Copy link
Contributor Author

🤦🏻‍♀️ Uff. Terribly sorry

@cexbrayat
Copy link
Member

Awesome, thanks @freakzlike for looking into it ❤️

@marina-mosti
Copy link
Contributor Author

Turns out after tons of trial and error that the actual problem was that the exposed function was named do

defineExpose({
  count,
  do: _do
})

Changing the name makes it be correctly exposed again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants