Skip to content

[ShallowMount] Cannot click on Vuetify icon when stubbing VIcon #919

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
trollepierre opened this issue Aug 13, 2018 · 21 comments
Closed

[ShallowMount] Cannot click on Vuetify icon when stubbing VIcon #919

trollepierre opened this issue Aug 13, 2018 · 21 comments

Comments

@trollepierre
Copy link
Contributor

Version

1.0.0-beta.24

Reproduction link

https://codesandbox.io/s/61jzxnovnk

Steps to reproduce

Trying to test click when using VIcon

I reproduce some scenarii :
it works when shallowMounting without using Vuetify, it works when mounting using Vuetify
I doesn't work when shallowMounting with Vuetify when selecting Component, by ref, by name

What is expected?

a way to trigger click on stub when shallowMount

What is actually happening?

it doesn't work

@tbsvttr
Copy link

tbsvttr commented Aug 15, 2018

Same problem here. I guess this is because Vuetify components are now seen as child components and stubs it therefore.

@trollepierre
Copy link
Contributor Author

So the question is: "How could it be possible to trigger event on stubbed components?"

@eddyerburgh
Copy link
Member

eddyerburgh commented Aug 19, 2018

A temporary workaround is to stub the component manually:

const Stub = {
  template: '<div />'
}

const wrapper = shallowMount(TestComponent, {
  stubs: {
    'VIcon': Stub
  }
})

wrapper.find(Stub)

@gentksb
Copy link

gentksb commented Aug 20, 2018

I have same problem on <v-btn> & <v-textarea>.
My workaround is changing data same as click action.

@ferm10n
Copy link

ferm10n commented Aug 21, 2018

@eddyerburgh is this what you were thinking for a workaround? https://codesandbox.io/s/42rxjjvz44
Not sure if I implemented your suggestion how you intended.

@trollepierre
Copy link
Contributor Author

@eddyerburgh : to me, the issue is not fixed in beta 25.
On my CodeSandbox, I update VueTestUtils version 25, it doesn't solve anything

Are you sure, this issue is fixed?

@ElVisPL
Copy link

ElVisPL commented Oct 17, 2018

Confirming. v25 didn't fix stubs problem

@trollepierre did you find some solution with shallowMount?

@trollepierre
Copy link
Contributor Author

trollepierre commented Oct 17, 2018

No. I had to mount. :'(

@eddyerburgh is it possible to reopen this issue?

@eddyerburgh eddyerburgh reopened this Oct 18, 2018
@p1pchenk0
Copy link

@eddyerburgh , thank you for your suggestion, but it does not help in the case, when there is a click handler passed as prop to parent component:

<div>
    I am parent
    <v-btn @click="callbackFromProp"></v-btn>
</div>
export default {
    props: {
        callbackFromProp: Function
    }
}

@eddyerburgh
Copy link
Member

@p1pchenk0 In that case you need to supply a stub that calls the listener on the root element:

const wrapper = shallowMount(TestComponent, {
  stubs: {
    'v-icon': {
      template: '<div class="v-icon" @click="$listeners.click" />'
    }
  }
})

wrapper.find('.icon').trigger('click')

I don't think we should add all listeners to the auto stubs. With normal components we use the on value of the component that's being stubbed, but not all components expose this info.

@eddyerburgh
Copy link
Member

eddyerburgh commented Dec 29, 2018

I'm going to close this issue, since it's not a bug. Let me clear up some misconceptions.

shallowMount only stubs components that are registered. This is to avoid false positives where components are stubbed and rendered in tests, but can't render in production.

The only guaranteed ways to select an auto stubbed component is to use a component selector:

const wrapper = shallowMount(TestComponent)
wrapper.find(Icon)

Or a ref selector:

const wrapper = shallowMount(TestComponent)
wrapper.find({ ref: 'icon' })

If you don't have access to the component, you must use the stubs option, and use the stub as a selector:

const Stub = { template: '<div />' }

const wrapper = shallowMount(TestComponent, {
  stubs: { icon: Stub }
})
wrapper.find(Stub)

@salvadordiaz
Copy link

If anyone is still trying to figure this out and is confused by the conversation in this issue, I found the solution in the documentation. In short, instead of triggering through the wrapper API like this:

wrapper.find(ChildComponent).trigger('click')

you should call the vue instance $emit function like this:

wrapper.find(ChildComponent).vm.$emit('click')

Hope that helps.

@sevillaarvin
Copy link

sevillaarvin commented Jun 5, 2019

