0

I'm new to Vue, but have a project with a login page. I can run it fine, but I'm trying to retro-add unit-testing (I know, I'm doing this backwards). I'm using Mocha + Chai and vue test utils. when I try to shallowMount I get an error saying that it cannot read a property. Here is the full error text:

  1) Login
       Has login text:
     TypeError: Cannot read property 'email' of undefined
      at Proxy.render (webpack:///./src/views/user/Login.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options:65:34)
      at VueComponent.Vue._render (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:3640:22)
      at VueComponent.updateComponent (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4168:21)
      at Watcher.get (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4582:25)
      at new Watcher (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4569:45)
      at mountComponent (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4175:3)
      at VueComponent.Vue.$mount (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:8512:10)
      at init (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:3232:13)
      at createComponent (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:6053:9)
      at createElm (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:6001:9)
      at VueComponent.patch [as __patch__] (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:6611:7)
      at VueComponent.Vue._update (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4039:19)
      at VueComponent.updateComponent (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4168:10)
      at Watcher.get (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4582:25)
      at new Watcher (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4569:45)
      at mountComponent (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:4175:3)
      at VueComponent.Vue.$mount (webpack:///./node_modules/vue/dist/vue.runtime.esm.js?:8512:10)
      at mount (webpack:///./node_modules/@vue/test-utils/dist/vue-test-utils.js?:13265:21)
      at shallowMount (webpack:///./node_modules/@vue/test-utils/dist/vue-test-utils.js?:13278:10)
      at Context.eval (webpack:///./tests/unit/login.spec.js?:17:87)
      at processImmediate (internal/timers.js:439:21)

Here is my packages file:

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test:unit": "vue-cli-service test:unit",
    "test:e2e": "vue-cli-service test:e2e",
    "test": "mochapack --webpack-config webpack.config.js --require tests/setup.js tests/**/*.spec.js"
  },
  "dependencies": {
    "axios": "^0.19.2",
    "core-js": "^3.6.4",
    "vue": "^2.6.11",
    "vue-axios": "^2.1.5",
    "vue-router": "^3.1.5",
    "vuelidate": "^0.7.5",
    "vuetify": "^2.1.0",
    "vuex": "^3.1.2"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^4.2.0",
    "@vue/cli-plugin-e2e-cypress": "^4.2.0",
    "@vue/cli-plugin-router": "^4.2.0",
    "@vue/cli-plugin-unit-mocha": "^4.2.0",
    "@vue/cli-plugin-vuex": "^4.2.0",
    "@vue/cli-service": "^4.2.0",
    "@vue/test-utils": "^1.0.0-beta.31",
    "chai": "^4.1.2",
    "jsdom": "^16.1.0",
    "jsdom-global": "^3.0.2",
    "mocha": "^7.0.1",
    "mochapack": "^1.1.13",
    "node-sass": "^4.12.0",
    "sass": "^1.19.0",
    "sass-loader": "^8.0.2",
    "vue-cli-plugin-vuetify": "^2.0.4",
    "vue-template-compiler": "^2.6.11",
    "vuetify-loader": "^1.3.0"
  }
}

Here is my component code:

<template>
  <v-container fluid fill-height>
    <v-layout align-center justify-center>
      <v-flex xs12 sm8 md4>
        <v-card class="elevation-12">
          <v-toolbar color="primary" dark flat>
            <v-toolbar-title>Login form</v-toolbar-title>
          </v-toolbar>
          <v-card-text>
            <v-form @submit.prevent="login">
              <v-text-field
                autofocus
                v-model="email"
                label="Email"
                prepend-icon="mdi-account-circle"
                @blur="$v.email.$touch()"
              />
              <div class="red--text text--lighten-1" v-if="$v.email.$error">
                <div v-if="!$v.email.required">
                  <v-icon color="red">mdi-alert-circle-outline</v-icon>
                  Email is required
                </div>
                <div v-if="!$v.email.email">
                  <v-icon color="red">mdi-alert-circle-outline</v-icon>
                  Invalid email address.
                </div>
              </div>

              <v-text-field
                v-model="password"
                :type="showPassword ? 'text' : 'password'"
                label="Password"
                prepend-icon="mdi-lock"
                :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
                @click:append="showPassword = !showPassword"
                @blur="$v.password.$touch()"
              />

              <div
                class="red--text text--lighten-1"
                v-if="$v.password.$error && !$v.password.required"
              >
                <v-icon color="red">mdi-alert-circle-outline</v-icon>
                Password is required
              </div>

              <v-btn
                type="submit"
                color="success"
                name="button"
                :disabled="$v.$invalid"
              >
                Login now
              </v-btn>

              <v-btn
                text
                small
                color="primary"
                :to="{ name: 'forgotPassword' }"
              >
                Forgot your password?
              </v-btn>

              <div v-if="error" class="red--text text--lighten-1">
                <v-icon color="red">mdi-alert-circle-outline</v-icon>
                {{ getLoginErrorMsg() }}
              </div>
            </v-form>
          </v-card-text>
        </v-card>
      </v-flex>
    </v-layout>
  </v-container>
</template>

<script>
import { required, email } from "vuelidate/lib/validators";

export default {
  name: "Login",

  data() {
    return {
      email: "",
      password: "",
      error: null,
      showPassword: false
    };
  },

  validations: {
    email: {
      required,
      email
    },
    password: {
      required
    }
  },

  methods: {
    login() {
      console.log("Entering component login method");

      this.$store
        .dispatch("user/login", {
          username: this.email,
          password: this.password
        })
        .then(() => {
          console.log(
            'this.$store.getters["user/passwordChangeRequired"]' +
              this.$store.getters["user/passwordChangeRequired"]
          );
          if (this.$store.getters["user/passwordChangeRequired"]) {
            console.log("push to changePassword");
            this.$router.push({ name: "changePassword" });
          } else {
            console.log("pushing to home");
            this.$router.push({ name: "home" });
          }
        })
        .catch(err => {
          this.error = err.response;
        });
    },

    getLoginErrorMsg() {
      if (this.error.status == 401) {
        return "Invalid username or password";
      } else {
        // return `Login failed: {this.error.statusText}`;
        return "failed";
      }
    }
  }
};
</script>

<style></style>

Here is the test file:

import { expect } from "chai";
import { mount, shallowMount } from "@vue/test-utils";
import Login from "../../src/views/user/Login.vue";

describe("Login", () => {

  it("Has login text", () => {
    // const wrapper = mount(Login);
    const wrapper = shallowMount(Login);
    // not getting here.
    expect(2).to.equal(2);
  });
});

Note: neither mount or shallowMount works. They both throw the same error.

Here is my webpack.config.js file:

const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // this will apply to both plain `.js` files
      // AND `<script>` blocks in `.vue` files
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // this will apply to both plain `.css` files
      // AND `<style>` blocks in `.vue` files
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // make sure to include the plugin for the magic
    new VueLoaderPlugin()
  ]
}

I've spent hours googling and I can't really find anything. So, I'm clearly doing something really stupid, because no one else seems to be having this issue. Please tell me why it is throwing this error and unable to shallowMount or mount this vue component.

1 Answer 1

1

It appears that my tests/unit/index.js was missing some things... I didn't have vuelidate in there and I believe that was the issue. I changed it to the following and it now works:

// index.js
import Vue from 'vue';
import Vuetify from 'vuetify';
import VueRouter from 'vue-router';
import Vuelidate from "vuelidate";


Vue.config.productionTip = false;
Vue.use(Vuetify);
Vue.use(VueRouter);
Vue.use(Vuelidate);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.