eddyerburgh / avoriaz

🔬 a Vue.js testing utility library
https://eddyerburgh.gitbooks.io/avoriaz/content/
MIT License
759 stars 62 forks source link

Mount and shallow behaviour #75

Open rmartins90 opened 7 years ago

rmartins90 commented 7 years ago

Start to ask if you can detail the shallow and mount differences in documentation, it would be great to know exactly what to expect.

I'm getting what I think could be a bug, or something that I'm missing. For the given test, component and result:

Component:

<template>
    <div>
        <popover ref="popover" :triggerElements="['.input']"></popover>
    </div>
</template>

Test:

it.only('onFocus - load failed', done => {
    const wrapper = shallow(SelectSearchInput, {
        propsData: {
            ...defaultProps,
            loadFailed: true
        }
     })
     const emitStub = sinon.stub(wrapper.vm, '$emit')

     // Show options should be false since input field is empty
     expect(wrapper.vm.onFocus()).to.be.true
     expect(emitStub).to.be.calledWith('startLoading', wrapper.data().inputValue)
     emitStub.restore()
     done()
})

Error:

ERROR LOG: '[Vue warn]: Error in callback for watcher "triggerElements": "TypeError: undefined is not a constructor (evaluating 'this.$el.querySelector(this.triggerElements[0])')"

found in

---> <Popover> at /resources/assets/js/components/popover.vue <Root>'

Popover component has a watcher on triggerElements prop.

If I replace shallow by mount everything works just fine.

eddyerburgh commented 7 years ago

shallow stubs every child components render functions and lifecycle events, and then mounts it.

shallow({
  render: h => h('div', ComponentWithProps, { }, [
    {
      name: 'ChildComponent',
      mounted() { console.log('mounted' },
      render: h => h('p'),
      props: {
        prop1: true
      }
    }
  ])
})

will call mount with this:

mount({
  render: h => h('div', ComponentWithProps, { }, [
    {
      name: 'ChildComponent',
      mounted: () => {},
      render: () => {},
      props: {
        prop1: true
      }
    }
  ])
})

Maybe shallow should stub other child properties? It's good to keep props so you can check the child component has props, but there might be other properties on the child component that cause errors like the one you're seeing.

rmartins90 commented 7 years ago

Maybe it should stub watchers then? What about router-link component? If I use shallow router-link still requires router.resolve, shouldn't it be stubbed?

eddyerburgh commented 7 years ago

@rmartins90 Yes I think you're right

Maybe the default behavior should be to return a component with the minimal props required to identify it and test that it's been passed the correct propsData.

bmfteixeira commented 7 years ago

@eddyerburgh I agree with @rmartins90. The shallow should allow us to skip all the dependencies of a child component, only testing high-level parent components.

eddyerburgh commented 7 years ago

Yep, I'll add this tonight

eddyerburgh commented 7 years ago

Ok, I've fixed this in 2.4.2

rmartins90 commented 7 years ago

Thanks @eddyerburgh for the quick fix. Congrats on this library. I'll try it.

bmfteixeira commented 7 years ago

Thanks @eddyerburgh. Very well done. 👍

rmartins90 commented 7 years ago

@eddyerburgh this still doesn't work for components that are not specified on components dict. For example, <router-link> still tries to user resolve function (that is on router undefined object) if I use shallow. I think shallow should mock this components as well.

eddyerburgh commented 7 years ago

Global components?

I'll have a look into how best do this.

rmartins90 commented 7 years ago

Yes, like <router-link> component. I think every child component should be ignored in order to be able to do a more unitary test for a component, don't you agree? I don't think it'll be easy. By now I'm mocking them using your trick from https://github.com/eddyerburgh/avoriaz/issues/41 but I think it'll be more elegant if avoriaz shallow function could deal with this global components.

eddyerburgh commented 7 years ago

@rmartins90 yeah I agree, I'll implement it tomorrow 👍

eddyerburgh commented 7 years ago

@rmartins90 just to be clear, do you mean shallow should overwrite any global component that has already been rendered. Or do you mean shallow should stub every component referenced inside a template?

The first one I can implement tonight. The second one will take much longer.

I'm not even sure if it's doable without being prone to errors.

I think this will probably always be the approach for global components that aren't registered:

// mock component
const routerView = {
  name: 'router-view',
  render: h => h('div'),
};

// register mock component
Vue.component('router-view', routerView);

Or maybe as an improvement:

import { componentStub } from 'avoriaz'

// register mock component
Vue.component('router-view', componentStub)
rmartins90 commented 7 years ago

@eddyerburgh You're right, the interesting feature would be stub every component referenced inside a template, but as I said previously it's not easy to implement. Your mock component solution works just fine for now.

eddyerburgh commented 7 years ago

shallow now stubs every globally registered in 2.4.3

jwilkey commented 7 years ago

I am using shallow to render my subject component which contains a child Modal, this Modal contains a slot in which subject places another component, we'll say MyContent. MyContent has props that subject is providing, but I don't seem to be able to test this because Modal is not rendered as per the stubbing of it's render function. How can I use shallow and test that subject is correctly passing props to my "slotted" content? I have seen shallow>options.slots, but it is not clear to me from the docs if this is the correct place to go.

eddyerburgh commented 7 years ago

I think you can use mount for this, unless you think it's a bug. If it's a bug, can you post the component and test?

jwilkey commented 7 years ago

Yes, I could use mount, but I have another complex child component that is a sibling of my Modal, so I don't want to render that. 2 options I see may be: (1) somehow provide a test interface to assert slots were configured properly, even if they are not actually rendered, or (2) in mount options add a .blacklist [Component], or in shallow options add .whitelist [Component] which would conditionally not-render/render the specified child components. This way, I could do something like shallow(MyPage, { whitelist: [Modal] }). I could look at a PR if you think this a possibility

eddyerburgh commented 7 years ago

The option you've described where you can blacklist components sounds like the stub option that exits in vue-test-utils (it's the currently unreleased official test library). You can pass it a template string, and it will replace the component you named with a stub:

