import store in routes.js file cause error in jest (vuejs)

1k views Asked by At

I am using vue-test-utils and jest for TDD. I've imported $store in vuejs route file(routes.js) and using store for some purposes. I've written a simple unit test for login page but when I run test, jest will throw the following error before even one of the testes run.

error:

   TypeError: Cannot read property 'state' of undefined

      130 |             isAuth: true,
    > 131 |             layout: $store.state.dom.isMobile ? mobileSinglePage : panel
          |                            ^
 

route.js:

import $store from "../store"

// --------- layouts
import panel from "../layout/panel";
import mobileSinglePage from "../layout/mobileSinglePage";
import MainLayout from "../layout/index";
import auth from "../layout/auth"


// transactions
import _transaction from "../pages/transaction/_transaction"

const routes = [
    {
        path: "",
        component: MainLayout,
        children: [
            {
                path: "",
                redirect: "/panel/dashboard",
            },
            {
                path: "login",
                component: login,
                name: "login",
                meta: { 
                     preventLoggedIn: true,
                     layout: auth
                }
            },
            {
                path: "/panel/transactions/:url",
                name: "_transactions",
                component: _transaction,
                meta: { 
                    isAuth: true,
                    layout: $store.state.dom.isMobile ? mobileSinglePage : panel
                }
            },
            {
                path: "*",
                name: 'error_page',
                redirect: '/404'
            }
        ],
    },
];
export default routes;

login.spec.js



import { mount, createLocalVue } from "@vue/test-utils"
import login from '@/pages/auth/login'
import Vuex from "vuex"

const localVue = createLocalVue()
localVue.use(Vuex)


describe("login", () => {
    it("simple test", () => {
        const wrapper = mount(login, {
            localVue,
            data(){
                return {
                    form: {
                        mobile_number: '',
                    },
                }
            },
        })
        wrapper.find('#login').text()

    })
})

Login.vue

<template>
  <div class="w-100">
    <transition>
      <form @submit.prevent="requestOtp" class="w-100" v-if="step === 1">
        <MobileInput
            ref="mobile_number"
            v-model="form.mobile_number"
            autocomplete="false"
            outlined
            :counter="false"
            label="mobile number"
        />
        <AppButton
            :disabled="!form.mobile_number || form.mobile_number.length !== 11"
            type="submit"
            color="secondary"
            large
            :loading="loading"
            class="w-100 fontMobileMedium font0.875 loginBtn">
          login
        </AppButton>
      </form>
    </transition>
    <transition>
      <form @submit.prevent="verifyOtp" v-if="step === 2">
        <AppPinCode
            autofocus
            dir="ltr"
            :disabled="loading"
            :length="6"
            class="mb-6"
            @complete="verifyOtp"
            v-model="form.otpCode"/>
        <AppButton
            type="submit"
            :loading="loading"
            :disabled="!form.otpCode"
            color="secondary"
            large
            class="w-100 fontMobileMedium font0.875 ">
          login
        </AppButton>
      </form>
    </transition>
  </div>
</template>
<script>
    import {mapActions} from "vuex"
    import MobileInput from "../../components/inputs/MobileInput";
    import AppPinCode from "../../components/inputs/AppPinCode";

    import Mobile from "../../mixins/Mobile";
    import {_setAuthData} from "../../services/utils"

    export default {
        name: "Login",
        mixins: [Mobile],
        data() {
            return {
                ...mapActions('main', ['crud']),
                form: {
                    mobile_number: '',
                    device_info: {
                        hardware: "web",
                        device_id: ""
                    },
                    otpId: null,
                    password: null,
                    otpCode: '',
                },
                step: 1,
                countDownTimer: null,
                loading: false,
                showPass: false,
                interValTime: null,
            }
        },
        components: {
            MobileInput,
            AppPinCode
        },
        methods: {
            async requestOtp() {
                if (!this.form.mobile_number)
                    return
                try {
                    this.loading = true
                    const otp = await this.crud({
                        action: 'post',
                        section: 'auth/otp',
                        data: {mobile_number: this.normalizeMobile(this.form.mobile_number)},
                        auth: false,
                        showError: true,
                    })
                    this.form.otpId = otp.data.id
                } catch (e) {
                    this.step = 2
                    console.log(e)
                } finally {
                    this.loading = false
                }
            },
            async verifyOtp() {
                if (!this.form.otpCode || !this.form.otpId)
                    return
                try {
                    this.loading = true
                    const data = {
                        mobile_number: this.normalizeMobile(this.form.mobile_number),
                        otp: this.form.otpCode
                    }
                    const verify = await this.crud({
                        action: 'patch',
                        section: `auth/otp/${this.form.otpId}`,
                        data,
                        auth: false,
                        showError: true,
                    })
                    const {auth} = verify.data
                    await this.finalLogin(auth)
                } catch (e) {
                    this.form.otpCode = ''
                    console.log(e)
                } finally {
                    this.loading = false
                }
            },
            async finalLogin(userData) {
                const {access_token, refresh_token, expires_in, user} = userData
                await _setAuthData({
                    access_token,
                    refresh_token,
                    expires_in,
                    profile: user
                }, true)
                this.$router.push({name: 'dashboard'})
            },
        },

    }
