Elsa Szymczak
Elsa Szymczak

Reputation: 33

How to use Jest with vuex-module-decorators

I have a vue component written in Typescript where i import a vuex store that was written with vuex-module-decorators. My code works well but when i try to write a unit test using vue-test-utils and jest i'm not able to inject my store.

Here is my vue component :

mycomponent.vue

<template>
  <v-autocomplete
     v-model="job
     :items="jobs"
     outlined
     dense
     @change="editJob"
  />
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { jobsWantedStore } from '../../utils/store-accessor'

@Component
export default class SelectJob extends Vue {

    async editJob() {
            try {
                await jobsWantedStore.EDIT_JOB(this.job)
            } catch (error) {
                this.error = error
            }
        }
    }

}
</script>

The store :

/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Module, VuexModule, Action, Mutation, getModule } from 'vuex-module-decorators'
import { PrioritisedJob } from '~/models'
import JobsWantedService from '~/services/jobs-wanted-service'

@Module({
    name: 'jobsWanted',
    stateFactory: true,
    namespaced: true,
})
export default class JobsWanted extends VuexModule {
    jobsWanted: Array<PrioritisedJob> = []

    // Getters
    public get JobsWanted(): Array<PrioritisedJob> {
        return this.jobsWanted
    }

    // Mutations
    @Mutation
    setJobs(jobsWanted: Array<PrioritisedJob>): any {
        this.jobsWanted = jobsWanted
    }

    @Mutation
    setJob(jobWanted: PrioritisedJob): any {
        this.jobsWanted = this.jobsWanted.map((job) => {
            if (jobWanted.id === job.id) {
                return jobWanted
            }
            return { ...job }
        })
    }


    @Action({ rawError: true })
    public async EDIT_JOB(newJob: PrioritisedJob): Promise<any> {
        try {
            const job = await JobsWantedService.editJob(newJob)
            this.setJob(job)
        } catch (error) {
            throw error.response
        }
    }
}

Store accessor :

/* eslint-disable import/no-mutable-exports */
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import jobsWanted from '../store/jobsWanted'

let jobsWantedStore: jobsWanted

function initialiseStores(store: Store<any>): void {
    jobsWantedStore = getModule(jobsWanted, store)
}

export { initialiseStores, jobsWantedStore }

Here is my test file :

job.spec.js

import { shallowMount, createLocalVue } from '@vue/test-utils'
import mycomponent from '@/components/mycomponent.vue'
import Vuetify from 'vuetify'
import Vuex from 'vuex'

const localVue = createLocalVue()
const vuetify = new Vuetify()
localVue.use(Vuex)

describe('SelectJob', () => {
    let wrapper
    let store
    let actions
    let model

    beforeEach(() => {
    
        actions = {
            EDIT_JOB: jest.fn(),
        }
        store = new Vuex.Store({
            modules: {
                jobsWanted: {
                    namespaced: true,
                    actions,
                },
            },
        })
        wrapper = shallowMount(SelectJob, {
            propsData,
            localVue,
            vuetify,
            store,
        })
    })
    it('should edit a job', () => {
        wrapper.vm.editJob()
        try {
            expect(actions.EDIT_JOB).toHaveBeenCalled()
        } catch (e) {
            expect(e).toMatch('error')
        }
    })
})

It tells me that it received 0 number of calls, and it's because jobsWantedStore is undefined when i run my tests. Anyone knows how to inject a store properly using vuex-module-decorators ?

Upvotes: 1

Views: 1744

Answers (2)

Elsa Szymczak
Elsa Szymczak

Reputation: 33

Turns out I forgot to inject localVue when I declare my wrapper! So it should be:

wrapper = shallowMount(SelectJob, {
    propsData,
    localVue,
    vuetify,
    store,
})

Upvotes: 1

Yaroslav Skripalenko
Yaroslav Skripalenko

Reputation: 36

I ran into the same issue. Just to clarify: component, store module and store accessor described in the question look similar to what I had. Probably we both used examples from vuex-module-decorators GitHub.

I won't include the code from the whole testing spec, because it will be the same as from Elsa, so here's only missing part. The thing that helped me was calling initialiseStores in beforeEach hook, like this:

import { initialiseStores } from '~/store';

describe('MyComponent test', () => {
  ...
  beforeEach(() => {
    initialiseStores(store) // pass your mock store
  })
  ...
}

Upvotes: 1

Related Questions