Vue-test-utils: Watch with immediate totally breaks setProps

Created on 28 Jan 2020  ·  7Comments  ·  Source: vuejs/vue-test-utils

Version

1.0.0-beta.31

Reproduction link

https://github.com/aldarund/vue-test-utils-transition-bug/blob/master/components/watch.test.js
https://github.com/aldarund/vue-test-utils-transition-bug/blob/master/components/watchtest.vue

Steps to reproduce

use watch with immediate as prop

What is expected?

both test run succesfully

What is actually happening?

setProps when there is watch with immediate in component dont do anything, it just dont set prop at all.

Most helpful comment

Well, the usecase pretty simple and general. Test components with different props passed to it. Its not specific to watch in any way.
The only other way that i see is to do full mount each time, but i dont see how its beneficial.
But specifically setProps can be used to see how component reacts on when passed property changed, isnt that how user change it? e.g. change the value in upper level and and see how component react - so for this setProps, and watch

Thanks for your work on test utils too :)

All 7 comments

There is a test for this usecase, well almost https://github.com/vuejs/vue-test-utils/blob/c979087793329fedf4aa400c76a2f6da3f422802/test/specs/wrapper/setProps.spec.js#L83

But if u do add watch in mounted -> it works.
But it dont work if u define watch as prop, not in mounted, so that test dont cover this issue

Hey, thanks for the report. This is definitely a bug. What is your use case for this? There might be a better way than setProps. setProps is really an anti-pattern (the user cannot arbitrarily change the props) and I'm exploring depreciating this method for that reason.

Are you able to share your use case? Happy to provide some alternatives to setProps. Also happy to fix the bug (shouldn't be too difficult) if there is a strong use case this feature.

BTW thanks for your work on Nuxt.js, it is awesome, I love it.

Well, the usecase pretty simple and general. Test components with different props passed to it. Its not specific to watch in any way.
The only other way that i see is to do full mount each time, but i dont see how its beneficial.
But specifically setProps can be used to see how component reacts on when passed property changed, isnt that how user change it? e.g. change the value in upper level and and see how component react - so for this setProps, and watch

Thanks for your work on test utils too :)

The only other way that i see is to do full mount each time, but i dont see how its beneficial.

I think this is ok - then you don't need to await nextTick, you just mount and make your assertion. Here is an example of a refactor in Vuetify where we moved from setProps to a for loop and mounting options - resulted in less code. Let me know what you think.

But specifically setProps can be used to see how component reacts on when passed property changed, isnt that how user change it? e.g. change the value in upper level and and see how component react - so for this setProps, and watch

Kind of - setProps forces a change in the props and does some $forceUpdate. A user would not do this - they click a button (or something) and it would $emit an event, which changes the prop, and the UI updates without $forceUpdate. I don't think this causes any problems in VTU. I think this is a feature we should add to VTU for Vue 3 - some kind of "wrapWithParent" feature, so you can more accurately simulate these situations. Any ideas?

Oddly enough, this passes:

 it('with imeddiate', async () => {
      component.setProps({
        test: 'second'
      })
      await localVue.nextTick()
      component.setProps({
        test: 'second'
      })
      await localVue.nextTick()
      expect(component.vm.test).toBe('second')
    })

I think the problem is if immediate is true, it fires once, and will not fire again until the value changes again. We could probably hack setProps to get this to work, but since most of the focus in on preparing for VTU and Vue 3, I'm not sure I can justify the time to fix this. Would be happy to help out with some ideas on how to fix this if you want to make a PR - message me on discord or try making a PR.

It is likely we will be revisiting watch for the Vue 3 compat; it looks like immediate is going to be the new default behavior (although not called immediate anymore). Although I still think we should depreciate setProps, we will definitely be making this kind of case (async behavior, immediate watchers) a priority for VTU + Vue 3, so I do appreciate all the issues you are reporting - it really helps identify pain points of VTU.

I'll second what was said. User input may not be in the component I'm testing, but the result of it may cause the props being passed into my component to update. So testing that the component appropriately responds to changing props is a very valid use case in my opinion, and one that we test a decent amount.

Yep, setProps is useful and not going anywhere @kspackman 👍 it is available in the Vue 3 integration, found here.

As for this bug, I did not get time to investigate it yet. If you would like to do so, I can probably give you some pointers as to where to start?

@lmiller1990 @aldarund I took a stab at this just as an idea. Maybe this is a terrible idea, but figured we can return a promise wrapping nextTick and checking if the props were set accordingly, and if not, recursively call setProps. This is super hacky, but not sure if there is another way to accomplish this functionality and figured it can't hurt to create a PR :smile:

https://github.com/vuejs/vue-test-utils/pull/1618/files

Was this page helpful?
0 / 5 - 0 ratings