vuematerial / vue-material

Vue.js Framework - ready-to-use Vue components with Material Design, free forever.
https://www.creative-tim.com/vuematerial
MIT License
9.88k stars 1.16k forks source link

MDDialog - Error in beforeDestroy hook: "TypeError: Cannot read property 'propsData' of undefined" #2278

Open rd-stefan-riehmer opened 3 years ago

rd-stefan-riehmer commented 3 years ago

When testing the closing behavior of a dialog, the jest-test with vue-cli for closing runs fine, but there's an ugly stacktrace output:

console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
    [Vue warn]: Error in beforeDestroy hook: "TypeError: Cannot read property 'propsData' of undefined"

    found in

    ---> <MdPortal>
           <MdOverlay> at src/components/MdOverlay/MdOverlay.vue
             <Transition>
               <MdDialog> at src/components/MdDialog/MdDialog.vue
                 <MemberDetails>
                   <Root>

  console.error node_modules/vue/dist/vue.runtime.common.dev.js:1884
    TypeError: Cannot read property 'propsData' of undefined
        at VueComponent.transitionName (/Users/mr.x/folder/top-secret-project/node_modules/vue-material/dist/vue-material.js:1229:64)
        at Watcher.get (/Users/mr.x/folder/top-secret-project/node_modules/vue/dist/vue.runtime.common.dev.js:4465:25)
        at Watcher.evaluate (/Users/mr.x/folder/top-secret-project/node_modules/vue/dist/vue.runtime.common.dev.js:4570:21)
        at VueComponent.computedGetter (/Users/mr.x/folder/top-secret-project/node_modules/vue/dist/vue.runtime.common.dev.js:4819:17)
        at VueComponent.leaveClass (/Users/mr.x/folder/top-secret-project/node_modules/vue-material/dist/vue-material.js:1240:19)
        at Watcher.get (/Users/mr.x/folder/top-secret-project/node_modules/vue/dist/vue.runtime.common.dev.js:4465:25)
        at Watcher.evaluate (/Users/mr.x/folder/top-secret-project/node_modules/vue/dist/vue.runtime.common.dev.js:4570:21)
...

Steps to reproduce

package.json

{
  "name": "vue-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test:unit": "vue-cli-service test:unit",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "axios": "^0.19.2",
    "core-js": "^3.6.5",
    "flush-promises": "^1.0.2",
    "vue": "^2.6.11",
    "vue-material": "^1.0.0-beta-15",
    "vue-router": "^3.2.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.4.0",
    "@vue/cli-plugin-eslint": "~4.4.0",
    "@vue/cli-plugin-router": "~4.4.0",
    "@vue/cli-plugin-unit-jest": "~4.4.0",
    "@vue/cli-service": "~4.4.0",
    "@vue/eslint-config-standard": "^5.1.2",
    "@vue/test-utils": "^1.0.3",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^6.2.2",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "vue-template-compiler": "^2.6.11"
  }
}
MemberDetails.vue

<template>
  <div>
    <md-dialog class="md-extended-dialog" :md-active.sync="active">
      <md-dialog-actions>
        <md-button @click="active = false" data-test-name="close-member-details">Cancel</md-button>
      </md-dialog-actions>
    </md-dialog>
  </div>
</template>

<script>
import EventBus from '@/services/event-bus'

export default {
  name: 'MemberDetails',
  data: () => ({
    active: true
  })
}
</script>
MemberDetails.spec.js

  it('member details can be closed by clicking on "cancel"', async () => {
    // given
    const testSubject = mount(MemberDetails, {localVue, materialDesign})
    expect(testSubject.find('[data-test-name="close-member-details"]').exists()).toBe(true)

    // when
    await testSubject.find('[data-test-name="close-member-details"]').trigger('click')

    // then: button is gone - succeeds, but the ugly stacktrace is thrown (see above)
    expect(testSubject.find('[data-test-name="close-member-details"]').exists()).toBe(false)
  })

What is expected?

No stacktrace thrown.

vinaypai commented 3 years ago

This seems to be because MdPortal is trying to find "transition" inside the child node's propsData without checking if propsData is defined (it's allowed to be undefined by the VNodeOptions interface). I'll create a PR for this, but for now I was able to work around the issue by stubbing out MdPortal in my test like so:

const wrapper = mount(MyComponent, { 
    props:{...},
    stubs: {
        'MdPortal': { template: '<div><slot/></div>' }
    }
})