avoriaz icon indicating copy to clipboard operation
avoriaz copied to clipboard

Mount and shallow behaviour

Open rmartins90 opened this issue 7 years ago • 25 comments

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.

rmartins90 avatar Jul 03 '17 14:07 rmartins90

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.

eddyerburgh avatar Jul 03 '17 14:07 eddyerburgh

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?

rmartins90 avatar Jul 03 '17 14:07 rmartins90

@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.

eddyerburgh avatar Jul 03 '17 14:07 eddyerburgh

@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.

bmfteixeira avatar Jul 03 '17 14:07 bmfteixeira

Yep, I'll add this tonight

eddyerburgh avatar Jul 03 '17 14:07 eddyerburgh

Ok, I've fixed this in 2.4.2

eddyerburgh avatar Jul 03 '17 21:07 eddyerburgh

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

rmartins90 avatar Jul 03 '17 22:07 rmartins90

Thanks @eddyerburgh. Very well done. 👍

bmfteixeira avatar Jul 04 '17 08:07 bmfteixeira

@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.

rmartins90 avatar Jul 04 '17 13:07 rmartins90

Global components?

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

eddyerburgh avatar Jul 04 '17 13:07 eddyerburgh

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.

rmartins90 avatar Jul 04 '17 13:07 rmartins90

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

eddyerburgh avatar Jul 04 '17 13:07 eddyerburgh

@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)

eddyerburgh avatar Jul 04 '17 17:07 eddyerburgh

@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.

rmartins90 avatar Jul 05 '17 12:07 rmartins90

shallow now stubs every globally registered in 2.4.3

eddyerburgh avatar Jul 12 '17 06:07 eddyerburgh

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.

jwilkey avatar Jul 12 '17 19:07 jwilkey

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?

eddyerburgh avatar Jul 12 '17 20:07 eddyerburgh

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

jwilkey avatar Jul 13 '17 13:07 jwilkey

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

eddyerburgh avatar Jul 13 '17 13:07 eddyerburgh

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

beliolfa avatar Sep 17 '17 13:09 beliolfa

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

beliolfa avatar Sep 17 '17 16:09 beliolfa

@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?

scottadamsmith avatar Nov 30 '17 20:11 scottadamsmith

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 [email protected] 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 .

eddyerburgh avatar Dec 01 '17 06:12 eddyerburgh

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.

scottadamsmith avatar Dec 17 '17 04:12 scottadamsmith

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

eddyerburgh avatar Dec 17 '17 07:12 eddyerburgh