How to test UX of v-dialog (Vuetify) components which is not appears in wrapper?

Hello, friends!

Already two weeks I’ll try to solve a little problem with vue-test-utils and testing of v-dialog component.

Aim: To test proper behaviour of UI.

Chain of Components: CreateProposal -> CreateEditProposal -> List of form fields components.

This is how tested component looks like:

My test is:

import { mount } from '@vue/test-utils'
import Vuetify from 'vuetify'
import VueRouter from 'vue-router'
import Vue from 'vue'
import CreateProposal from '../../../src/components/createProposal'
import Vuex from "vuex";
import TestHelpers from "../../test-helpers";

let wrapper
let h

Vue.use(Vuetify)
Vue.use(VueRouter)
Vue.use(Vuex)

Vue.config.errorHandler = (e) => { console.info(e) }

describe('CreateProposal Test', () => {

  const routes = [{ path: '/proposal/create', name: 'CreateProposal' }]
  const router = new VueRouter({ routes })

  const emptyProposal = {
    aimsAndScope: '',
    title: '',
    valid: false
  }

  const dummyPerson = {
    id: 77,
    firstName: 'Joe',
    lastName: 'Dummy',
    jobTitle: 'Fake employee',
    department: 'Ghost department'
  }

  let store = new Vuex.Store({
    modules: {
      jomPerson: {
        state: {
          data: {
            authInfo: null
          }
        },
        mutations: {
          setAuthInfo (state, authInfo) {
            state.authInfo = authInfo
          }
        },
        getters: {
          getAuthInfo (state) {
            return state.authInfo
          }
        }
      }
    }
  })

  store.commit('setAuthInfo', dummyPerson)

  beforeEach(() => {
    wrapper = mount(CreateProposal, {
      store,
      router,
      sync: false,
      propsData: {
        proposal: emptyProposal,
        isProcessing: false
      },
      attachToDocument: true
    })

    h = new TestHelpers(wrapper, expect)
  })

  it('ui rendered', async () => {
    wrapper.find('button#addr_btn_person-rseditor').trigger('click')
    wrapper.vm.$nextTick()
    expect(wrapper.html()).toContain('Search Responsible Editor')
  })

})

I expect that after clicking a “Open Address Book” button I can check if modal window appears.

But in fact, I can’t do that because mount method does not render it.

After a days of research, I found a way to render v-dialog modal html code using render() method from ‘@vue/server-test-utils’.

import { render } from '@vue/server-test-utils'
import CreateEditProposal from '../../../src/components/createEditComponent/createEditProposal'
import CreateProposal from '../../../src/components/createProposal'
import TestHelpers from '../../test-helpers'

describe('CreateProposal UI', () => {
  let wrapper
  let h

  beforeEach(async () => {
    wrapper = await render(CreateProposal, {
      components: {CreateEditProposal}
    })

    h = new TestHelpers(wrapper, expect)
  })

  it('is rendered all elements correctly', () => {
    h.see('Responsible Editor')
    h.see('Title')
    h.see('Open Address Book')
    h.see('Create')
    h.see('>0 / 256<')
    h.see('Search Responsible Editor')
  })

})

The result HTML contains all I want:

But this wrapper is not a VUE instance. So I cant interact with it with vue-test-utils.

So. Question is:
How to test UI and UX with vue-test-utils and especially with vuetify v-dialog component.

Thanks for any help!

So, friendly and attentive VUE community is not so… I see.

So, anyway, problem is in .html() that not renders dynamic tags.

How to test modal windows is appear or not:

console.log(wrapper.find('#headertext_person-rseditor').isVisible())
wrapper.find('button#addr_btn_person-rseditor').trigger('click')
await wrapper.vm.$nextTick()
console.log(wrapper.find('#headertext_person-rseditor').isVisible())

did you ever find out an answer to this?

The best solution I’ve came up with is to stub the <v-dialog> component directly, and make it render the slots inside the wrapper, not somewhere under document.

<template>
  <div class="v-dialog-mock">
    <slot name="activator" />
    <slot v-if="opened" />
  </div>
</template>

<script lang="ts">
export default {
  name: 'DialogMock',
  props: {
    value: { type: Boolean, required: true },
    fullscreen: { type: Boolean, default: false },
    width: { type: String, default: null }
    // Add more props used on v-dialog here so that they appear on the wrapper.props()
  },
  computed: {
    isOpen: {
      get () {
         return this.value 
      },
      set (val) {
         this.$emit('input', val)
      }
    }
  }
}
</script>
// Unit testing
const wrapper = mount(MyComponent, {
    stubs: {
       'v-dialog': DialogMock,
       'VDialog': DialogMock,
    }
})

// Can test properties passed to `v-dialog` as if it was the real component
wrapper.findComponent({ name: 'DialogMock' }).props() // { value: false, fullscreen: false, width: null }

wrapper.find('.container-inside-dialog-slot') // Works

The downside of this is you have to manually update your mock to cover v-dialog functionalities, but at least you have access to its slot :slight_smile:

issue solved on stackoverflow