Home > database >  How to solve ElementUI unknown element warning in unit tests?
How to solve ElementUI unknown element warning in unit tests?

Time:02-09

I'm testing a component using <el-buton> from element-plus

I am not using directly, <el-button> is inside of my Connect component

import { shallowMount } from '@vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '@/components/connect'

const store = createStore({
  state() {
    return {
      user: {},
    }
  },
  mutations: {},
})

let wrapper
const app = createApp(App)
app.use(store)

beforeEach(() => {
  wrapper = shallowMount(App, {
    propsData: {},
    global: {
      plugins: [store],
    },
   
  })
})

I am getting this warning in all test of my components:

If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.

Asked on github as well: https://github.com/element-plus/element-plus/issues/5830

CodePudding user response:

I don't know what you mean by "I'm not using directly", but the warning tells you Jest found this tag when traversing the component's DOM, during shallow mounting, and it doesn't know how to resolve it, as it's neither a native DOM tag, nor a known component.

Important note: the fact that Jest stubs sub-components when shallow mounting doesn't mean it doesn't resolve them.

  1. If <el-button> is not relevant for the current test, simply ignore this warning.
    Jest: "but I don't know what <el-button> is!"
    You: "That's fine, you don't need to."
  2. If <el-button> is not relevant for the test, but the warning annoys you, either declare <el-button> a web component (which is false, but it tells Jest to ignore it), by following the link and the instructions shown in the warning; or stub it manually (replace it with an empty <div />) in this test suite:
import { config } from '@vue/test-utils'

config.stubs['el-button'] = '<div />'
  1. If <el-button> is relevant for the current test, you have to add ElementUI to the wrapper instance's globals.
    This will make your wrapper know what each of ElementUI's components and directives are. It doesn't mean they won't get stubbed. They will be, but Jest will know what it's stubbing and, most importantly, it will resolve their dependencies:
import ElementUI from 'element-ui';

beforeEach(() => {
  wrapper = shallowMount(App, {
    propsData: {},
    global: {
      plugins: [store, ElementUI],
    },
  });
});

This is, in fact, the correct way to provide them, because it's how they're provided to the instance in the real app, when you're installing them on the root instance, using app.use(SomePlugin).


You should do this for all other plugins used in app. If it gets too repetitive, create a helper function, pass the currently tested component and its setup as arguments, returning a wrapper which has all the bells and whistles of your app.

Some purists might say: "but I don't want to test my component with all the global plugins, I want to only test it with the ones it uses."
IMHO, that's wrong. even if the component only uses a few of the global provided plugins, it runs (on live) in a context where they are all provided. So, should there be any conflict between some global plugin and the current component, you probably want to learn about it from this test, not from a github issue.

Generic wrapper factory example:

import { shallowMount, mount } from '@vue/test-utils'
import SomePlugin from 'some-library';
import SomeOtherPlugin from 'some-other-library';
import { createStore } from 'vuex'

export const useWrapper = (component, setup, shallow = true) => {
  const store = createStore({ /* ... */ });
  return (shallow ? shallowMount : mount)(component, {
    globals: {
      plugins: [SomePlugin, SomeOtherPlugin, store]
    },
    ...setup,
  })
}

Note this even allows you to override globals, through setup, should you want to but, IMHO, that's not a good idea, for the reason outlined above.

And now you can import useWrapper in all your tests, which are a bit more boilerplate-free:

import { useWrapper } from '../path-to-helpers';
import SomeComponent from '@/components/SomeComponent.vue'

//...
describe('SomeComponent', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = useWrapper(SomeComponent, {
      propsData: {} // extend as needed
    });
  });

  it('should do stuff...', () => {
    // expect it does stuff...
  })
});

When you need to mount instead of shallowMount, pass false as third argument to useWrapper.

  •  Tags:  
  • Related