newUser
newUser

Reputation: 51

How to test a teleported dialog in Vue3, using Vuetify3 and vitest?

I have the following problem:

I am trying to write a test for the following Vuetify 3 Dialog using vitest, and jsdom.

The Dialog Component

<template >
  <div class="text-center">
    <v-dialog v-model="dialog" width="auto">
      <template v-slot:activator="{props}">
        <v-btn id="open" to="#my-teleport" @click="dialog=true" color="primary" v-bind="props"> Open Dialog </v-btn>
      </template>
      <v-card>
        <v-card-text>Test Text</v-card-text>
        <v-card-actions>
          <v-btn id="close" color="primary" block @click="dialog = false">Close Dialog</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>

<script>
export default {
  data ({props}) {
    return {
      dialog: false,
    }
  },
}
</script>

The test:

describe('Dialog', async () => {
  beforeEach(() => {
    const el = document.createElement('div');
    el.id = 'my-teleport';
    document.body.appendChild(el);
  });

  afterEach(() => {
    document.body.outerHTML = ''
  })

  test('test open dialog', async () => {
    const wrapper = mount(Dialog, { global: {plugins: [vuetify]}});

    const openBtn = await wrapper.find('#open');
    await openBtn.trigger('click')

    expect(wrapper.vm.dialog).toBe(true)
    console.log(wrapper.html());

    const dialog= wrapper.getComponent('VDialog')
    dialog.getComponent('VBtn')
  });
});

The Error I get when running the test

Error: Unable to get component with selector v-dialog within:

 <div class="text-center"><a class="v-btn v-btn--elevated v-theme--light bg-primary v-btn--density-default v-btn--size-default v-btn--variant-elevated" id="open" modelvalue="true"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
    <!----><span class="v-btn__content" data-no-activator=""> Open Dialog </span>
    <!---->
    <!---->
  </a>
  <!--teleport start-->
  <!--teleport end-->
</div>

Do you know how can I test this or where the problem is? I tried to test according to teleport testing text but it didnt work.

Thanks in advance!

Upvotes: 5

Views: 1794

Answers (2)

Phil
Phil

Reputation: 7607

The problem is that teleport is attaching the dialog on window.body while the wrapper lies deeper within the DOM structure.

The teleport page will not help you as it is about the teleport component which I guess vuetify is using underneath for teleport, but when compiled, it probably is not directly replaceable anymore. The page does give a hint about what to do though:

stubs: {
  teleport: true
}

which stubs the component named <teleport>. You could do the same to stub <v-dialog>:

const wrapper = mount(Dialog, { global: {plugins: [vuetify], stubs: {VDialog: true}}});

Now the v-dialog should be rendered within the wrapper as you can see from console.log(wrapper.html()) and you can both get and set the v-dialog model-value:

  expect(wrapper.find('v-dialog-stub').attributes()).toEqual(
    expect.objectContaining({
      modelvalue: `${true}`
    })
  );

and to change the value (simulating an outside-/close-click):

wrapper.findComponent(VDialog).vm.$emit('update:modelValue', false);

Remember, you don't need to test framework components in unit tests, only your own code. For testing components in integration, you could use Playwright for example.

Upvotes: 1

rgrosjean
rgrosjean

Reputation: 59

I'm currently migrating an old application to vue3/vuetify3 and have the same issue. I'm not completely finished but if you look at the doc (thanks for the link ;)), VDialog is the Navbar of the example. the wrapper is already the VDialog, so in your case you could try:

const button= wrapper.getComponent('VBtn')

Hope this help.

Upvotes: -1

Related Questions