callumacrae / vue-test

:checkered_flag: DEPRECATED: Component testing for Vue.js
90 stars 6 forks source link

Documentation for usage with Vuex #2

Open davidmoshal opened 7 years ago

davidmoshal commented 7 years ago

Hi, awesome library, finding it really useful with Vue 1.x Question: is there a way to use this with Vuex? ie: to bind a component to Vuex, mutate a vuex store, and then check that the changes shows up in the bound component?

Thanks again Dave

callumacrae commented 7 years ago

I guess that there needs to be an option to pass in a Vuex store. I'll add it to my todo list (alternatively, happy to accept a PR).

davidmoshal commented 7 years ago

Thanks, much appreciated, haven't been able to get vuex working yet.

Doesn't help that my setup is a bit atypical: vue-typescript code, with wallabyjs as test runner.

callumacrae commented 7 years ago

https://github.com/callumacrae/vue-test/blob/master/src/MountedComponent.js#L29

If I'm understanding the vuex docs correctly (it's been a while since I set up vuex), it needs a store argument passing in there, and a way to pass it in.

Wondering if it's worth adding more than just basic support for vuex: would more support be useful? Say, if you wanted to write a button and test whether the store had changed.

davidmoshal commented 7 years ago

That part already works with your library. The part that doesn't work is the other way round, IE changing the store and then seeing if a change occurs in a bound view component. IE testing reactivity.

Dave

callumacrae commented 7 years ago

Not sure I follow. Would you be able to provide a code sample of what you want to do?

davidmoshal commented 7 years ago

Sure, am travelling right now will do so when I land.

davidmoshal commented 7 years ago

quick note from the gate. from the vuex 1.0 docs: https://github.com/vuejs/vuex/blob/1.0/docs/en/tutorial.md

Assuming a vuex store:

import Vuex from 'vuex'

const state = {
  count: 0
}

const mutations = {
  INCREMENT (state) {
    state.count++
  }
}

export default new Vuex.Store({
  state,
  mutations
})

and a component: hello.vue

<template>
  <div>
    <h3>Count is {{ counterValue }}</h3>
  </div>
</template>

<script>
import { getCount } from '../vuex/getters'
export default {
  vuex: {
    getters: {
      // note that you're passing the function itself, and not the value 'getCount()'
      counterValue: getCount
    }
  }
}
</script>

the goal would be to be to change the store, and test that the compent changes.

// this part works fine:
describe('mutations', () =>{
  it('INCREMENT', () =>{
    increment()
    expect(store.state.count).to.equal(1)
  })
})

// this part doesn't work
// note: 
describe('Hello.vue not using vue-test', () =>{
  it('should render correct contents', ( done ) =>{
    const vm = new Vue({
      template  : '<hello></hello>',
      components: {Hello}
    }).$mount()
    increment({count: 2})
    Vue.nextTick(() =>{
      console.log(vm)
      done()
    })
  })
})

Reason for this is that our app is completely reactive, and responds to server generated events, which mutate the store. So, we need to make sure that server generated events create the correct ui changes.

davidmoshal commented 7 years ago

Note the second sample doesn't use vue-test but also doesn't work either

davidmoshal commented 7 years ago

And doesn't work with Vue nextTick

davidmoshal commented 7 years ago

Btw, that's a rough outline of the concept, to show the the general idea. I'll post a better version later, with accurate variable names and missing vuex actions.

davidmoshal commented 7 years ago

Ok, here you go, but firstly, an outline of the concept. Given that Vue is a reactive framework, our UI components bind to the store which itself binds to events on the server. How it does that isn't relevant, because we're simulating that by calling actions. The workflow is shown here:

screen shot 2017-01-12 at 12 46 42 pm
davidmoshal commented 7 years ago

So this example works, but it's not using vue-test. It's in Typescript, but I took out the types so it probably works in ES6. It's using the marvelous vue-typescript library which only works in Vue 1.x. (Which is fine, Vue 2.0 doesn't seem like a step forward, and certainly its TS libraries are less mature).

1) the store:

import * as Vuex from 'vuex'
Vue.use(Vuex);

export const store = new Vuex.Store({
  state    : {
    count: 0
  },
  mutations: {
    SET_COUNT( state, value ) {
      state.count = value;
    }
  }
})

export const actions = {
  SET_COUNT( {dispatch}, count ) {
    dispatch('SET_COUNT', count)
  }
}
  1. UI component:
    
    import { VueComponent } from 'vue-typescript'
    import { store, actions } from "./store/Store"

@VueComponent({ template: `

count: {{count}}

          <button @click="inc">increment</button>
        </div>`

}) export class Hello extends Vue{ get count(){ return store.state.count } inc(){ actions.SET_COUNT(store, this.count + 1) } }


3. Test (non vue-test):
```ts
import { Hello } from "../src/Hello"
import { mount, chaiPlugin } from "vue-test"
import * as chai from "chai"
import { expect } from "chai"
import { store, actions } from "../src/store/Store"
chai.use(chaiPlugin)

describe('Hello.vue not using vue-test', () =>{
  actions.SET_COUNT(store, 0)
  expect(store.state.count).to.equal(0)

  it('store mutation should result in correct component data', () =>{
    const vm = new Vue({
      template  : '<hello v-ref:component></hello>',
      components: {Hello}
    }).$mount()

    actions.SET_COUNT(store, 12)
    expect(store.state.count).to.equal(12)
    expect(vm.$refs.component.count).to.equal(12)
  })
})
callumacrae commented 7 years ago

I'm unsure what would need to change in vue-test to get that example to work.

Also, wouldn't it be better to test the value in the DOM, instead of the property? You're just indirectly testing the store.

davidmoshal commented 7 years ago

@callumacrae I published the project here:

https://github.com/davidmoshal/reactive-vue-typescript-testing

I'm unsure what would need to change in vue-test to get that example to work.

True, I wanted to get it working without vue-test first, I think you are correct, probably nothing needs to change for vue-test, will try it later and publish results.

Also, wouldn't it be better to test the value in the DOM, instead of the property? You're just indirectly testing the store.

That depends on what you're testing!

In the simple cases, yes, we want to know that the DOM accurately renders the information in the model, true.

However, in our case the Vuex model itself is bound to a sever side model. In our app, which is essentially a multiplayer game, not all server events should be shown in every browser.

Additionally, our components are complex two-dimensional tables, in which rows, colums, and cells can all change in response to different server events.

Furthermore, some of these are 'computed' in the Vue component.

Therefore we simulate the server events (easy using Vuex actions), and then make sure that each component's view model, including computed values, is correct.

ie: we're testing that a server event results in the correct view models.

Separately, it's also important to know that the view model results in the correct markup as you point out, no disagreement there, but that's testing a different part of the architecture.

Does that make sense?

Next step is to investigate how best to make use of the vue-test functionality. It may indeed be easier, and more useful, in some cases to just test the DOM itself using vue-test.

Dave

callumacrae commented 7 years ago

Makes sense!

Even if this doesn't require any changes in vue-test, it might be a good idea to document it anyway. Let me know what you find out 😃

davidmoshal commented 7 years ago

my thinking exactly. will do. Dave