2

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.

1 Answer 1

4

Some issues to fix:

  1. 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);
    
  2. The template ref to the State component would be the component's root element, so no need to find() the <select>:

    // ❌
    // await wrapper.findComponent({ ref: 'state-component' }).find('select').setValue(expectedStateValue);
                                                              ^^^^^^^^^^^^^^^
    // ✅
    await wrapper.findComponent({ ref: 'state-component' }).setValue(expectedStateValue);
    
  3. The emitted() object key is the event name, and the value is an array of of arrays, containing emitted data. You can verify the first create-event data contains another object with 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' });
    
  4. 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')
    

demo

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.