I'm trying to use findComponent
with find
method to find a child component's element and set it's value. But every time I run test, it gives me Cannot call setValue on an empty DOMWrapper.
error.
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>
I'm fairly new to VueJS so I'm open to all tips and tricks, thanks.
CodePudding user response:
Some issues to fix:
You don't need to attach the component to the document, so remove that:
// ❌ // const div = document.createElement('div'); // div.id = 'root'; // document.body.append(div); // const wrapper = mount(Create, { attachTo: '#root' }); // ✅ const wrapper = mount(Create);
The template ref to the
State
component would be the component's root element, so no need tofind()
the<select>
:// ❌ // await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue); ^^^^^^^^^^^^^^^ // ✅ await wrapper.findComponent({ ref: 'state-component' }).setValue(expectedStateValue);
The
emitted()
object key is the event name, and the value is an array of of arrays, containing emitted data. You can verify the firstcreate
-event data contains another object withtoMatchObject(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' });
The last assertion tries to find
input[name='state']
, but that's actually a<select>
, not an<input>
:// ❌ // expect(wrapper.find(`input[name='state']`).element.value).toEqual('Pending') ^^^^^ // ✅ expect(wrapper.find(`select[name='state']`).element.value).toEqual('Pending')