Skip to content

methods [mount] can not render child component correctly #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
74sharlock opened this issue Jan 5, 2018 · 16 comments
Closed

methods [mount] can not render child component correctly #329

74sharlock opened this issue Jan 5, 2018 · 16 comments
Labels

Comments

@74sharlock
Copy link

env:

  • node: 9.3.0
  • vue-test-utils: 1.0.0-beta.9

code:

let wrapper = mount({
  template: `<div>1<tester></tester></div>`,
  components: {
    tester: {
      template: `<div class="tester">test</div>`
    }
  }
});

expect:

console.log(wrapper.html());
// return:
// <div>1<div class="tester">test</div></div>

result:

console.log(wrapper.html());
// return:
// <div>1<!--function (a, b, c, d) { return createElement(vm, a, b, c, d, true); }--></div>
@lmiller1990
Copy link
Member

lmiller1990 commented Jan 5, 2018

You need to pass a Vue component to mount. You are just passing a regular JavaScipt object, no?

You should do something like:

mount(Vue.component('tester', {
  template: ...
}

Although I am not sure what this test would be useful for. You could make a component.js and do

export default Vue.component(......)

then in the test

import Foo from './component.js'

mount(Foo)

@74sharlock
Copy link
Author

Hi @lmiller1990, thanks for reply. I know what you mean absolutely.

I'm testing a special Vue plugin, not a component, and this plugin just need a simple component scene.

and next, I made a file named scene.js like this:

import Vue from 'vue';

export default Vue.component('scene', {
  template: `<div>1</div>`
});

in the test:

import scene from './scene.js';

let wrapper = mount(scene);

console.log(wrapper.is(scene));  // true
console.log(wrapper.vm.$el); // Comment { ... a lot of props }
console.log(wrapper.vm.$el.outerHTML); // undefined
console.log(wrapper.html()); // undefined

I don't know what i missed, but I changed scene.js to scene.vue like this:

<template>
  <div>1</div>
</template>

and it works!

I also test some dist components files, no issue.

So what happened?

@lmiller1990
Copy link
Member

lmiller1990 commented Jan 5, 2018

Hey @74sharlock , I guess it doesn't like the Vue.component syntax. Maybe Vue.extend({ ... })?

Did the solution you found solve the problem? In the vast majority of cases, people are using vue-test-utils to test single file components with the .vue extension, as opposed to plugins.

Can you post the (full) code for the plugin you are testing or link the repo? If it is just a plugin, and not a component, maybe you don't even need vue-test-utils - it sounds like you would just use vue-test-utils to see if the plugin was working, by using it in conjunction with a regular .vue component.

@74sharlock
Copy link
Author

Hi, @lmiller1990. I've tried Vue.componentVue.extend && other method, but I still can not solve problem. Here is my plugin code:

plugin code:

const namespace = 'namespace';

const noop = () => {};

const eventMethods = {
  onChange(_property, value, fn = noop) {
    new Function('value', `this.${_property} = value;`).call(this, value);
    fn();
  },
  onOpen(_property, fn = noop) {
    new Function(`this.${_property} = true;`).call(this);
    fn();
  },
  onClose(_property, fn = noop) {
    new Function(`this.${_property} = false;`).call(this);
    fn();
  }
  // ... other methods like above
}

export default {
  install(vue, options = { namespace }) {
    Object.defineProperty(Vue.prototype, options.namespace, {
      get() {
        let methods = {};

        Object.keys(eventMethods).forEach(key => {
          methods[key] = eventMethods[key].bind(this);
        });

        return methods; 
      }
    });
  }
}

plugin use:

<div>
  <a-component @change="namespace.onChange('formData.name', $event.target.value, callback)"></a-component>
</div>
export default {
  data () {
    return {
      formData: { name: '' }
    }
  },
  methods: {
    callback () { 
      // ...code 
    }
  }
}

This plugin is aimed at components' “v-model”, so I need a component for testing. I just thought that method [mount] would be consistent with Vue app. Maybe I should find another way to test this plugin?

Any way, thanks very much.

@eddyerburgh eddyerburgh added the bug label Jan 7, 2018
@eddyerburgh
Copy link
Member

eddyerburgh commented Jan 7, 2018

This is a legitimate use case, and it should render correctly

@eddyerburgh
Copy link
Member

This will be fixed in beta.10

@daviddaxi
Copy link

Hey!

Is it possible that mounting a VueComponent (that is not a single file component) is broken again? I am using vue-test-utils 1.0.0-beta.25 and i tried to mount a component like below. I also tried it with Vue.component and lot of other ways but I always came to the same result.

const comp = {
  template: `
    <div>
      <h1>Test!</h1>
    </div>
  `,
  props: {
    type: {
      default: 'info'
    }
  }
};

export default comp;
import TestComp from '...';

const localVue = createLocalVue();
const component = localVue.extend(TestComp);

let wrapper = mount(component,{
  localVue
});

Then I get the following error message:

TypeError: Cannot read property '$children' of undefined (vue-test-utils.js 940)

Any ideas?

Thanks!

@eddyerburgh
Copy link
Member

Can you post a full reproduction?

@daviddaxi
Copy link

Ok I think it is my fault, because I saw that you have a unit test for exact that case in html.spec.ts (test/unit/specs/mount/Wrapper/) in the vue-test-utils project:

it('returns a VueWrappers HTML as a string when component has no render function', () => {
    const wrapper = mount({
      template: `<div>1<tester></tester></div>`,
      components: {
        tester: {
          template: `<div class="tester">test</div>`
        }
      }
    })
    const expectedHtml = '<div>1<div class="tester">test</div></div>'
    wrapper.update()
    expect(wrapper.html()).to.equal(expectedHtml)
  })

It is also passing the test if I run npm run test:unit on the vue-test-utils project, but if i copy this test in my project test file which looks like the following, I always get the same error message:

let cleanupBrowserEnvironment = require('jsdom-global')();
import { expect } from 'chai';
import sinon from 'sinon';
import { mount } from '@vue/test-utils';

describe('Some tests', function() {
  afterEach(() => {
    cleanupBrowserEnvironment();
  });

  it('returns a VueWrappers HTML as a string when component has no render function', () => {
    const wrapper = mount({
      template: `<div>1<tester></tester></div>`,
      components: {
        tester: {
          template: `<div class="tester">test</div>`
        }
      }
    })
    const expectedHtml = '<div>1<div class="tester">test</div></div>'
    wrapper.update()
    expect(wrapper.html()).to.equal(expectedHtml)
  })
});
Some tests
       returns a VueWrappers HTML as a string when component has no render function:
     TypeError: Cannot read property '$children' of undefined
      at findAllInstances (node_modules/@vue/test-utils/dist/vue-test-utils.js:940:10)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:5647:29)
      at Context.<anonymous> (tests/test.js:29:21)

And in my package.json to run the test:

{
  // ....
  "scripts": {
    "test": "./node_modules/.bin/mocha --require @babel/register ./tests/**/*.js"
  },
  // ...
}

If you know where I did a mistake, please tell me :)

