Element: [Bug Report] vue/test-util cannot mount "ElSelectDropdown"

Created on 5 Sep 2018  ·  16Comments  ·  Source: ElemeFE/element

Element UI version

2.4.6

OS/Browsers version

mac os

Vue version

2.5.17

Reproduction Link

https://github.com/valterbarros/trackthings

Steps to reproduce

try to mount a component with el-select inside throw :

---> <ElSelectDropdown>
       <Transition>
         <ElSelect>
           <ElFormItem>
             <ElForm>
               <Anonymous>
                 <Root>

TypeError: Cannot read property '$el' of undefined
at VueComponent.mounted (/Users/mhanne/src/report-builder/viewer/node_modules/element-ui/lib/element-ui.common.js:9131:54)
at callHook (/Users/mhanne/src/report-builder/viewer/node_modules/vue/dist/vue.runtime.common.js:2919:21)
at Object.insert (/Users/mhanne/src/report-builder/viewer/node_modules/vue/dist/vue.runtime.common.js:4156:7)
at invokeInsertHook (/Users/mhanne/src/report-builder/viewer/node_modules/vue/dist/vue.runtime.common.js:5958:28)

What is Expected?

Mount le select input

What is actually happening?

test crash. with jest or ava same issues

Most helpful comment

https://vue-test-utils.vuejs.org/api/components/

import { config } from '@vue/test-utils'
config.stubs.transition = false

This may solve this problem.

The built-in transition component has property named abstract, but transitionStub does not.

vue/src/core/instance/lifecycle.js

export function initLifecycle (vm: Component) {
  const options = vm.$opti

  // locate first non-abstract parent
  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
 ...
} 

This causes the child component to have the wrong parent component.

All 16 comments

We face the same issue and tried to locate the culprit. And I found that the problematic line in ElSelectDropdown is the line it accessing $ref in mounted() as below.

mounted() {
...
this.referenceElm = this.$parent.$refs.reference.$el;
...
}

The Vue official documents for $ref usage specifies.

An important note about the ref registration timing: because the refs themselves are created as a result of the render function, you cannot access them on the initial render - they don’t exist yet! $refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.

As per document, the this.$parent.$refs does not contain child references on mounted. This seems quite clear.

@JaekwanLee Have you find a solution ?

@matthieuHanne Not really. Just testing it with shallowMount for now. It is weird that it seems working when it runs on browser though.

Hi, is there any development @JaekwanLee ? or @matthieuHanne have found some kind of solution ? I'm currently adding some unit tests with Mocha to my Vue project and I have the same issue.

Thank you :)

We've also encountered exactly same issue in tests

@ziyoung @wacky6 Any ideas what is to be done here?

As far as I know, Element requires the page to be render in a real browser.

referenceElm is used by Popper mixin to show popup according to this reference element's position.


What to be done?
Maybe, set referenceElm to null, hope vue-popper does not throw and mounts the dropdown (or you won't be able to select the option).

I think there is modifications going on in vue-popper for next branch. You have to ask @ziyoung for details. That part of Element is out of my expertise.


My recommendation for testing applications with Vue/Element (in fact any MVVM framework applications, such as those built with React) is to use a state management library.

Then, assume UI library's implementation is correct, test the states / data (of state management libraries) are correct (through mutations / setState), instead of performing MVVM component-level tests.

I write a simple demo https://github.com/ziyoung/vue-test-with-jest
All test cases pass.

 PASS  src/components/__tests__/hello-world.test.js
 PASS  src/components/Select/__tests__/select.test.js
  ● Console

    console.log src/components/Select/select.vue:18
      HTMLParagraphElement {}

Maybe it's not a bug of Element.

@ziyoung Here's a patch to your vue-test-with-jest that will allow you to reproduce the issue:
ref.txt

https://vue-test-utils.vuejs.org/api/components/

import { config } from '@vue/test-utils'
config.stubs.transition = false

This may solve this problem.

The built-in transition component has property named abstract, but transitionStub does not.

vue/src/core/instance/lifecycle.js

export function initLifecycle (vm: Component) {
  const options = vm.$opti

  // locate first non-abstract parent
  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
 ...
} 

This causes the child component to have the wrong parent component.

Thank you @huihao.
@matthieuHanne @syn-zeta it should work for you. https://github.com/ziyoung/vue-test-with-jest/blob/master/src/components/__tests__/hello-world.test.js

Thank you @huihao.
@matthieuHanne @syn-zeta it should work for you. https://github.com/ziyoung/vue-test-with-jest/blob/master/src/components/__tests__/hello-world.test.js

This is the same mistake.
image

@huihao
Thank you for your solution!

@huihao Thank you

We face the same issue and tried to locate the culprit. And I found that the problematic line in ElSelectDropdown is the line it accessing $ref in mounted() as below.

mounted() {
...
this.referenceElm = this.$parent.$refs.reference.$el;
...
}

I couldn't find where this this.referenceElm is used. What is this variable for?
I commented it out and both the tests and browser view worked fine.

@ziyoung I would like to ask if this issue could be reopened. I am still having that same exact error, and huihao's answer

import { config } from '@vue/test-utils';
config.stubs.transition = false;

doesn't solve it. Did anyone find a workaround or a working solution?

Was this page helpful?
0 / 5 - 0 ratings