Skip to content

Can't trigger event of child component #1410

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

Closed
CzBiX opened this issue Jan 18, 2020 · 18 comments
Closed

Can't trigger event of child component #1410

CzBiX opened this issue Jan 18, 2020 · 18 comments

Comments

@CzBiX
Copy link

CzBiX commented Jan 18, 2020

Version

1.0.0-beta.29

Reproduction link

https://codesandbox.io/s/ecstatic-moser-b402n

Steps to reproduce

Run test with npx vue-cli-service test:unit.

What is expected?

Test passed.

What is actually happening?

Test failed.

@CzBiX CzBiX changed the title Can trigger event of child component Can't trigger event of child component Jan 18, 2020
@lmiller1990
Copy link
Member

The reproduction sandbox isn't running for me - complaining about Jest and It been undefined.

Can you try using await/async and await nextTick? Eg:

it("demo without stub", async () => {
  const cb = jest.fn();

  const wrapper = mount(Demo, {
    localVue,
    stubs: {
      Dummy: false
    }
  });

  wrapper.setData({
    cb
  });

  console.log(wrapper.html());
  wrapper.find("div").trigger("input");
  // or
  wrapper.find("div").vm.$emit("input");
  await wrapper.vm.$nextTick()

  expect(cb).toBeCalled();
});

And see if that works

@CzBiX
Copy link
Author

CzBiX commented Jan 20, 2020

Yes, codesandbox broken for @vue/vue-test-utils or jest. You can export it to local and test.
I tried upgrade to 1.0.0-beta.30 and use nextTick, but issue still exists.

I debugged it, found wrapper.trigger method only trigger DOM event, so it's not working for component.
And wrapper.find('div').vm will return root Vue instance(wrapper.vm === wrapper.find('div').vm), so wrapper.find('div').vm.$emit not working neither.

@dobromir-hristov
Copy link
Contributor

Try find({ ref: 'some-ref' }) and tell me if it works :) I may know the reason why 😄

@lmiller1990
Copy link
Member

I wasn't thinking when I posted - it should it be wrapper.find(Demo).vm.$emit("input"), I think.

I tried Export to ZIP but nothing happened. I can create a template this evening and try this out.

@CzBiX
Copy link
Author

CzBiX commented Jan 21, 2020

I tried find({ ref: 'some-ref' }).trigger('input') and find(Demo).vm.$emit('input'), still no luck.

@lmiller1990
Copy link
Member

Weird, I'll give this a look this evening.

@CzBiX
Copy link
Author

CzBiX commented Jan 21, 2020

Sorry, I forget about trigger only trigger DOM event.
I correct it to find({ ref: 'some-ref' }).vm.$emit('input'), then it works w/wo stubs.

Is this a bug or my incorrect usage? I'm not sure.

Maybe we should point it out in docs:
"You can get child component vm instance via find by ref."
"Triggers an DOM event asynchronously…"

@lmiller1990
Copy link
Member

You should not need to use ref - @dobromir-hristov seems to have some info around this.

@aldarund
Copy link

isnt it same as #1385 ?

@lmiller1990
Copy link
Member

lmiller1990 commented Jan 28, 2020

I think you could be right @aldarund.

@CzBiX are you able to share your exact use case for this functionality with some more context?

I'm starting to think instead of attempting to fix/cover every edge case for using find (we have ref, querySelector, component, and there are some that appear very hard or impossible to solve as expected in #1385), we should start providing best practices and guides to writing tests with VTU so people don't need to resort to things like find({ ref: 'ref' }).vm.$emit('event'), which doesn't at all reflect something a user would be able to do.

@CzBiX
Copy link
Author

CzBiX commented Jan 28, 2020

I'm using Vuetify, which provide v-btn component. And I want to write test for click button action.

Add ref in src only for unit test is hacky to me.
Right now, I have to wrap something like <v-btn @input="someValue = !someValue" /> to <v-btn @input="clickBtn" />, then I can just call wrapper.vm.clickBtn() to test button events.

@aldarund In my use case child component is not the root component.

@lmiller1990
Copy link
Member

lmiller1990 commented Jan 28, 2020