const wrapper = mount(Component, { 
  stub: { 
    ComplexComponent: '<div />' 
  } 
})

If I find the time in the next few weeks I will add it to avoriaz. In the meantime, if you feel up to it you could implement a PR using the code from vue-test-utils.

The code is here - https://github.com/vuejs/vue-test-utils/blob/master/src/mount.js#L74 And the tests - https://github.com/vuejs/vue-test-utils/blob/master/test/integration/specs/mount/options/stub.spec.js

beliolfa commented 7 years ago

I will check vue-test-utils repo and try to make a PR on this.

beliolfa commented 7 years ago

Check it out #130 I think it could do the trick. This is valid for my case.

scottadamsmith commented 6 years ago

@eddyerburgh I could really use that stub option for mount/shallow. I am considering porting it to avoriaz or making the move to vue-test-utils to get it. Not sure which makes more sense. Curious how long you think it will be worthwhile to continue to port new features back here?

eddyerburgh commented 6 years ago

I think some people will be using this project for a long time, so porting this feature would be very useful for some people. I'd happily accept a PR that added the stubs option!

On Thu, Nov 30, 2017 at 8:15 PM, Scott Smith notifications@github.com wrote:

@eddyerburgh https://github.com/eddyerburgh I could really use that stub option for mount/shallow. I am considering porting it to avoriaz or making the move to vue-test-utils to get it. Not sure which makes more sense. Curious how long you think it will be worthwhile to continue to port new features back here?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/eddyerburgh/avoriaz/issues/75#issuecomment-348308120, or mute the thread https://github.com/notifications/unsubscribe-auth/AMlbW0J4uO9DOlRQSI0h3OCq2nV-1p0Uks5s7wzygaJpZM4OMU68 .

scottadamsmith commented 6 years ago

I started looking this and realized that I may actually want to build upon the renderDefaultSlot option that was added earlier by @disitec. Currently, that option applies only to global components. However, we struggle with wanting to test the default slot content for a child component while using shallow. If I understand things correctly, shallow stubs child components the same way that global components are stubbed, so would it be presumptuous to say that the renderDefaultSlot option could also apply to stubbed child components when using shallow?

Let me know what you think and if we are on agreement, I can work on this update.

eddyerburgh commented 6 years ago

@scottadamsmith yes if you'd like to implement I'd be happy to review and merge 🙂