If anyone is still trying to figure this out and is confused by the conversation in this issue, I found the solution in the documentation. In short, instead of triggering through the wrapper API like this:

wrapper.find(ChildComponent).trigger('click')

you should call the vue instance $emit function like this:

wrapper.find(ChildComponent).vm.$emit('click')

Hope that helps.

I've used this solution before, but it doesn't seem to work now. On further inspection I noted the following:

// Default stub of a v-text-field (Vuetify) child component
const vTextFieldStub = wrapper.find('v-text-field-stub[name="password"]')

// Output: `Wrapper { isFunctionalComponent: true }`
console.log(vTextFieldStub)

// Output: `undefined`
console.log(vTextFieldStub.vm)

I can't emit the click event because vm is undefined. I assume it is because it is a functional component. How can I emit an event on functional components? Or is it even possible?

Or maybe isFunctionalComponent is a result of the stubbing process? I will have to check.
Here is the result of a v-container (Vuetify) child component stub, which shows that it is not a functional component.

// Output:
//VueWrapper {
//  isFunctionalComponent: undefined,
//  _emitted: [Object: null prototype] {},
//  _emittedByOrder: []
//}
console.log(wrapper.find('v-container-stub'))

I found this question, but I can't seem to get it to work properly.

@nayucolony
Copy link

I think this problem is not solved. could you reopen ?

@nayucolony
Copy link

in my case, I use @click.native Instead of @click it works.

@lukaVarga
Copy link

I have a similar issue with .trigger('click') not dispatching a click on a VBtn in a fully mounted component. So instead of wrapper.find(selector).trigger('click') and then asserting that the click called a method, I am dispatching a classic browser click event, eg:
document.querySelector(selector).dispatchEvent(new Event('click'))

Doing it this way makes my assertion pass as expected.

@chenxeed
Copy link

I have the same issue as well (in beta.24)

In my component, the child component I'm targetting is a functional component, and so it can't trigger its custom event by using vm.$emit.

const wrapper = shallowMount(MyComponent);

// ChildComp is a functional component
const childWrapper = wrapper.find({ name: 'ChildComp' });

// cannot trigger with this method, as `vm` is undefined
childWrapper.vm.$emit('customEvt'); // ERROR: cannot read property $emit of undefined

@ccarstens
Copy link

I am running beta 34 and I can't reproduce any of the solutions.

https://vue-test-utils.vuejs.org/guides/#emitting-event-from-child-component
This does not work, the parent component doesn't receive any events and the event handlers don't fire at all.

@lukaVarga document.querySelector(selector) returns undefined for me..

My test:

    it('increments the internal counter when the + button is clicked', () => {
        const wrap = mount(NumericSpinner)
        wrap.find(SpinnerValueChangeButton).vm.$emit('click')
        console.log(wrap.emitted())
    })

The console.log() outputs [Object: null prototype] {}

@lukaVarga
Copy link

@ccarstens beta 34? Do you mean beta 31 (which is the latest version atm), or beta 24?

Regardless, for document.querySelector(foo) to return something, you have to pass the attachToDocument option (see https://vue-test-utils.vuejs.org/api/options.html#attachtodocument) when mounting the component.

I'm currently running beta 31 and I can do wrapper.find(foo).trigger('click') and it works fine for me for all use-cases (including triggering it and checking that the handler was invoked on eg. a Vuetify component, so <v-btn @click="doSth"></v-btn>, or on a classic html element, like <a href="..." @click="doSthElse"></a> for instance).
After having upgraded from beta 29, I in fact had to adjust tests to not use the method I mentioned in my previous comment (document.querySelector(selector).dispatchEvent(new Event('click'))) anymore and had to change it to wrapper.find(selector).trigger('click')

@sleonardoaugusto
Copy link

sleonardoaugusto commented May 13, 2020

Usually I have a lot of same child components, like buttons, so the implementation wrapper.find(ChildComponent).trigger('click') is not good for that case

@alexmiddeleer
Copy link
Contributor

alexmiddeleer commented Nov 10, 2020

If anyone else stumbles on this thread looking for a way to use shallowMount yet still interact with simple ui-components like buttons, this worked for me:

import ComponentBeingTested from 'Component.vue';
import ButtonComponent from 'ButtonComponent.vue';

....

// In a test:
const wrapper = shallowMount(ComponentBeingTested, {
    stubs: { ButtonComponent } // use real implementation for this "stub"
});
wrapper.find('.first-button-component').trigger('click');
await wrapper.vm.$nextTick();

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

No branches or pull requests