Thanks!

@eddyerburgh
Copy link
Member

Can you create a GitHub repo with the reproduction and open a new issue? Then I can debug it

@daviddaxi
Copy link

daviddaxi commented Nov 29, 2018

Ok i was able to fix it now.

One solution was to load the vue-test-util library with the require syntax and not the import snytax:

// import { mount } from '@vue/test-utils';
let mount = require('@vue/test-utils').mount;

or with the import syntax but then the jsdom-library needs to be loaded over a mocha.opts file and not in the test file itself:

// test/mocha.opts
-r jsdom-global/register

Otherwise always the same error occurred:

TypeError: Cannot read property '$children' of undefined

I created a repository with the issue for you, if you want to take a look at it:

https://github.com/daviddaxi/VueTesting

(Tried it with Babel 6 as well as with Babel 7)

@OmgImAlexis
Copy link

I also seem to be getting this issue.

➜  webui git:(master) ✗ yarn ava test/specs/components/_helpers/user-card.spec.js
yarn run v1.12.3
$ /Users/xo/code/unraid/webui/node_modules/.bin/ava test/specs/components/_helpers/user-card.spec.js


  1 test failed

  mounts

  /Users/xo/code/unraid/webui/node_modules/@vue/test-utils/dist/vue-test-utils.js:940

  Error thrown in test:

  TypeError {
    message: 'Cannot read property \'$children\' of undefined',
  }

  findAllInstances (node_modules/@vue/test-utils/dist/vue-test-utils.js:940:10)
  mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:5647:29)
  shallowMount (node_modules/@vue/test-utils/dist/vue-test-utils.js:5690:10)
  test (test/helpers/generators.js:484:53)
  componentTest (test/specs/components/_helpers/user-card.spec.js:8:2)

If there's anything I can do to help resolve this issue please let me know as this is causing all of my tests to fail.

@OmgImAlexis
Copy link

OmgImAlexis commented Dec 23, 2018

Looking into this if I check dist/vue-test-utils.js#940 and log the instances I get [ undefined ].

Checking the line that calls that leads me to this line.

  var vm = parentVm.$mount(elm).$refs.vm;

parentVm.$mount(elm).$refs is {} meaning vm won't exist and the next line will fail.

var componentsWithError = findAllInstances(vm).filter(
    function (c) { return c._error; }
  );

Incase it helps here are my component and options args that were being passed to mount.

{ component:
   { name: 'user-card',
     directives: { ImgFallback: [Object] },
     props: { id: [Object] },
     computed:
      { users: [Function],
        user: [Function: user],
        name: [Function],
        img: [Function],
        description: [Function],
        password: [Function],
        fallback: [Function: fallback] },
     render: { [Function] _withStripped: true },
     staticRenderFns: [] },
  options:
   { localVue:
      { [Function: VueComponent]
        cid: 1,
        options: [Object],
        super: [Function],
        extend: [Function],
        mixin: [Function],
        use: [Function],
        component: [Function],
        directive: [Function],
        filter: [Function],
        superOptions: [Object],
        extendOptions: [Object],
        sealedOptions: [Object],
        util: [Object],
        set: [Function: set],
        delete: [Function: del],
        nextTick: [Function: nextTick],
        version: '2.5.21',
        config: [Object],
        _installedPlugins: [Array],
        bus: [VueComponent] },
     stubs:
      { 'font-awesome-icon': true,
        'router-view': true,
        'v-dialog': true,
        'user-card': [Object] },
     mocks: { '$store': [Store], '$t': [Function] },
     propsData: {},
     components:
      { fragment: [Object],
        KeepAlive: [Object],
        Transition: [Object],
        TransitionGroup: [Object] } } }

@eddyerburgh
Copy link
Member

@OmgImAlexis can you create a new issue with a reproduction repo that I can run to debug?

@OmgImAlexis
Copy link

@eddyerburgh
Copy link
Member

It's quite a large reproduction, if you could remove some of the moving parts it would make it easier for me to debug. Can you please create a new issue with either a smaller reproduction, or the current reproduction if you don't have time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants