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.