I see. This is a pretty common use case (testing Vuetify).

I use this approach in my Vuetify apps (and when people ask for help with testing Vuetify).

Component:

<template>
  <div>
    <div id="count">
      Count: {{ count }}
    </div>
    <v-btn data-test-btn @click="handleInput">Ok</v-btn>
  </div>
</template>

<script>
export default {
  name: 'App',

  methods: {
    handleInput() { 
      this.count++ 
    }
  },

  data: () => ({
    count: 0
  }),
};
</script>

Test:

import { mount, createLocalVue } from '@vue/test-utils'
import Vuetify from 'vuetify'

import App from '@/App.vue'

describe('', () => {
  it('renders props.msg when passed', async () => {
    const localVue = createLocalVue()
    localVue.use(Vuetify)
    const vuetify = new Vuetify()
    const wrapper = mount(App, {
      localVue,
      vuetify
    })

    wrapper.find('[data-test-btn]').find('button').trigger('click')
    await wrapper.vm.$nextTick()
    expect(wrapper.find('#count').text()).toBe('Count: 1')
  })
})

I added a data-test attribute to make it easier to find my <v-btn>. Then I just chain find again to find the <button> Vuetify wraps.

I am not sure if this solution is ideal for every case, but I've found it pretty good. The reason I like it is because you do not need to use .vm, just basic things like find and trigger.

Anyway, we will still try out best to fix all the trigger and find bugs, but this might be useful to you as well. Thanks for the feedback and useful bug reports, it's clear that we need better tooling for testing Vuetify and other third party libs. I'll take this on board in planning for VTU 1.0, and Vue 3 support :)

@aldarund
Copy link

@CzBiX but in your provided CBS in Demo.vue and test you are testing the root component . How its not the root?

medariox added a commit to pymedusa/Medusa that referenced this issue Jan 28, 2020
medariox added a commit to pymedusa/Medusa that referenced this issue Jan 29, 2020
* Update JS vue-base

* yarn dev

* yarn test -u

* Fix vuejs/vue-test-utils#1410

* More async goodness

* async gotta be async

Co-authored-by: WhiteSource Renovate <[email protected]>
Co-authored-by: Dario <[email protected]>
@CzBiX
Copy link
Author

CzBiX commented Jan 29, 2020

@aldarund This issue still exists when the child component is not root component. The CSB is just a simplified demo for reproduction/debug.

@dobromir-hristov
Copy link
Contributor

You are correct. There are a few issues with this.

  1. trigger is meant to trigger DOM events on DOM nodes. Custom Vue Events bound to custom Vue components may not work in this case.
  2. When using find via a DOM selector, like .class or div, it will actually find the first Vue Component, that has that tag as it's root node. But as we wee in your demo, both Demo and Dummy share the same root DOM element, div. So even if you expect to trigger something on Dummy, you actually get Demo returned to you.

Is this confusing? Yes, definitely... Is there a fix for this atm? No...

We will try to find a sensible solution to this in the future.

For now, if you must trigger a custom Vue event, you need to find the DOM component that emits it, or use vm.$emit() on the Vue component that has the listener attached to it.

@lmiller1990
Copy link
Member

I think we need an alternative API to stubbing in VTU for Vue 3. The current API is confusing and requires the user to know the implementation details of their component, which is not how tests should work - coupling your tests to implementation details leads to brittle tests. This is not really the fault of the user, but of VTU, to provide cohesive API and guidance.

Anyway, there is a work around and the reason is known, I think we should close this as a wontfix.

The docs should definitely be updated, I'll think about how to explain it best and update them soon. At the moment their is a lot of bloat with guides on how to configure things, we need to reduce that kind of content and publish more guides on how to actually use VTU.

@CzBiX
Copy link
Author

CzBiX commented Jan 29, 2020

@dobromir-hristov I must to point out, the div tag in Dummy doesn't matter, when I change it to span in Dummay, wrapper.vm === wrapper.find('span').vm still be true.
Looks like the only way to get child compoent vm is find by ref at the moment.

@dobromir-hristov
Copy link
Contributor

correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants