'After test is run, Error "TypeError: Cannot read property '$model' of undefined..." appears
UPDATE: I've found out that i have to mock certain functions but it is still a hell of a job :)
I've been stuck on this particular problem for almost 3 weeks now and I haven't been able to find a similar case.
I am trying to run vue test utils with vuelidate in Vue3, but everytime I run the test it gives me this error:
src/components/Input/Input.test.ts > Input.vue > looks for error message
TypeError: Cannot read property '$model' of undefined
❯ Proxy._sfc_render src/components/Input/Input.vue:103:61
104|
105| computed: {
106| // If error prop is passed then the class name for the error with the corresponding styling will be used, else the default class name will be used
| ^
107| errorInputStyling(): string {
108| return this.v$.$errors.length === 0 ? 'input-field' : 'input-error';
❯ renderComponentRoot ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:887:44
❯ ReactiveEffect.componentUpdateFn [as fn] ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4963:57
❯ ReactiveEffect.run ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:171:25
❯ setupRenderEffect ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5089:9
❯ mountComponent ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4872:9
❯ processComponent ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4830:17
❯ patch ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4433:21
❯ ReactiveEffect.componentUpdateFn [as fn] ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4970:21
❯ ReactiveEffect.run ../../node_modules/.pnpm/@[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:171:25
Here is the input component:
<template>
<div class="form-group">
<label class="input-label" v-if="label" :for="name">{{
formattedLabel
}}</label>
<input
:class="errorInputStyling"
@input="$emit('update:modelValue', state.value)"
:name="name"
:type="type"
v-bind="$attrs"
v-model="v$.value.$model"
/>
<p class="error-message" v-for="error of v$.$errors" :key="error.$uid">
<strong>{{ error.$message }}</strong>
</p>
</div>
<slot />
</template>
<script lang="ts">
import { defineComponent, reactive, computed, PropType } from 'vue';
import useVuelidate from '@vuelidate/core';
import * as v from '@vuelidate/validators';
export default defineComponent({
name: 'Input',
inheritAttrs: false,
emits: ['update:modelValue'],
props: {
label: {
type: String,
},
modelValue: {
type: String,
required: false,
},
name: {
type: String,
required: true,
},
rules: {
type: Object as PropType<Record<string, any>>,
required: false,
},
type: {
default: 'text',
validator: (value: unknown) => {
return (
typeof value == 'string' &&
['email', 'number', 'password', 'text'].indexOf(value) !== -1
);
},
},
},
setup: function (props) {
const state = reactive({
value: props.modelValue,
});
const rules = computed(() => {
if (!props.rules) {
return {};
}
const values = {} as Record<string, any>;
// we map the rules object to the validators from vuelidate
for (const [key, value] of Object.entries(props.rules)) {
if (typeof value === 'boolean') {
if (value) {
values[key] = (v as any)[key];
console.log(key, value);
}
} else {
values[key] = (v as any)[key](value);
console.log(key, value);
}
}
// after this we append some default validators based on the type
if (props.type === 'email') {
values.email = v.email;
} else if (props.type === 'number') {
values.numeric = v.numeric;
} else if (props.type === 'password') {
values.required = v.required;
}
return { value: values };
});
//@ts-ignore
const v$ = useVuelidate(rules, state, {
$lazy: true,
});
return {
state,
v$,
};
},
And here is the test file:
import { describe, expect, test } from 'vitest';
import { mount } from '@vue/test-utils';
import Input from './Input.vue';
import useVuelidate from '@vuelidate/core';
describe('Input.vue', () => {
test('looks for error message', async () => {
const wrapper = mount(Input, {
global: {
plugins: [useVuelidate],
},
sync: false,
});
const input = wrapper.find('input')
const text = wrapper.find('strong')
await input.setValue('test')
expect(input.element.value).toBe('test')
// input.setValue('Test')
// expect(text.text()).toContain('');
// await wrapper.vm.$nextTick()
// expect(text.text()).toContain('Value is required');
// wrapper.vm.$v.$model();
// wrapper.vm.$v.$nextTick();
// await wrapper.find('input').trigger('keyup');
// await wrapper.vm.$nextTick()
// expect(wrapper.text()).toContain('Test');
});
});
I've come to a wits' end and I would appreciate any help. Thanks in advance!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|