Vue-test-utils: Child component using Vue.extends not receiving global $state property from Vuex

Created on 27 Jan 2018  路  8Comments  路  Source: vuejs/vue-test-utils

Version

1.0.0-beta.10

Reproduction link

https://jsfiddle.net/7t28dy1u/

Steps to reproduce

Run the fiddle - console error because this.$store is undefined on the child component.

What is expected?

All child components should receive the global $store mixin from Vuex.

What is actually happening?

Only the root element is receiving the $store property.


I can fix the jsbin by removing the Vue.extend notation and just passing the raw javascript objects into mount. I also confirmed that this works as expected when using Vue to construct the component normally (not using vue-test-utils).

I ran into this because I was using ES6 imports to import my store into all my app components. I switched to using this.$store which works fine in the app, but broke a lot of my unit tests that were using vue-test-utils to mount the components.

discussion

Most helpful comment

Great to see you, Edd in VueConf.US 馃槃
I'm facing similar troubles with Vuex $store, vue-i18n $t and TypeScript.

I agree with Edd's third option if the second option is too much for non-extend way.

mount(Component, {
  localVue,
  extendChildren: true
})

Ideally, don't want to have the weird option. I wonder if we could detect whether Component of mount(Component, {... is by extend or not, could handle child components automatically 馃

All 8 comments

I _think_ this is happening because the constructor of the child component is not extending from the localVue which has the Vuex plugin, so when the child component is constructed the beforeCreate hook that Vuex uses is not present. I'm not too sure how to fix this, if someone can point me in the right direction I may be able to.

As you said, the problem is that you're creating the component with Vue.extend.

There are a few options I can think of to fix this.

First, we could fix the stub option to compile template functions (I think we should do that anyway), then leave it to the user to call localVue.extend on the component and pass it as a stub:

mount(Component, {
  subComponent: localVue.extend(subComponent)
})

Second, we could call localVue.extend on every component in the tree if you use localVue. I don't think we should do this.

Third, we could add an option that would call localVue.extend on every child component.

I'm calling Vue.extend because that's the only way for typescript to work properly with the Vue component definition.

I don't think the first option, of leaving it the user to call localVue.extend is really feasible, because that code is in source files, not test files. You'd have to import the component into the test, and then recurse through the whole components tree and wrap them in localVue.extend.

Great to see you, Edd in VueConf.US 馃槃
I'm facing similar troubles with Vuex $store, vue-i18n $t and TypeScript.

I agree with Edd's third option if the second option is too much for non-extend way.

mount(Component, {
  localVue,
  extendChildren: true
})

Ideally, don't want to have the weird option. I wonder if we could detect whether Component of mount(Component, {... is by extend or not, could handle child components automatically 馃

I am also having troubles with this. LocalVue does not seem capable of passing the $store down to subcomponents. Shallow works fine. I guess I will just stick to doing tests with shallow rendering for now.

We now extend all extended child components automatically

That's awesome, thanks for working on that, Edd!

I think I do observe exactly the problem mentioned in this issue with Async Recursive Components in a Webpack environment...

An abstract of the setup:

// GenericView.ts
export default Vue.extend({
  name: 'GenericView',
  props: ['kind'],
  render: function(h) {
    switch (this.kind) {
      case 'Chart':
        return h(Chart)
      case 'Dashboard':
        return h(Dashboard)
   }
  }
})

// Dashboard.ts
export default Vue.extend({
  name: 'Dashboard',
  components: {
    GenericView: () => import('@/src/components/GenericView')
  },
 template: '<GenericView kind="Chart" />'
})

When testing GenericView and injecting a store, the store is available in the first render of the component, but not in the render for the GenericView loaded by the Dashboard component...

My workaround for now is to stub the GenericView, as paradox as it sounds... This way it will render with the store the first time, but will be stubbed out the second time.

UPDATE:
Figured that it might not be an issue with vue-test-utils, but vue-testing-library: https://github.com/testing-library/vue-testing-library/issues/122

Was this page helpful?
0 / 5 - 0 ratings

Related issues

benm-eras picture benm-eras  路  3Comments

matt-sanders picture matt-sanders  路  3Comments

jonyoder picture jonyoder  路  3Comments

38elements picture 38elements  路  3Comments

kjugi picture kjugi  路  3Comments