VueJS test-utils 无法在子组件中找到元素
VueJS test-utils can't find element inside child component
我正在尝试使用 findComponent
和 find
方法来查找子组件的元素并设置它的值。但是每次我 运行 测试时,它都会给我 Cannot call setValue on an empty DOMWrapper.
错误。
Test file
import { mount } from '@vue/test-utils';
import Create from './Create.vue';
// import State from '@/components/State.vue';
describe('it tests Create component', () => {
test('it emits create event and resets the form when form is valid and create button is clicked', async () => {
const div = document.createElement('div');
div.id = 'root';
document.body.append(div);
const expectedNameValue = 'TODO_NAME';
const expectedStateValue = 'Pending';
const wrapper = mount(Create, {
attachTo: '#root',
});
await wrapper.find(`input`).setValue(expectedNameValue);
await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue);
await wrapper.find(`form`).trigger('submit');
expect(wrapper.emitted().create).toBeTruthy();
expect(wrapper.emitted().create[0]).toEqual([expectedNameValue]);
expect(wrapper.emitted().create[1]).toEqual(['Pending']);
expect(wrapper.find(`input[name='name']`).element.value).toEqual('');
expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending');
});
});
Create
component
<template>
<form @submit.prevent="createTodo" class="flex gap-2 w-full">
<input class="flex-1 shadow rounded-md p-2 focus:ring-2 focus:ring-blue-900 focus:outline-none" type="text" placeholder="Todo Name" name="name" required/>
<State ref="state-component"/>
<button type="submit" class="rounded-md shadow text-white bg-blue-700 py-2 px-6">Create</button>
</form>
</template>
<script>
import State from '@/components/State.vue';
export default {
components: { State },
emits: ['create'],
methods: {
createTodo(event) {
const elems = event.target.elements;
const todo = { name: elems.name.value, state: elems.state.value };
this.$emit('create', todo);
elems.name.value = '';
elems.state.value = 'Pending';
}
}
}
</script>
<style scoped>
</style>
State
component
<template>
<select id="state-select" class="rounded-md bg-green-200 text-white" name="state">
<option
v-for="(state, index) in states"
:selected="isSelected(state)"
:key="index"
>
{{ state }}
</option>
</select>
</template>
<script>
export default {
props: ["todo", "index"],
data() {
return {
name: "",
state: "",
states: ["Pending", "In Progress", "Done"],
};
},
created() {
if(!this.todo) return true;
this.state = this.todo.state;
this.name = this.todo.name;
},
methods: {
isSelected(equivalent){
return equivalent === this.state;
}
}
};
</script>
<style scoped></style>
我是 VueJS 的新手,所以我愿意接受所有提示和技巧,谢谢。
需要解决的一些问题:
您不需要将组件附加到文档,因此删除:
// ❌
// const div = document.createElement('div');
// div.id = 'root';
// document.body.append(div);
// const wrapper = mount(Create, { attachTo: '#root' });
// ✅
const wrapper = mount(Create);
State
组件的模板引用将是组件的根元素,因此不需要 find()
<select>
:
// ❌
// await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue);
^^^^^^^^^^^^^^^
// ✅
await wrapper.findComponent({ ref: 'state-component' }).setValue(expectedStateValue);
emitted()
对象键是事件名称,值是数组的数组,包含发出的数据。您可以验证第一个 create
事件数据包含另一个对象 toMatchObject(object)
:
// ❌
// expect(wrapper.emitted().create[0]).toEqual([expectedNameValue]);
// expect(wrapper.emitted().create[1]).toEqual(['Pending']);
// ✅
expect(wrapper.emitted().create[0][0]).toMatchObject({ name: expectedNameValue, state: 'Pending' });
最后一个断言试图找到 input[name='state']
,但那实际上是 <select>
,而不是 <input>
:
// ❌
// expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending')
^^^^^
// ✅
expect(wrapper.find(`select[name='state']`).element.value).toEqual('Pending')
我正在尝试使用 findComponent
和 find
方法来查找子组件的元素并设置它的值。但是每次我 运行 测试时,它都会给我 Cannot call setValue on an empty DOMWrapper.
错误。
Test file
import { mount } from '@vue/test-utils';
import Create from './Create.vue';
// import State from '@/components/State.vue';
describe('it tests Create component', () => {
test('it emits create event and resets the form when form is valid and create button is clicked', async () => {
const div = document.createElement('div');
div.id = 'root';
document.body.append(div);
const expectedNameValue = 'TODO_NAME';
const expectedStateValue = 'Pending';
const wrapper = mount(Create, {
attachTo: '#root',
});
await wrapper.find(`input`).setValue(expectedNameValue);
await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue);
await wrapper.find(`form`).trigger('submit');
expect(wrapper.emitted().create).toBeTruthy();
expect(wrapper.emitted().create[0]).toEqual([expectedNameValue]);
expect(wrapper.emitted().create[1]).toEqual(['Pending']);
expect(wrapper.find(`input[name='name']`).element.value).toEqual('');
expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending');
});
});
Create
component
<template>
<form @submit.prevent="createTodo" class="flex gap-2 w-full">
<input class="flex-1 shadow rounded-md p-2 focus:ring-2 focus:ring-blue-900 focus:outline-none" type="text" placeholder="Todo Name" name="name" required/>
<State ref="state-component"/>
<button type="submit" class="rounded-md shadow text-white bg-blue-700 py-2 px-6">Create</button>
</form>
</template>
<script>
import State from '@/components/State.vue';
export default {
components: { State },
emits: ['create'],
methods: {
createTodo(event) {
const elems = event.target.elements;
const todo = { name: elems.name.value, state: elems.state.value };
this.$emit('create', todo);
elems.name.value = '';
elems.state.value = 'Pending';
}
}
}
</script>
<style scoped>
</style>
State
component
<template>
<select id="state-select" class="rounded-md bg-green-200 text-white" name="state">
<option
v-for="(state, index) in states"
:selected="isSelected(state)"
:key="index"
>
{{ state }}
</option>
</select>
</template>
<script>
export default {
props: ["todo", "index"],
data() {
return {
name: "",
state: "",
states: ["Pending", "In Progress", "Done"],
};
},
created() {
if(!this.todo) return true;
this.state = this.todo.state;
this.name = this.todo.name;
},
methods: {
isSelected(equivalent){
return equivalent === this.state;
}
}
};
</script>
<style scoped></style>
我是 VueJS 的新手,所以我愿意接受所有提示和技巧,谢谢。
需要解决的一些问题:
您不需要将组件附加到文档,因此删除:
// ❌ // const div = document.createElement('div'); // div.id = 'root'; // document.body.append(div); // const wrapper = mount(Create, { attachTo: '#root' }); // ✅ const wrapper = mount(Create);
State
组件的模板引用将是组件的根元素,因此不需要find()
<select>
:// ❌ // await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue); ^^^^^^^^^^^^^^^ // ✅ await wrapper.findComponent({ ref: 'state-component' }).setValue(expectedStateValue);
emitted()
对象键是事件名称,值是数组的数组,包含发出的数据。您可以验证第一个create
事件数据包含另一个对象toMatchObject(object)
:// ❌ // expect(wrapper.emitted().create[0]).toEqual([expectedNameValue]); // expect(wrapper.emitted().create[1]).toEqual(['Pending']); // ✅ expect(wrapper.emitted().create[0][0]).toMatchObject({ name: expectedNameValue, state: 'Pending' });
最后一个断言试图找到
input[name='state']
,但那实际上是<select>
,而不是<input>
:// ❌ // expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending') ^^^^^ // ✅ expect(wrapper.find(`select[name='state']`).element.value).toEqual('Pending')