</script>

store/dom.js(store module)

import breakPoints from "../assets/scss/breakpoints.scss"

export default {
    namespaced: true,
    state: {
        isRtl: true,
        isMobile: false,
        isTablet: false,
        breakpoints: {
            sm: breakPoints.minSm.split("px")[0],
            md: breakPoints.minMd.split("px")[0],
            lg: breakPoints.minLg.split("px")[0],
            xl: breakPoints.minXl.split("px")[0],
        },
        sizeToHideSidebar: breakPoints.minLg.split("px")[0],
        mobileSize: breakPoints.minSm.split("px")[0],
        tabletSize: breakPoints.minMd.split("px")[0],
        AppWindowWidth: window.innerWidth,
        AppWindowHeight: window.innerHeight,
    },
    mutations: {
        SET(state, {key, value}) {
            state[key] = value
        },
        DELETE(state, {key, type = 'string'}) {
            switch (type) {
                case 'array':
                    state[key] = []
                    break
                case 'object':
                    state[key] = {}
                    break
                case 'boolean':
                    state[key] = false
                    break
                default:
                    state[key] = null
            }
        },

        APPEND(state, {key, value}) {
            // console.log(state)
            if (typeof state[key] !== 'object')
                return false

            if (Array.isArray(state[key])) {
                // console.log('also here')
                state[key].push(value)
            }

            if (state[key] && !Array.isArray(state[key]) && state[key] !== null) {
                // console.log(value)
                state[key] = {...state[key], ...value}
                // console.log(state)
            }
        }
    }, 
    actions: {
 
        deviceDetectBySize({commit, state}) {
            const width = window.innerWidth
             commit('SET', {key: 'isTablet', value: width <= state.tabletSize})
             commit('SET', {key: 'isMobile', value: width <= state.mobileSize})
        },
        windowSizeFn({dispatch}) {
            dispatch('deviceDetectBySize')
            window.addEventListener('resize', function (event) {
                dispatch('deviceDetectBySize')
            });
        }
    },
}

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

import main from "./main"
import dom from "./dom"

Vue.use(Vuex)
const Store = new Vuex.Store({
    modules: {
        main,
        dom,
    },
    strict: process.env.NODE_ENV === 'development'
}) 
export default Store

as you can see I've never used router object in my test file but there is error related to it.

1

There are 1 answers

4
laruiss On

So, your Login.vue uses both router (this.$router.push({name: 'dashboard'})) and store (...mapActions('main', ['crud']),), but you give neither on the mount.

Maybe you should give it both:

import { mount, createLocalVue } from "@vue/test-utils"
import login from '@/pages/auth/login'
import Vuex from "vuex"
import VueRouter from "vue-router"

const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)


const store = new Vuex.Store({
  state: {
    // Your minimal default state here
  },
  actions: {
    // at least the 'main.crud' action, here
  }
})

const routes = [
  // Here minimal routes
]

const router = new VueRouter({ routes })

describe("login", () => {
    it("simple test", () => {
        const wrapper = mount(login, {
            router,
            store,
            localVue,
            data(){
                return {
                    form: {
                        mobile_number: '',
                    },
                }
            },
        })
        wrapper.find('#login').text()

    })
})

See here and here for more details.

Also, mapActions() should be in methods rather than data (See here)