在route.js文件中导入存储会导致玩笑vuejs错误

问题描述

我正在将vue-test-utils和jest用于TDD。 我已经将$ store导入vuejs路由文件(routes.js)中,并出于某些目的使用store。 我已经为登录页面编写了一个简单的单元测试,但是当我运行测试时,开玩笑会在甚至没有一个测试者运行之前抛出以下错误

错误

   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,})
                    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,profile: user
                },true)
                this.$router.push({name: 'dashboard'})
            },}
</script>

store / dom.js(商店模块)

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,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,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

如您所见,我从未在测试文件中使用过路由器对象,但存在与之相关的错误

解决方法

因此,您的Login.vue同时使用路由器(this.$router.push({name: 'dashboard'}))和存储(...mapActions('main',['crud']),),但您都不在安装架上使用。

也许您应该同时给它们:

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()

    })
})

有关更多详细信息,请参见herehere

此外,mapActions()应该在methods中,而不是data(请参阅here