Unit testing vuejs methods using Jest - javascript

I'm trying to test the methods in jest framework. For javascript methods i was able to import the methods from file using require() function and test it (expect(addition(1,2).toBe(3))).
But unable to replicate the same in VueJS methods.
// VueJS
export default defineComponent({
name: "App",
components: {
HelloWorld,
},
methods: {
addition(a: number, b: number) {
return a + b;
},
subtraction(a: number, b: number) {
return a - b;
},
multiplication(a: number, b: number) {
return a * b;
},
},
});
test file
import addition from "./App.vue"
describe("HelloWorld.vue", () => {
it("testing vue method", () => {
expect(addition(1,2).toBe(3));
});
});
//error on line 4 states "Value of type 'DefineComponent<{}, {}, any, ComputedOptions, MethodOptions, ComponentOptionsMixin,
// ComponentOptionsMixin, ... 4 more ..., {}>' is not callable. Did you mean to include 'new'?"

The App.vue file is compiled into a component definition, which is used to instantiate the App component with Vue Test Util's mount or shallowMount, which results in a test wrapper. The wrapper has a vm property that provides direct access to the component's methods and props.
So your test should look similar to this:
import { shallowMount } from "#vue/test-utils";
import App from "./App.vue";
describe("App.vue", () => {
it("testing vue method", () => {
const wrapper = shallowMount(App);
expect(wrapper.vm.addition(1,2)).toBe(3);
});
});

Related

Can't pass data from main.js to App.vue in Vue js?

In my main.js I do authentication and after that I populate a data property. I'm trying to pass that data property to the App component but seems not possible?
main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
data: {
test: 'test'
},
//render: h => h(App)
render: h => h(App, { props: { 'test': this.test }})
}).$mount('#app')
App.vue
<template>
<div id="app" :test="test">
<h1>{{test}}</h1>
</div>
</template>
<script>
export default {
name: 'app',
props: ['test']
}
</script>
Just gives error as test and/or this.test is undefined. Passing a hard-coded value works. Without using props on the render line has no errors but App doesn't receive data. Am I just doing it wrong?
You can set render to a normal function in order for this to refer to the Vue instance:
render: function (h) {
return h(App, { props: { test: this.test } });
}
Your problem is that you're defining render as an anonymous arrow function. which means that it's this context isn't bind to you Vue instance. if you want a function's context to be the "hosting" object, you must define it with the function syntax.
new Vue({
data: {
test: 'test'
},
render(h) {
return h(App, { props: { 'test': this.test }})
}
}).$mount('#app')
you can read about arrow function vs regular functions here

Vue JS - custom properties on vue component gives TS errors

I have created some Vue middleware and I am trying to add a custom property to one of my components in Vue like so:
middleware.js:
import { VueConstructor } from 'vue/types';
function eventPlugin(vue: VueConstructor): void {
const Socket = new someClass();
Object.defineProperties(vue.prototype, {
$socket: {
get: function get() {
return Socket;
},
},
});
vue.$socket = Socket;
}
myComponent.js
const MyComponent = Vue.extend({
name: 'MyComponent',
$socket: {
event(data: any) {
}
},
methods: {
MyMethod() {
}
}
})
app.js
import Vue from 'vue';
import eventPlugin from './middleware.js';
import MyComponent from './myComponent.js'
Vue.use(eventPlugin);
export default new Vue({
render: (h) => h(MyComponent),
}).$mount('#app');
The custom property I am trying to add here is obviously socket. The problem is when I add it I get typescript errors:
Object literal may only specify known properties, and 'socket' does
not exist in type 'ComponentOptions<Vue, DefaultData,
DefaultMethods, DefaultComputed, PropsDefinition<Record<string,
any>>, Record<...>>'.
As you can see in middleware.js I have tried defining the property there so I am not sure why I am receiving the error?
When adding instance properties or component options, you also need to augment the existing type declarations.
Based on Augmenting Types for Use with Plugins (Vue 2):
To type-hint the $socket instance property:
declare module 'vue/types/vue' {
interface VueConstructor {
$socket: string
}
}
export {}
To type-hint the $socket component option:
import Vue from 'vue'
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
$socket?: string
}
}
export {}
The type declarations above should go in a .d.ts file in your src directory. If using VS Code, any new .d.ts files might require restarting VS Code to load.

how to mock and test this.$parents in vue using vue-test-utils

I am trying to test a vue component that uses this.$parents.props in it.
e.g.
export default class item extends Vue {
private point = this.$parent.$props.point;
}
The reason why I think I need to mock this.$parents.$props is because I get an error like this.
TypeError: Cannot read property 'point' of undefined
I have tried mounting options, parentComponent, but it throws an error saying "[vue-test-utils]: options.parentComponent should be a valid Vue component options object".
This is the code I used for testing.
import Parent from "#/components/Parent.vue";
let wrapper: any;
describe("item.vue", () => {
it("item vue testing", () => {
wrapper = mount(item, {
propsData: {
num: 1,
},
parentComponent: Parent
});
});
});
What should I do to mock this.$parent.$props? What is the mistake in the above test code?
you can mock like that
describe("item.vue", () => {
it("item vue testing", () => {
wrapper = mount(item, {
propsData: {
num: 1,
},
parentComponent: Parent
});
//with functions
wrapper.vm.$parent= jest.fn().mockResolvedValue("return")
//or property
wrapper.vm.$parent.$props.point = "value"
});
});

