vuejs / test-utils

Vue Test Utils for Vue 3
https://test-utils.vuejs.org
MIT License
1.04k stars 244 forks source link

AsyncComponent can't be mounted #627

Closed m-barrera closed 3 years ago

m-barrera commented 3 years ago

Hello I encountered an issue when using asyncComponents the mount seems to fail.

For my Icons Component, I use a base class that used asyncComponents to lazy load the iconname.vue files which consists only out of a template containing some props to change the color and size of the svg code part. When trying to test my components by using mount it does not mount the asyncComponent in this case the iconname.vue file. Visually everything works as expected without any problems. But the wrapper returns following

VueWrapper {
  wrapperElement: Comment {},
  __app: null,
  rootVM: {},
  componentVM: {
    iconName: [Getter/Setter],
    size: [Getter/Setter],
    iconColor: [Getter/Setter],
    animation: [Getter/Setter],
    getIconComponent: [Getter/Setter],
    hasOwnProperty: [Function (anonymous)]
  },
  __setProps: undefined
}

The mounted part looks like that:

<div id="target"><div data-v-app=""><!----></div></div>

My Vue File which import a svg template.

<template>
     <component :is="getComponent" />
</template>

<script>
export default {
  name: "Icon",
  props: {
      element: {
          type: String,
          default: 'Exit'
      }
 },
 computed: {
     getComponent() {
         return defineAsyncComponent(() => 
             import(`../icons/${this.element}.vue`)
         )
     }
 }
</script>
describe("Icons", () => {
  global.SVGElement = Element;
  let wrapper;
  describe("passing props should override deaulf icon props", () => {
    beforeEach(async () => {
      (document.body.innerHTML = `<div id="target"></div>`),
        (wrapper = mount(Icon, {
          propsData: {
            element: "appartment",
          },
          attachTo: document.getElementById("target"),
        }));
    });
    it("check if its mounted", () => {
      console.log("1:" + document.body.innerHTML);
      console.log(wrapper.findComponent(Icon)); // returns [Object: null prototype] {}
      console.log(wrapper.findComponent(Appartment)); // returns [Object: null prototype] {}
    });

Dependencies: -"@vue/cli-plugin-unit-mocha": "~4.5.0" -"chai": "^4.1.2" -"@vue/test-utils": "^2.0.0-rc.6",

I am not sure if it's a bug or I am doing it wrong. Thanks for helping me. Kind regards

cexbrayat commented 3 years ago

Have you tried calling await flushPromises() before wrapper.findComponent? As the resolution is async, I wouldn't be surprised that you need to wait for it before checking your template. See https://next.vue-test-utils.vuejs.org/guide/advanced/async-suspense.html for more info

If that doesn't help, maybe it's an issue. It would be ideal if you could provide a simplified repro, either as a github repo or, even better, by adding a failing unit test to this project (the simpler, the better).

m-barrera commented 3 years ago

@cexbrayat Thank you for the quick reply using await flushPromises() did not solve it for me. Here a quick replication repo https://github.com/m-barrera/vue-test-asyncComponent-issue

I am still unsure if it is an issue or just me doing something wrong.

Kind regards

lmiller1990 commented 3 years ago

Are you using Jest? Assuming so - is lazy load (via import) something supported in Jest?

Edit: I guess it is, we have this test using import.

lmiller1990 commented 3 years ago

This is an issue in your configuration or code base.

I pasted your exact code into this code base and it worked as expected. Here's the branch: https://github.com/vuejs/vue-test-utils-next/compare/repro-627?expand=1

Happy to help explore the problem in your code base more, but I don't think this is a bug, so I will close this one for now. I'll take a closer look at your code base in the meantime.

image

lmiller1990 commented 3 years ago

I pulled your code-base. I keep getting:

 Icons
    test async
[Vue warn]: Unhandled error during execution of async component loader
  at <AsyncComponentWrapper>
  at <Icon element="circle" ref="VTU_COMPONENT" >
  at <VTUROOT>
(node:41068) UnhandledPromiseRejectionWarning: Error: Cannot find module './circle.vue'
    at webpackEmptyContext (/Users/lachlan/code/dump/repros/vue-test-asyncComponent-issue/dist/js/webpack:/src/components sync:2:1)

I see the problem. I changed it to hard code (() => import('./circle.vue') and it worked. Using a dynamic import does not work in mochapack. I wonder if webpack is doing some static analysis, and cannot create a chunk for the dynamic import. That would explain the problem.

Not really a fix, but have you considered using Jest instead of mocha + webpack combo? It seems to "just work" in Jest.

lmiller1990 commented 3 years ago

I tried a bunch of things but I could not get an import with string interpolation to work with mocha + webpack. I think there should be some way to do this, I am not sure what it is. I'd like to know.

m-barrera commented 3 years ago

Hi first of all thank you for your investigation. I just tested the repo I prepared and yes you are right this error occurs but strangely only in this repo. In the main repo it does not complain about not finding the vue file. The only diffrence in the main repo are storybook and our vue.config.js which only handles automatic imports for scss files.

  chainWebpack: (config) => {
    if (process.env.NODE_ENV === 'test') {
      const scssRule = config.module.rule('scss')
      scssRule.uses.clear();
      scssRule
        .use('null-loader')
        .loader('null-loader')
    }
  },
  css: {
    loaderOptions: {
      scss: {
        additionalData: `
          @import "~@/style/_variables.scss";
          @import "~@/style/_normalize.scss";
          @import "~@/style/_global.scss";
        `
      }
    }
  }
};

I would like to stick to mocha & chai but. I allready tried diffrent solutions. But in worst case I will move to Jest. This is the output which is generated when running the tests inside the main repo.

  Icons
    passing props should override deaulf icon props
1:<div id="target"><div data-v-app=""><!----></div></div>
      1) has correct iconName
[Vue warn]: Unhandled error during execution of async component loader 
  at <AsyncComponentWrapper iconName="appartment" size=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <Icon iconName="appartment" iconSize=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <VTUROOT>
      2) has correct iconSize
[Vue warn]: Unhandled error during execution of async component loader 
  at <AsyncComponentWrapper iconName="appartment" size=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <Icon iconName="appartment" iconSize=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <VTUROOT>
      3) has correct iconColor
[Vue warn]: Unhandled error during execution of async component loader 
  at <AsyncComponentWrapper iconName="appartment" size=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <Icon iconName="appartment" iconSize=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <VTUROOT>
      4) has correct animation
[Vue warn]: Unhandled error during execution of async component loader 
  at <AsyncComponentWrapper iconName="appartment" size=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <Icon iconName="appartment" iconSize=32 iconColor="rgb(193, 21, 57)"  ... > 
  at <VTUROOT>

  0 passing (346ms)
  4 failing
lmiller1990 commented 3 years ago

Does this happen in a Vue CLI project out of the box?

I'll research this a bit more when I have time. It's definitely a config issue - not a Test Utils specific one - but it's not entirely clear how to solve it. Mocha and chai is pretty good, I'd like to have this working for that combination of libraries.