Vue-test-utils: Add component instances to slots on mount

Created on 5 Sep 2017  路  10Comments  路  Source: vuejs/vue-test-utils

Is there a way to somehow pass component instances to the slots property of mount? The use case is, say I have a component that has a required property:

export default {
    name: 'Message',
    props: {
      message: {
        type: String,
        required: true,
        validator: value => value.length > 1
      }
  }

Then if in a test I wanna add a list of Message as the default slots, I cannot do it like it's documented: I'll get a "message" property is required error:

mount(MessageList, {
  slots: {
    default: messages
  }
})

I tried using mount and getting the vm out of it, or also extending the component and using new Vue(...) but I get the error [Vue warn]: Failed to mount component: template or render function not defined.:

beforeEach(() => {
  const indexes = [0, 1, 2]
  const messages = indexes.map(i => mount(Message, {
    propsData: { message: `Message ${i}` }
  }).vm)

  cmp = mount(MessageList, {
    slots: {
      default: messages
    }
  })
})

So, how's it possible to accomplish that?

feature request

Most helpful comment

Oh snap, I just figured it out how to do it properly. Try this.

const localVue = createLocalVue()
localVue.component('child', Child)

const wrapper = mount(Parent, {
  slots: {
    default: `
      <child foo="bar"><div>Lorem ipsum</div></child>
      <child foo="qux"><div>Dolor sit amet</div></child>
    `
  },
  localVue
})

localVue was there for the whole time but I kept trying to mount slots with createElement. Damn.

All 10 comments

There isn't currently a way to pass mounted components to slots.

There's some work being done in avoriaz to add the option to pass Wrappers as slots. But right now there's an unsolved bug - https://github.com/eddyerburgh/avoriaz/issues/113.

I'd like to add this functionality, but don't have time to work on this feature right now. I'd welcome a PR and could give you some guidance if you were willing to make one.

Thanks, right now I also don't have the time, but probably in the coming days I can get some and lend a hand in there. I'll contact you for that guidance in that case ;).

FYI, as a workaround, this seems to work:

mount(MessageList, {
  slots: {
    default: {
      render(h) {
        return h(Message, { props: { message: 'hey' }  })
      }
    }
  }
})

Hmm this doesn't seen to work for me. The component is created, but the props are undefined when I try to access them...

const table = mount(TableComponent, {
    propsData: {
        data: [
            { firstName: 'John', lastName: 'Lennon' },
            { firstName: 'Paul', lastName: 'McCartney' },
            { firstName: 'George', lastName: 'Harrison' },
            { firstName: 'Ringo', lastName: 'Starr' },
        ],
    },
    slots: {
        default: {
            render(h) {
                return h(TableColumn, {
                    props: {
                        show: 'firstName',
                        label: 'First name',
                    },
                });
            },
        },
    },
});

console.log(table.vm.$slots.default[0].componentInstance.show);

try this:

const tableCol = table.find(TableColumn)
expect(tableCol.vm.show).toBe('firstName')
expect(tableCol.vm.label).toBe('First name')

We aren't going to implement this at the moment. You can pass a component, but not an instance.

@eddyerburgh Is this functionality anywhere on the roadmap? I have some functionality that can't really be tested without access to instance properties, and workarounds don't always work well.

It's not on the roadmap. I would review a PR if somebody wants to add it.

If anyone is looking for a workaround, try testing with another Component

let App = Vue.extend({
  render (h) {
    return h(Parent, {
      props: {
        // parent props
      }
    }, [
      // slots
      h(Child, {
        props: {
          // child props
        }
      })
    ])
  }
})

it('has a button', () => {
  let wrapper = mount(App)
  console.log(wrapper.find(Parent).vm.$slots.default)
})

Oh snap, I just figured it out how to do it properly. Try this.

const localVue = createLocalVue()
localVue.component('child', Child)

const wrapper = mount(Parent, {
  slots: {
    default: `
      <child foo="bar"><div>Lorem ipsum</div></child>
      <child foo="qux"><div>Dolor sit amet</div></child>
    `
  },
  localVue
})

localVue was there for the whole time but I kept trying to mount slots with createElement. Damn.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eddyerburgh picture eddyerburgh  路  4Comments

38elements picture 38elements  路  3Comments

AustinGil picture AustinGil  路  3Comments

alexanderstudte picture alexanderstudte  路  3Comments

benm-eras picture benm-eras  路  3Comments