how to access "this" in props validator

I'm working on a project using nuxt.js, I'm injecting a function in the context of the application as recommended in the official documentation
https://nuxtjs.org/guide/plugins/#inject-in-root-amp-context
but when I try to call the function inside a props validation I get an error
/plugins/check-props.js
import Vue from 'vue'
Vue.prototype.$checkProps = function(value, arr) {
return arr.indexOf(value) !== -1
}
in a component vue
export default {
props: {
color: {
type: String,
validator: function (value, context) {
this.$checkProps(value, ['success', 'danger'])
}
}
}
ERROR: Cannot read property '$checkProps' of undefined
Does anyone know how I can access "this" within validation?
thanks in advance!
Props validation is done before the component is initialized, so you won't have access to this as you are extending Vue.prototype.
Form their documentation:
Note that props are validated before a component instance is created, so instance properties (e.g. data, computed, etc) will not be available inside default or validator functions.
In general, if $checkProps is only used for checking the value of these props, I would just use a helper function.
// array.helpers.js
export function containsValue(arr, val) {
return arr.indexOf(value) !== -1
}
// component
import { containsValue } from 'path/to/helpers/array.helpers';
props: {
foo: {
//
validator(value) {
return containsValue(['foo', 'bar'], value);
}
}
}
Update
Based on your comments, if you don't want to import this specific function over and over again, you can just Array.prototype.includes see docs
// component
props: {
color: {
//
validator(value) {
return ['success', 'danger'].includes(value);
}
}
}
From the doc:
props are validated before a component instance is created, so
instance properties (e.g. data, computed, etc) will not be available
inside default or validator functions
If you want to access the nuxt plugins you can always use the window object.This is how I do it to access the i18n library
{
validator: (value: any): boolean => {
return window.$nuxt.$te(value);
}
}
In your case:
{
validator: (value: any): boolean => {
return window.$nuxt.$checkProps(value, ['success', 'danger']);
}
}
In any case you should never prototype in the window object. Let nuxt handle it with a plugin:
path: plugins/check-props.js or .ts
function checkProps(value: any, arr: string[]): boolean {
return arr.indexOf(value) !== -1
}
const $checkProps: Plugin = (_context: Context, inject: Inject) => {
inject('checkProps', checkProps);
};
export default $checkProps;
And then in nuxt config
{
plugins: [{ src: 'plugins/check-props.js', ssr: true }]
}

Setting up a utility class and using it within a vue component

A vue application I am working on currently has lots of code redundancies relating to date functions. In an effort to reduce these redundancies, I'd like to create a utility class as shown below, import it and set it to a Vue data property within the component, so I can call the date functions within it.
I am not certain on the best way to implement this. The current implementation results in an error saying TypeError: this.dates is undefined and my goal is not only to resolve this error but create/utilize the class in the Vue environment using best standards.
Importing utility class
import Dates from "./utility/Dates";
...
Component
const contactEditView = Vue.component('contact-edit-view', {
data() {
return {
contact: this.myContact
dates: Dates
}
},
...
Dates.js
export default {
dateSmall(date) {
return moment(date).format('L');
},
dateMedium(date) {
return moment(date).format('lll');
},
dateLarge(date) {
return moment(date).format('LLL');
}
};
View
Date of Birth: {{ dates.dateMedium(contact.dob) }}
My suggestion for this is to use a plugin option in Vue. About Vue plugin
So you will crate a new folder called services, add file yourCustomDateFormater.js:
const dateFormater = {}
dateFormater.install = function (Vue, options) {
Vue.prototype.$dateSmall = (value) => {
return moment(date).format('L')
}
Vue.prototype.$dateMedium = (value) => {
return moment(date).format('lll')
}
}
In main.js:
import YourCustomDateFormater from './services/yourCustomDateFormater'
Vue.use(YourCustomDateFormater)
And you can use it anywhere, like this:
this.$dateSmall(yourValue)
Or, if you want to use mixin. Read more about mixin
Create a new file dateFormater.js
export default {
methods: {
callMethod () {
console.log('my method')
}
}
}
Your component:
import dateFormater from '../services/dateFormater'
export default {
mixins: [dateFormater],
mounted () {
this.callMethod() // Call your function
}
}
Note: "Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It’s also a good idea to ship them as Plugins to avoid duplicate application." - Vue documentation
dateUtilsjs
import moment from 'moment-timezone'
function formatDateTime(date) {
return moment.utc(date).format("M/D/yyyy h:mm A")
}
export { formatDateTime }
Component JS
...
import { formatDateTime } from '../utils/dateUtils'
...
methods: {
formatDateTime,
}
Used within component
{{ formatDateTime(date) }}

Categories