简体   繁体   中英

Unable to test vue component with v-dialog

I have been killing myself trying to figure out how to test a Vue component with a v-dialog, something which worked perfectly fine in Vue2. Currently I am using Vue3, Vitest, Vuetify3.

here is a very simple component which demonstrates a problem

<template>
  <div>
    <v-btn @click.stop="dialog=true" class="open-dialog-btn">click me please</v-btn>
    <v-dialog v-model="dialog" max-width="290" >
      <div class="dialog-content">
        <v-card>welcome to dialog</v-card>
      </div>
    </v-dialog>
  </div>
</template>

<script setup>
import {ref} from "vue";

const dialog = ref(false);
</script>

and here is a unit test for it:

import '../setup';
import { mount } from '@vue/test-utils';
import { createVuetify } from "vuetify";
import HelloDialog from "@/components/HelloDialog.vue";

describe('HelloDialog', () => {
  let wrapper;
  let vuetify;

  beforeEach(() => {
    vuetify = createVuetify();
  });

  describe('dialog tests', () => {
    beforeEach(async () => {
      wrapper = await mount(HelloDialog, {
        global: {
          plugins: [vuetify],
        },
      });
    });

    test('test dialog', async () => {
      expect(wrapper.find('.dialog-content').exists()).toBeFalsy();
      await wrapper.find('.open-dialog-btn').trigger('click');
      console.log(wrapper.html());
      expect(wrapper.find('.dialog-content').exists()).toBeTruthy();
    });
  });
});

the last line in unit test is not working - dialog content is not displayed. Here is an output from wrapper.html() after button is clicked:

<div><button type="button" class="v-btn v-btn--elevated v-theme--light v-btn--density-default v-btn--size-default v-btn--variant-elevated open-dialog-btn"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
    <!----><span class="v-btn__content" data-no-activator="">click me please</span>
    <!---->
    <!---->
  </button>
  <!---->
  <!--teleport start-->
  <!--teleport end-->
</div>

AssertionError: expected false to be truthy
    at ....../HelloDialog.spec.js:27:56

here is test section from vite.config.js :

  test: {
    // https://vitest.dev/config/
    globals:true,
    environment: 'happy-dom',
    setupFiles: "vuetify.config.js",
    deps: {
      inline: ["vuetify"],
    },
  },

and here is vuetify.config.js :

global.CSS = { supports: () => false };

here some versions from package.json :

  "dependencies": {
    "@mdi/font": "7.1.96",
    "@pinia/testing": "^0.0.14",
    "axios": "^1.2.0",
    "dotenv": "^16.0.3",
    "happy-dom": "^8.1.1",
    "jsdom": "^20.0.3",
    "lodash": "^4.17.21",
    "pinia": "^2.0.27",
    "roboto-fontface": "*",
    "vue": "^3.2.45",
    "vuetify": "3.0.6",
    "webfontloader": "^1.0.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "@vue/test-utils": "^2.2.6",
    "vite": "^4.0.3",
    "vite-plugin-vuetify": "^1.0.0-alpha.12",
    "vitest": "^0.26.2"
  }

I have tried everything at this point, and I think the problem has something to do with v-dialog using teleport component. After struggling for several days trying to figure out I settled on using a stub to not use a real dialog when testing but I really don't like this approach.

any ideas would be greatly appreciated

I have the same issue and found the content of v-dialog was rendered in document.body when I called mount() . You can test the dialog content like below.

// expect(wrapper.find('.dialog-content').exists()).toBeTruthy();
expect(document.querySelector('.dialog-content')).not.toBeNull();

I recommend to call unmount() after each test.

afterEach(() => {
  wrapper.unmount()
});

Hope this helps although I doubt it's a good approach because I don't want to care whether the component is using teleport or not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM