Search code examples
vue.jsvitestvue-test-utils

How to test v-text-field rule placeholder's text in v-messages__message?


<v-text-field
    density="compact"
    v-model="textMaterial"
    :rules="textMaterialRules"
    label="Text Material"
></v-text-field>

This is a simple example of how I am using v-text-field in a with a at the bottom of type submit which triggers the rule output to be displayed. The rules are defined as:

export default defineComponent({
  data() {
    return {
      textMaterial: '',
      textMaterialRules: [
        (value: string | undefined) => {
          if (value?.length != 0) return true

          return 'An error message.'
        }
      ]
    }
}

This works fine when testing manually. But testing using vitest I am not able to find the element which displays the rule output. Its class seems to be .v-messages__message.

it('displays error message', async () => {
    const wrapper = mount(Page);
    await wrapper.vm.$nextTick();

    const submitButton = wrapper.find('v-btn[type="submit"]');
    expect(submitButton.exists()).toBe(true);
    
    await submitButton.trigger('click');
    await wrapper.vm.$nextTick();

    // TODO: not able to find v-messages__message elements
    // expect(wrapper.find('.v-messages__message').text()).toContain('the error message above.');
});

Any help will be appreciated, I have been stuck on this for so long. I have tried getting v-form from wrapper.find() and figure out something but nothing good came out of it. wrapper.html() returns only html with v-form and v-text-field so I am thinking is there no way of getting this rules data from this wrapper and there is something else that I am not aware of to be done to make this text work.

Edit: There are 2 such v-text-fields that I am using on this component so I am using findAll generally. Use this to replicate: https://github.com/ksushant881/vue-testing-issue


Solution

  • The test will not actually render the Vuetify components unless you provide it with a Vuetify instance to resolve the components (Reference).

    I modified the example that you provided:

    // src/components/__tests__/HelloWorld.spec.ts
    import { describe, it, expect } from 'vitest'
    
    import { mount, config } from '@vue/test-utils'
    import HelloWorld from '../HelloWorld.vue'
    
    import { createVuetify } from 'vuetify'
    import * as components from 'vuetify/components'
    import * as directives from 'vuetify/directives'
    
    // Create a vuetify instance with all available components
    const vuetify = createVuetify({components, directives});
    
    // This turns off stubs that hide some components
    config.global.stubs.transition = false;
    config.global.stubs['transition-group'] = false;
    
    describe('HelloWorld', () => {
      it('displays error message', async () => {
        const wrapper = mount(HelloWorld, { global: { plugins: [vuetify] } });
    
        // Notice the change from `v-btn` to `.v-btn`,
        // since all vuetify components will now be rendered correctly
        const submitButton = wrapper.find('.v-btn[type="submit"]');
        expect(submitButton.exists()).toBe(true);
        
        let messages = wrapper.find('.v-messages__message');
        expect(messages.exists()).toBe(false);
    
        // Triggering 'click' will not actually do anything,
        // since the validation of the v-text-field is tied 
        // to the 'submit' event of the v-form
        await submitButton.trigger('submit');
    
        // It seems like `await wrapper.vm.$nextTick()` is not needed.
    
        messages = wrapper.find('.v-messages__message');
        expect(messages.exists()).toBe(true);
        expect(messages.text()).toContain('error message');
    });
    })
    

    The configuration needed to be adapted, too (Reference).

    // vitest.config.js
    
    // ...
    export default mergeConfig(
      // ...
      defineConfig({
        test: {
          globals: true, // added
          server: { deps: { inline: ['vuetify'] } }, // added
          // ...
        }
      })
    )